Hình ảnh có DPI cao cho mật độ pixel thay đổi

Một trong những đặc điểm trong bối cảnh thiết bị phức tạp ngày nay là có mật độ pixel trên màn hình rất rộng. Một số thiết bị có màn hình độ phân giải rất cao, trong khi một số thiết bị khác lại có màn hình phía sau. Một nhà phát triển ứng dụng có thể cần hỗ trợ nhiều mật độ pixel. Điều này có thể khá khó khăn. Trên web dành cho thiết bị di động, những thách thức kết hợp với một số yếu tố:

  • Nhiều loại thiết bị với nhiều kiểu dáng.
  • Băng thông mạng và thời lượng pin hạn chế.

Về hình ảnh, mục tiêu của các nhà phát triển ứng dụng web là phân phát hình ảnh có chất lượng tốt nhất một cách hiệu quả nhất có thể. Bài viết này sẽ đề cập đến một số kỹ thuật hữu ích để thực hiện việc này ngay hôm nay và trong tương lai gần.

Tránh sử dụng hình ảnh nếu có thể

Trước khi mở hộp sâu này, hãy nhớ rằng trên web có nhiều công nghệ mạnh mẽ chủ yếu phụ thuộc vào độ phân giải và DPI. Cụ thể, văn bản, SVG và phần lớn CSS sẽ "chỉ hoạt động" nhờ tính năng tự động điều chỉnh tỷ lệ pixel của web (thông qua devicePixelRatio).

Tuy nhiên, không phải lúc nào bạn cũng tránh được hình ảnh đường quét. Ví dụ: bạn có thể được cung cấp các thành phần khá khó sao chép trong SVG/CSS thuần tuý hoặc bạn đang xử lý một bức ảnh. Mặc dù bạn có thể tự động chuyển đổi hình ảnh thành SVG, nhưng việc vectơ hoá ảnh chụp rất ít có ý nghĩa vì các phiên bản mở rộng thường không đẹp.

Thông tin khái quát

Lịch sử mật độ hiển thị rất ngắn

Thuở ban đầu, màn hình máy tính có mật độ pixel là 72 hoặc 96dpi (số điểm trên mỗi inch).

Màn hình dần được cải thiện về mật độ pixel, chủ yếu nhờ vào trường hợp sử dụng thiết bị di động, trong đó người dùng thường đưa điện thoại lại gần khuôn mặt hơn, giúp các pixel hiển thị nổi bật hơn. Đến năm 2008, điện thoại 150dpi trở thành tiêu chuẩn mới. Xu hướng mật độ hiển thị tăng lên vẫn tiếp tục và các điện thoại mới hiện nay có màn hình 300dpi (thương hiệu "Retina" của Apple).

Tất nhiên, chén thánh là một màn hình trong đó các pixel hoàn toàn không nhìn thấy được. Đối với kiểu dáng điện thoại, màn hình Retina/HiDPI hiện tại có thể gần đạt mức lý tưởng đó. Tuy nhiên, các loại phần cứng và thiết bị đeo mới như Project Glass có thể sẽ tiếp tục giúp tăng mật độ pixel.

Trong thực tế, hình ảnh có mật độ thấp sẽ trông như cũ trên màn hình mới giống như trên màn hình cũ. Tuy nhiên, so với hình ảnh sắc nét mà người dùng quen xem, hình ảnh có mật độ điểm ảnh thấp trông khó chịu và bị vỡ ảnh. Sau đây là bản mô phỏng sơ bộ về hình ảnh 1x sẽ trông như thế nào trên màn hình 2x. Ngược lại, hình ảnh 2x trông khá đẹp.

khỉ đầu chó 1x
khỉ đầu chó 2x
Khủng long! có mật độ điểm ảnh khác nhau.

Pixel trên web

Khi web được thiết kế, 99% màn hình là 96dpi (hoặc giả sử) và một số điều khoản được thực hiện để thay đổi mặt trước này. Do sự thay đổi lớn về kích thước và mật độ màn hình, chúng tôi cần có một cách tiêu chuẩn để hình ảnh trông đẹp mắt trên nhiều mật độ và kích thước màn hình.

Thông số kỹ thuật HTML gần đây đã giải quyết vấn đề này bằng cách xác định pixel tham chiếu mà nhà sản xuất sử dụng để xác định kích thước của pixel CSS.

Bằng cách sử dụng pixel tham chiếu, nhà sản xuất có thể xác định kích thước pixel vật lý của thiết bị so với pixel tiêu chuẩn hoặc lý tưởng. Tỷ lệ này được gọi là tỷ lệ pixel của thiết bị.

Tính tỷ lệ pixel của thiết bị

Giả sử một chiếc điện thoại thông minh có màn hình với kích thước pixel thực là 180 pixel/inch (ppi). Để tính tỷ lệ pixel của thiết bị, bạn cần thực hiện 3 bước sau:

  1. So sánh khoảng cách thực tế mà thiết bị được giữ so với khoảng cách cho pixel tham chiếu.

    Theo thông số kỹ thuật, chúng ta biết rằng ở 28 inch, lý tưởng là 96 pixel trên inch. Tuy nhiên, vì đây là điện thoại thông minh, nên mọi người sẽ cầm thiết bị gần mặt mình hơn so với cầm máy tính xách tay. Hãy ước tính khoảng cách đó là 18 inch.

  2. Nhân tỷ lệ khoảng cách với mật độ chuẩn (96ppi) để có được mật độ pixel lý tưởng cho khoảng cách đã cho.

    lý tưởngPixelDensity = (28/18) * 96 = 150 pixel mỗi inch (xấp xỉ)

  3. Lấy tỷ lệ mật độ pixel thực tế với mật độ pixel lý tưởng để tính được tỷ lệ pixel của thiết bị.

    devicePixelRatio = 180/150 = 1,2

Cách tính devicePixelRatio.
Sơ đồ hiển thị một pixel góc tham chiếu để giúp minh hoạ cách tính devicePixelRatio.

Vì vậy, khi trình duyệt cần biết cách đổi kích thước hình ảnh để phù hợp với màn hình theo độ phân giải lý tưởng hoặc độ phân giải chuẩn, trình duyệt sẽ đề cập đến tỷ lệ pixel của thiết bị là 1,2 – tức là đối với mỗi pixel lý tưởng, thiết bị này có 1,2 pixel vật lý. Sau đây là công thức để chuyển đổi giữa pixel lý tưởng (như xác định theo thông số kỹ thuật web) và pixel vật lý (các dấu chấm trên màn hình thiết bị) như sau:

physicalPixels = window.devicePixelRatio * idealPixels

Trước đây, các nhà cung cấp thiết bị thường có xu hướng làm tròn devicePixelRatios (DPR). iPhone và iPad của Apple báo cáo DPR là 1 và báo cáo Retina tương đương 2. Quy cách của CSS khuyến nghị rằng

đơn vị pixel là tổng số pixel của thiết bị gần đúng nhất với pixel tham chiếu.

Một lý do giúp tỷ lệ tròn có thể tốt hơn là vì chúng có thể dẫn đến các cấu phần phần mềm pixel phụ ít hơn.

Tuy nhiên, bối cảnh thực tế về thiết bị còn đa dạng hơn nhiều và điện thoại Android thường có DPR là 1, 5. Máy tính bảng Nexus 7 có DPR là ~ 1,33, đạt được theo phép tính tương tự như ở trên. Chúng tôi dự kiến sẽ thấy nhiều thiết bị khác có DPR có thể thay đổi trong tương lai. Do đó, bạn không nên giả định rằng khách hàng của mình sẽ có DPR số nguyên.

Tổng quan về kỹ thuật tạo hình ảnh HiDPI

Có nhiều kỹ thuật để giải quyết vấn đề hiển thị hình ảnh chất lượng tốt nhất nhanh nhất có thể, thường gồm hai danh mục:

  1. Tối ưu hoá hình ảnh đơn lẻ và
  2. Tối ưu hoá lựa chọn giữa nhiều hình ảnh.

Phương pháp tiếp cận hình ảnh đơn: sử dụng một hình ảnh nhưng sử dụng hình ảnh khéo léo. Những phương pháp này có một hạn chế là bạn chắc chắn sẽ phải hy sinh hiệu suất, vì bạn sẽ tải hình ảnh HiDPI xuống ngay cả trên các thiết bị cũ có DPI thấp hơn. Dưới đây là một số phương pháp cho trường hợp hình ảnh đơn lẻ:

  • Hình ảnh HiDPI được nén quá nhiều
  • Định dạng hình ảnh cực kỳ tuyệt vời
  • Định dạng hình ảnh tăng tiến

Nhiều phương pháp tiếp cận hình ảnh: sử dụng nhiều hình ảnh, nhưng hãy khéo léo chọn hình ảnh cần tải. Những phương pháp này vốn đã tốn chi phí để nhà phát triển tạo nhiều phiên bản của cùng một tài sản, từ đó tìm ra chiến lược ra quyết định. Sau đây là các tùy chọn:

  • JavaScript
  • Phân phối phía máy chủ
  • Truy vấn phương tiện CSS
  • Các tính năng tích hợp sẵn trong trình duyệt (image-set(), <img srcset>)

Hình ảnh HiDPI được nén quá nhiều

Hình ảnh đã chiếm một con số khổng lồ 60% băng thông dành cho việc tải một trang web trung bình xuống. Bằng cách phân phát hình ảnh HiDPI cho tất cả khách hàng, chúng tôi sẽ tăng con số này. Nó sẽ lớn hơn bao nhiêu?

Tôi đã chạy một số thử nghiệm tạo ra các mảnh hình ảnh 1x và 2x với chất lượng JPEG ở mức 90, 50 và 20. Đây là tập lệnh shell mà tôi đã sử dụng (sử dụng ImageMagick) để tạo các tập lệnh đó:

Ví dụ 1 về thẻ thông tin. Ví dụ về thẻ thông tin 2. Ví dụ 3 về thẻ thông tin.
Mẫu hình ảnh ở nhiều độ nén và mật độ pixel.

Từ việc lấy mẫu nhỏ và không khoa học này, có vẻ như việc nén hình ảnh lớn mang lại sự đánh đổi chất lượng tốt so với kích thước. Đối với mắt tôi, hình ảnh 2x được nén quá mức thực sự trông đẹp hơn hình ảnh 1x không nén.

Tất nhiên, việc phân phát hình ảnh 2x chất lượng thấp và được nén ở mức cao cho thiết bị 2x sẽ tệ hơn việc phân phát hình ảnh có chất lượng cao hơn và phương pháp trên sẽ dẫn đến các hình phạt về chất lượng hình ảnh. Nếu bạn so sánh chất lượng: 90 hình ảnh với chất lượng: 20 hình ảnh, bạn sẽ thấy độ sắc nét giảm và độ hạt tăng lên. Những cấu phần phần mềm này có thể không được chấp nhận trong trường hợp hình ảnh chất lượng cao là chìa khoá (ví dụ: ứng dụng xem ảnh) hoặc đối với các nhà phát triển ứng dụng không sẵn sàng xâm phạm.

Phép so sánh ở trên được thực hiện hoàn toàn bằng tệp JPEG nén. Lưu ý: Có nhiều sự đánh đổi giữa các định dạng hình ảnh được triển khai rộng rãi (JPEG, PNG, GIF). Điều này khiến chúng ta...

Định dạng hình ảnh cực kỳ tuyệt vời

WebP là một định dạng hình ảnh hấp dẫn, nén rất tốt mà vẫn giữ được độ trung thực cao của hình ảnh. Tất nhiên, tính năng này chưa được triển khai ở mọi nơi!

Bạn có thể kiểm tra khả năng hỗ trợ WebP thông qua JavaScript. Bạn tải hình ảnh 1px qua data-uri, đợi sự kiện được tải hoặc sự kiện lỗi được kích hoạt, sau đó xác minh rằng kích thước là chính xác. Modernizr cung cấp một tập lệnh phát hiện tính năng như vậy. Tập lệnh này được cung cấp thông qua Modernizr.webp.

Tuy nhiên, cách tốt hơn để làm việc này là trực tiếp trong CSS bằng cách sử dụng hàm hình ảnh(). Vì vậy, nếu có hình ảnh WebP và hình ảnh dự phòng JPEG, bạn có thể viết như sau:

#pic {
  background: image("foo.webp", "foo.jpg");
}

Có một vài vấn đề với phương pháp này. Trước tiên, image() hoàn toàn không được triển khai rộng rãi. Thứ hai, mặc dù tính năng nén WebP sẽ xoá ảnh JPEG ra khỏi vùng nước, nhưng vẫn có một điểm cải tiến tương đối tăng dần – nhỏ hơn khoảng 30% dựa trên thư viện WebP này. Do đó, nếu chỉ dùng WebP là chưa đủ để giải quyết vấn đề DPI cao.

Định dạng hình ảnh tăng dần

Các định dạng hình ảnh luỹ tiến như JPEG 2000, Progressive JPEG, Progressive PNG và GIF có lợi ích (đôi lúc còn gây tranh cãi) khi thấy hình ảnh xuất hiện trước khi được tải hoàn toàn. Mặc dù có bằng chứng trái ngược về việc này, nhưng các định dạng này có thể làm phát sinh thêm một chút chi phí về kích thước. Jeff Atwood tuyên bố rằng chế độ luỹ tiến "tăng thêm khoảng 20% kích thước của hình ảnh PNG và khoảng 10% kích thước của hình ảnh JPEG và GIF". Tuy nhiên, Stoyan Stefanov tuyên bố rằng đối với các tệp lớn, chế độ tăng dần sẽ hiệu quả hơn (trong hầu hết các trường hợp).

Thoạt nhìn, hình ảnh liên tục trông rất hứa hẹn trong bối cảnh phân phát hình ảnh có chất lượng tốt nhất nhanh nhất có thể. Ý tưởng là trình duyệt có thể ngừng tải xuống và giải mã hình ảnh khi biết rằng dữ liệu bổ sung sẽ không làm tăng chất lượng hình ảnh (tức là tất cả những điểm cải thiện về độ chân thực đều là pixel phụ).

Mặc dù dễ dàng chấm dứt kết nối, nhưng việc bắt đầu lại thường sẽ tốn kém. Đối với trang web có nhiều hình ảnh, phương pháp hiệu quả nhất là giữ cho một kết nối HTTP duy nhất hoạt động và sử dụng lại kết nối này càng lâu càng tốt. Nếu kết nối bị chấm dứt sớm do một hình ảnh đã được tải xuống đủ, thì trình duyệt cần tạo một kết nối mới, điều này có thể thực sự chậm trong môi trường độ trễ thấp.

Một giải pháp cho vấn đề này là sử dụng yêu cầu Phạm vi HTTP, cho phép các trình duyệt chỉ định một phạm vi byte để tìm nạp. Một trình duyệt thông minh có thể đưa ra yêu cầu HEAD để lấy tiêu đề, xử lý tiêu đề đó, quyết định lượng hình ảnh thực sự cần thiết, sau đó tìm nạp. Rất tiếc, các máy chủ web không hỗ trợ dải HTTP, khiến cho phương pháp này không thực tế.

Cuối cùng, một hạn chế rõ ràng của phương pháp này là bạn không được chọn hình ảnh nào cần tải, mà chỉ có thể thay đổi độ chính xác của cùng một hình ảnh. Do đó, hàm này không giải quyết được trường hợp sử dụng "hướng dẫn nghệ thuật".

Sử dụng JavaScript để quyết định hình ảnh nào cần tải

Phương pháp đầu tiên và rõ ràng nhất để quyết định hình ảnh nào cần tải là sử dụng JavaScript trong ứng dụng. Phương pháp này cho phép bạn tìm hiểu mọi thứ về tác nhân người dùng của mình và làm đúng cách. Bạn có thể xác định tỷ lệ pixel của thiết bị qua window.devicePixelRatio, xem chiều rộng và chiều cao của màn hình, thậm chí có thể phát hiện một số kết nối mạng thông qua navigation.connection hoặc đưa ra yêu cầu giả mạo như thư viện foresight.js. Sau khi thu thập tất cả những thông tin này, bạn có thể quyết định hình ảnh nào cần tải.

Có khoảng một triệu thư viện JavaScript hoạt động như trên nhưng tiếc là không có thư viện nào trong số đó có đặc biệt nổi bật.

Một nhược điểm lớn của phương pháp này là khi sử dụng JavaScript, bạn sẽ trì hoãn việc tải hình ảnh cho đến khi trình phân tích cú pháp dự kiến hoàn tất. Về cơ bản, điều này có nghĩa là hình ảnh thậm chí sẽ không bắt đầu tải xuống cho đến khi sự kiện pageload kích hoạt. Tìm hiểu thêm về điều này trong bài viết của Jason Grigsby.

Quyết định hình ảnh nào cần tải trên máy chủ

Bạn có thể hoãn quyết định cho phía máy chủ bằng cách viết trình xử lý yêu cầu tuỳ chỉnh cho mỗi hình ảnh mà bạn phân phát. Một trình xử lý như vậy sẽ kiểm tra tính năng hỗ trợ Retina dựa trên User-Agent (thông tin duy nhất được chuyển tiếp đến máy chủ). Sau đó, dựa trên việc logic phía máy chủ có muốn phân phát các thành phần HiDPI hay không, bạn sẽ tải thành phần phù hợp (được đặt tên theo một số quy ước đã biết).

Thật không may là Tác nhân người dùng không nhất thiết phải cung cấp đủ thông tin để quyết định việc thiết bị sẽ nhận được hình ảnh chất lượng cao hay thấp. Ngoài ra, rõ ràng là bất cứ điều gì liên quan đến Tác nhân người dùng đều là tấn công và nên tránh nếu có thể.

Sử dụng các truy vấn phương tiện CSS

Nhờ mang tính khai báo, các truy vấn phương tiện CSS giúp bạn nêu rõ ý định của mình và để trình duyệt thay mặt bạn thực hiện những điều đúng đắn. Ngoài cách sử dụng phổ biến nhất là truy vấn nội dung đa phương tiện — khớp với kích thước thiết bị — bạn cũng có thể khớp devicePixelRatio. Truy vấn nội dung nghe nhìn được liên kết là tỷ lệ pixel thiết bị, và có các biến thể tối thiểu và tối đa được liên kết, như bạn có thể mong đợi. Nếu muốn tải hình ảnh có DPI cao và tỷ lệ pixel của thiết bị vượt quá ngưỡng, thì bạn có thể làm như sau:

#my-image { background: (low.png); }

@media only screen and (min-device-pixel-ratio: 1.5) {
  #my-image { background: (high.png); }
}

Điều này sẽ phức tạp hơn một chút khi kết hợp tất cả các tiền tố nhà cung cấp, đặc biệt là do sự khác biệt lớn về vị trí của tiền tố "tối thiểu" và "tối đa":

@media only screen and (min--moz-device-pixel-ratio: 1.5),
    (-o-min-device-pixel-ratio: 3/2),
    (-webkit-min-device-pixel-ratio: 1.5),
    (min-device-pixel-ratio: 1.5) {

  #my-image {
    background:url(high.png);
  }
}

Với phương pháp này, bạn có lại được các lợi ích của việc phân tích cú pháp trước đây (bị mất khi dùng giải pháp JS). Bạn cũng có được sự linh hoạt trong việc chọn các điểm ngắt thích ứng (ví dụ: bạn có thể có hình ảnh DPI thấp, trung bình và cao), nhưng bị mất do phương pháp phía máy chủ.

Rất tiếc, công cụ này vẫn hơi khó sử dụng và dẫn đến CSS trông lạ (hoặc cần phải xử lý trước). Ngoài ra, phương pháp này chỉ áp dụng cho các thuộc tính CSS, vì vậy, không có cách nào để đặt <img src> và hình ảnh của bạn phải là các phần tử có nền. Cuối cùng, nếu chỉ dựa vào tỷ lệ pixel của thiết bị, bạn có thể gặp phải trường hợp điện thoại thông minh có DPI cao phải tải xuống một thành phần hình ảnh có kích thước lớn gấp 2 lần khi đang dùng kết nối EDGE. Đây không phải là trải nghiệm người dùng tốt nhất.

Sử dụng các tính năng mới của trình duyệt

Gần đây, đã có rất nhiều cuộc thảo luận xoay quanh việc hỗ trợ nền tảng web cho vấn đề hình ảnh DPI cao. Gần đây, Apple đã xâm nhập vào không gian này, đưa hàm CSS image-set() vào WebKit. Do đó, cả Safari và Chrome đều hỗ trợ. Vì đây là một hàm CSS nên image-set() không giải quyết vấn đề đối với thẻ <img>. Nhập @srcset để giải quyết vấn đề này nhưng (tại thời điểm viết bài này) chưa triển khai tệp đối chiếu nào. Phần tiếp theo sẽ tìm hiểu kỹ hơn về image-setsrcset.

Các tính năng của trình duyệt để hỗ trợ DPI cao

Sau cùng, quyết định về phương pháp mà bạn thực hiện sẽ phụ thuộc vào các yêu cầu cụ thể của bạn. Tuy nhiên, hãy lưu ý rằng tất cả các phương pháp nêu trên đều có hạn chế. Tuy nhiên, trong tương lai, khi image-set và srcset được hỗ trợ rộng rãi, chúng sẽ là giải pháp thích hợp cho vấn đề này. Đến lúc này, hãy cùng thảo luận về một số phương pháp hay nhất có thể giúp chúng ta tiến gần nhất đến tương lai lý tưởng đó.

Trước tiên, hai công cụ này khác nhau như thế nào? image-set() là một hàm CSS, thích hợp dùng làm giá trị của thuộc tính CSS nền. srcset là một thuộc tính dành riêng cho các phần tử <img>, có cú pháp tương tự. Cả hai thẻ này đều cho phép bạn chỉ định phần khai báo hình ảnh, nhưng thuộc tính srcset cũng cho phép bạn định cấu hình hình ảnh nào cần tải dựa trên kích thước khung nhìn.

Các phương pháp hay nhất cho nhóm hình ảnh

Hàm CSS image-set() có tiền tố là -webkit-image-set(). Cú pháp khá đơn giản, sử dụng một hoặc nhiều phần khai báo hình ảnh được phân tách bằng dấu phẩy, bao gồm một chuỗi URL hoặc hàm url() theo sau là độ phân giải được liên kết. Ví dụ:

background-image:  -webkit-image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

Thông tin này cho trình duyệt biết là có hai hình ảnh để lựa chọn. Một trong số đó được tối ưu hoá cho màn hình 1x và một cho màn hình 2x. Sau đó, trình duyệt sẽ chọn tệp sẽ tải, dựa trên nhiều yếu tố, thậm chí có thể bao gồm tốc độ mạng, nếu trình duyệt đủ thông minh (hiện chưa được triển khai theo như tôi biết).

Ngoài việc tải đúng hình ảnh, trình duyệt cũng sẽ điều chỉnh tỷ lệ hình ảnh tương ứng. Nói cách khác, trình duyệt giả định rằng 2 hình ảnh lớn gấp đôi hình ảnh 1x và do đó sẽ giảm tỷ lệ hình ảnh 2x xuống hệ số 2 để hình ảnh có cùng kích thước trên trang.

Thay vì chỉ định 1x, 1,5x hoặc Nx, bạn cũng có thể chỉ định một mật độ pixel nhất định của thiết bị bằng dpi.

Tính năng này hoạt động tốt, ngoại trừ trong các trình duyệt không hỗ trợ thuộc tính image-set, trình duyệt sẽ không hiển thị hình ảnh nào! Điều này rõ ràng là không tốt, vì vậy, bạn phải sử dụng phương án dự phòng (hoặc chuỗi phương án dự phòng) để giải quyết vấn đề đó:

background-image: url(icon1x.jpg);
background-image: -webkit-image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);
/* This will be useful if image-set gets into the platform, unprefixed.
    Also include other prefixed versions of this */
background-image: image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

Thao tác trên sẽ tải thành phần phù hợp trong các trình duyệt có hỗ trợ tính năng đặt hình ảnh và nếu không, sẽ quay lại thành phần 1x. Cảnh báo rõ ràng là mặc dù trình duyệt image-set() hỗ trợ ở mức thấp, nhưng hầu hết các tác nhân người dùng sẽ nhận được tài sản gấp 1 lần.

Bản minh hoạ này sử dụng image-set() để tải đúng hình ảnh, quay lại thành phần 1x nếu hàm CSS này không được hỗ trợ.

Tại thời điểm này, có thể bạn đang thắc mắc tại sao không chỉ polyfill (tức là tạo một miếng đệm JavaScript cho) image-set() rồi gọi nó mỗi ngày? Giờ đây, thật khó để triển khai các polyfill hiệu quả cho các hàm CSS. (Để biết lý do chi tiết, hãy xem cuộc thảo luận về www-style này).

srcset hình ảnh

Dưới đây là ví dụ về srcset:

<img alt="my awesome image"
  src="banner.jpeg"
  srcset="banner-HD.jpeg 2x, banner-phone.jpeg 640w, banner-phone-HD.jpeg 640w 2x">

Như bạn có thể thấy, ngoài các nội dung khai báo x mà image-set cung cấp, phần tử srcset cũng lấy các giá trị w và h tương ứng với kích thước của khung nhìn, cố gắng phân phát phiên bản phù hợp nhất. Nội dung ở trên sẽ phân phát Banner-phone.jpeg cho các thiết bị có chiều rộng khung nhìn dưới 640px, Banner-phone-HD.jpeg cho các thiết bị có DPI cao trên màn hình nhỏ, banner-HD.jpeg cho các thiết bị có DPI cao có màn hình lớn hơn 640px và banner.jpeg cho các thiết bị khác có DPI.

Sử dụng nhóm hình ảnh cho các phần tử hình ảnh

Do thuộc tính srcset trên các phần tử img không được triển khai trong hầu hết các trình duyệt, bạn có thể muốn thay thế các phần tử img bằng <div> bằng nền và sử dụng phương pháp đặt hình ảnh. Cách này sẽ có hiệu quả, nhưng bạn cần lưu ý. Hạn chế ở đây là thẻ <img> có giá trị ngữ nghĩa lâu dài. Trong thực tế, điều này chủ yếu quan trọng đối với trình thu thập dữ liệu web và lý do hỗ trợ tiếp cận.

Nếu quyết định sử dụng -webkit-image-set, bạn có thể muốn sử dụng thuộc tính CSS nền. Hạn chế của phương pháp này là bạn cần chỉ định kích thước hình ảnh, trong đó hình ảnh không xác định được nếu bạn đang sử dụng hình ảnh không phải 1x. Thay vì làm như vậy, bạn có thể sử dụng thuộc tính nội dung CSS như sau:

<div id="my-content-image"
  style="content: -webkit-image-set(
    url(icon1x.jpg) 1x,
    url(icon2x.jpg) 2x);">
</div>

Thao tác này sẽ tự động điều chỉnh tỷ lệ hình ảnh dựa trên tỷ lệ PixelRatio thiết bị. Hãy xem ví dụ này về kỹ thuật ở trên trong thực tế, với một phương án dự phòng bổ sung cho url() cho các trình duyệt không hỗ trợ image-set.

polyfilling srcset

Một tính năng hữu ích của srcset là đi kèm với tính năng dự phòng tự nhiên. Trong trường hợp không triển khai thuộc tính srcset, tất cả trình duyệt đều biết để xử lý thuộc tính src. Ngoài ra, vì đây chỉ là một thuộc tính HTML nên bạn có thể tạo polyfills bằng JavaScript.

Polyfill này đi kèm với quy trình kiểm thử đơn vị để đảm bảo rằng nó giống với thông số kỹ thuật nhất có thể. Ngoài ra, chúng tôi cũng áp dụng các biện pháp kiểm tra để ngăn không cho polyfill thực thi bất kỳ mã nào nếu srcset được triển khai nguyên gốc.

Đây là bản minh hoạ tính năng polyfill đang hoạt động.

Kết luận

Không có dấu đầu dòng kỳ diệu nào để giải quyết vấn đề về hình ảnh có DPI cao.

Giải pháp dễ nhất là tránh hoàn toàn hình ảnh mà hãy chọn SVG và CSS. Tuy nhiên, điều này không phải lúc nào cũng thực tế, đặc biệt là khi bạn có hình ảnh chất lượng cao trên trang web của mình.

Các phương pháp trong JS, CSS và việc sử dụng phía máy chủ đều có điểm mạnh và điểm yếu riêng. Tuy nhiên, phương pháp hứa hẹn nhất là tận dụng các tính năng mới của trình duyệt. Mặc dù trình duyệt hỗ trợ image-setsrcset vẫn chưa hoàn thiện, nhưng bạn có thể sử dụng một số phương án dự phòng hợp lý.

Tóm lại, tôi có các đề xuất như sau:

  • Đối với hình nền, hãy sử dụng image-set với các phương án dự phòng thích hợp cho các trình duyệt không hỗ trợ hình ảnh đó.
  • Đối với hình ảnh nội dung, hãy sử dụng srcset polyfill hoặc dự phòng để sử dụng image-set (xem ở trên).
  • Trong những trường hợp bạn sẵn sàng hy sinh chất lượng hình ảnh, hãy cân nhắc sử dụng hình ảnh 2x được nén quá mức.