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

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

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

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ẽ trình bày 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 Pandora này, hãy nhớ rằng web có nhiều công nghệ mạnh mẽ, phần lớn độc lập với độ 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 có thể tránh sử dụng 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 lại không có ý nghĩa vì các phiên bản được tăng tỷ lệ thường không đẹp.

Thông tin khái quát

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

Thời kỳ đầu, màn hình máy tính có mật độ điểm ảnh là 72 hoặc 96dpi (số điểm trên mỗi inch).

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

Tất nhiên, mục tiêu tối thượng là một màn hình mà 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, thế hệ 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 tăng mật độ điểm ảnh.

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

Baboon 1x
Khỉ đầu chó 2x
Baboons! với mật độ pixel khác nhau.

Pixel trên web

Khi thiết kế web, 99% màn hình có độ phân giải 96dpi (hoặc giả vờ là) và có rất ít quy định về sự khác biệt về mặt này. Do sự khác biệt lớn về kích thước và mật độ màn hình, chúng tôi cần một cách thức chuẩn để làm cho hình ảnh trông đẹp trên nhiều mật độ và kích thước màn hình.

Gần đây, quy cách HTML đã giải quyết vấn đề này bằng cách xác định một pixel tham chiếu mà nhà sản xuất sử dụng để xác định kích thước của một 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 của pixel thực tế của thiết bị so với pixel chuẩn hoặc pixel 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 điện thoại thông minh có màn hình với kích thước pixel thực là 180 pixel trên mỗi 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ữ 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 ở kích thước 28 inch, kích thước lý tưởng là 96 pixel/inch. Tuy nhiên, vì đó là điện thoại thông minh nên mọi người sẽ cầm thiết bị gần khuôn mặt hơn so với khi 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 độ tiêu chuẩn (96ppi) để có được mật độ pixel lý tưởng cho khoảng cách đã cho.

    idealPixelDensity = (28/18) * 96 = 150 pixel/inch (xấp xỉ)

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

    devicePixelRatio = 180/150 = 1,2

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

Vì vậy, giờ đây, khi trình duyệt cần biết cách đổi kích thước hình ảnh cho vừa với màn hình theo độ phân giải lý tưởng hoặc tiêu chuẩn, trình duyệt sẽ tham chiếu đế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 thực. Công thức để chuyển đổi giữa pixel lý tưởng (do thông số kỹ thuật web xác định) và pixel thực (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ị có xu hướng làm tròn devicePixelRatios (DPR). iPhone và iPad của Apple báo cáo DPR là 1 và các thiết bị tương đương với màn hình Retina báo cáo 2. Quy cách CSS đề xuất rằng

đơn vị pixel đề cập đến số nguyên của các pixel thiết bị gần nhất với pixel tham chiếu.

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

Tuy nhiên, thực tế về chế độ ngang của thiết bị lại đ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, được tính theo cách tương tự như trên. Chúng tôi hy vọng sẽ có thêm nhiều thiết bị có DPR khác nhau trong tương lai. Do đó, bạn không bao giờ được giả định rằng ứng dụng khách sẽ có DPR dạng 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ể, về cơ bản chia thành hai loại:

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

Phương pháp sử dụng hình ảnh đơn lẻ: sử dụng một hình ảnh nhưng hãy khéo léo sử dụng hình ảnh đó. Những phương pháp này có nhược điểm là bạn không thể tránh khỏi việc 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 một hình ảnh:

  • Hình ảnh HiDPI bị nén nhiều
  • Định dạng hình ảnh cực kỳ tuyệt vời
  • Định dạng hình ảnh luỹ tiến

Nhiều phương pháp sử dụng hình ảnh: sử dụng nhiều hình ảnh, nhưng hãy lựa chọn một cách thông minh để chọn hình ảnh cần tải. Các phương pháp này có chi phí cố hữu để nhà phát triển tạo nhiều phiên bản của cùng một thành phần, sau đó tìm ra chiến lược 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 của trình duyệt (image-set(), <img srcset>)

Hình ảnh HiDPI bị nén nhiều

Hình ảnh đã chiếm tới 60% băng thông dùng để 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ả ứng dụng, chúng tôi sẽ tăng số lượng này. Nó sẽ lớn hơn bao nhiêu?

Tôi đã chạy một số thử nghiệm tạo 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 chúng:

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

Từ mẫu nhỏ, 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 tốt về chất lượng so với kích thước. Theo tôi, hình ảnh nén 2x 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 có chất lượng thấp, được nén ở mức 2x 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. Phương pháp nêu trên dẫn đến các hình phạt về chất lượng hình ảnh. Nếu 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à độ nhiễu hạt tăng lên. Các 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à quan trọng (ví dụ: ứng dụng trình 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.

Bản so sánh ở trên được thực hiện hoàn toàn bằng các tệp JPEG nén. Lưu ý rằng 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 mang lại cho 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!

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

Tuy nhiên, cách tốt hơn để thực hiện việc này là trực tiếp trong CSS bằng cách sử dụng hàm image(). Vì vậy, nếu có hình ảnh WebP và 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. Thứ nhất, image() không được triển khai rộng rãi. Thứ hai, mặc dù việc nén WebP vượt trội so với JPEG, nhưng vẫn có sự cải thiện tương đối – nhỏ hơn khoảng 30% dựa trên thư viện WebP này. Do đó, chỉ riêng WebP là chưa đủ để giải quyết vấn đề DPI cao.

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

Các định dạng hình ảnh tăng tiến như JPEG 2000, JPEG tăng tiến, PNG tăng tiến và GIF có lợi ích (đôi khi gây tranh cãi) là cho phép xem hình ảnh trước khi tải xong. Các lớp này có thể gây ra một số hao tổn về kích thước, mặc dù có bằng chứng mâu thuẫn về điều này. Jeff Atwood tuyên bố rằng chế độ tăng dần "tăng 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 cho biết 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 tăng tiến có vẻ rất hứa hẹn trong bối cảnh phân phát hình ảnh chất lượng cao 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 sau 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ả các điểm cải thiện độ trung thực đều là điểm ảnh phụ).

Mặc dù dễ dàng chấm dứt kết nối, nhưng việc khởi động lại thường tốn kém. Đối với một trang web có nhiều hình ảnh, phương pháp hiệu quả nhất là duy trì một kết nối HTTP, sử dụng lại kết nối đó càng lâu càng tốt. Nếu kết nối bị chấm dứt sớm vì 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 có độ 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. Yêu cầu này cho phép trình duyệt chỉ định một phạm vi byte để tìm nạp. Trình duyệt thông minh có thể tạo một 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. Đáng tiếc là Phạm vi HTTP được hỗ trợ ít trong các máy chủ web, khiến phương pháp này không thiết thực.

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 cần tải, mà chỉ có thể thay đổi độ tin cậy của cùng một hình ảnh. Do đó, trường hợp sử dụng "hướng dẫn nghệ thuật" không được đề cập đến.

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

Phương pháp đầu tiên và rõ ràng nhất để quyết định hình ảnh 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 và làm điều đúng đắn. Bạn có thể xác định tỷ lệ pixel của thiết bị qua window.devicePixelRatio, lấy thông tin về chiều rộng và chiều cao màn hình, thậm chí có thể dùng navigator.connection hoặc gửi một yêu cầu giả, như thư viện foresight.js để đánh dấu kết nối mạng. Sau khi thu thập tất cả thông tin này, bạn có thể quyết định tải hình ảnh nào.

Có khoảng một triệu thư viện JavaScript làm những việc tương tự như trên, nhưng đáng tiếc là không có thư viện nào đặc biệt nổi bật.

Một nhược điểm lớn của phương pháp này là việc sử dụng JavaScript có nghĩa là 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 xem trước đã 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ề vấn đề này trong bài viết của Jason Grigsby.

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

Bạn có thể trì 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 từng hình ảnh mà bạn phân phát. Một trình xử lý như vậy sẽ kiểm tra khả năng hỗ trợ Retina dựa trên User-Agent (phần 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 thích hợp (được đặt tên theo một số quy ước đã biết).

Thật không may, 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 xem thiết bị sẽ nhận được hình ảnh chất lượng cao hay thấp. Ngoài ra, không cần phải nói rằng mọi thứ liên quan đến Agent-User đều là hành vi xâm nhập và bạn nên tránh nếu có thể.

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

Các truy vấn đa phương tiện CSS cho phép bạn nêu ý định của mình và cho phép trình duyệt thực hiện công việc đúng đắn thay mặt bạn vì mang tính khai báo. Ngoài cách truy vấn nội dung nghe nhìn phổ biến nhất — kích thước thiết bị phù hợp — bạn cũng có thể so khớp devicePixelRatio. Truy vấn nội dung đa phương tiện được liên kết là device-pixel-ratio 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, 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); }
}

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

@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 sẽ lấy lại được các lợi ích của tính năng phân tích cú pháp xem trước mà giải pháp JS đã mất. Bạn cũng có thể linh hoạt chọn đ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), điều này không có trong phương pháp phía máy chủ.

Rất tiếc, cách này vẫn còn hơi khó sử dụng và dẫn đến CSS trông lạ mắt (hoặc yêu cầu xử lý trước). Ngoài ra, phương pháp này chỉ dành cho các thuộc tính CSS, vì vậy, không có cách nào để đặt <img src> và tất cả hình ảnh của bạn đều 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 tình huống mà điện thoại thông minh có DPI cao phải tải một thành phần hình ảnh khổng lồ lên gấp 2 lần khi đang sử 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 về việc hỗ trợ nền tảng web cho vấn đề hình ảnh có DPI cao. Gần đây, Apple đã xâm nhập vào nền tảng này, đưa hàm CSS image-set() vào WebKit. Do đó, cả Safari và Chrome đều hỗ trợ tính năng này. Vì là một hàm CSS, nên image-set() không giải quyết được vấn đề cho 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 có cách triển khai tham chiếu nào! Phần tiếp theo sẽ tìm hiểu sâu hơn về image-setsrcset.

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

Cuối cùng, quyết định về phương pháp bạn thực hiện 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. Hiện tại, 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 có thể đến tương lai lý tưởng đó.

Trước tiên, hai loại này khác nhau như thế nào? image-set() là một hàm CSS, thích hợp để sử 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 thông tin 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 của khung nhìn.

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

Hàm image-set() CSS có tiền tố là -webkit-image-set(). Cú pháp khá đơn giản, lấy một hoặc nhiều 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 liên quan. Ví dụ:

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

Điều này cho trình duyệt biết rằng có hai hình ảnh để chọn. Một trong số đó được tối ưu hoá cho màn hình 1x và màn hình còn lại được tối ưu hoá cho màn hình 2x. Sau đó, trình duyệt sẽ chọn nội dung cần tải, dựa trên nhiều yếu tố, thậm chí có thể bao gồm cả 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 cho phù hợp. Nói cách khác, trình duyệt giả định rằng 2 hình ảnh có kích thước lớn gấp đôi hình ảnh 1x, nên sẽ thu nhỏ hình ảnh 2x theo hệ số 2 để hình ảnh đó có vẻ 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 thiết bị nhất định theo dpi.

Cách 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. Các trình duyệt này sẽ không hiển thị hình ảnh nào cả! Đây rõ ràng là một vấn đề, vì vậy bạn phải sử dụng phương thức dự phòng (hoặc một loạt phương thức 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
);

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

Bản minh hoạ này sử dụng image-set() để tải hình ảnh chính xác, 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, bạn có thể thắc mắc tại sao không chỉ polyfill (tức là tạo một trình bổ trợ JavaScript cho) image-set() và gọi là xong? Hóa ra, khá khó để triển khai các polyfill hiệu quả cho các hàm CSS. (Để biết giải thích chi tiết lý do, hãy xem phần thảo luận về kiểu www).

Srcset hình ảnh

Dưới đây là một 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ó màn hình nhỏ có DPI cao, 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 mọi thiết bị khác.

Sử dụng image-set cho phần tử hình ảnh

Vì thuộc tính srcset trên các phần tử img không được triển khai trong hầu hết trình duyệt, nên bạn có thể thay thế các phần tử img bằng <div> có nền và sử dụng phương pháp nhóm hình ảnh. Cách này sẽ hoạt động, nhưng có một số 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 thông tin trên web và các lý do liên quan đến khả năng tiếp cận.

Nếu sử dụng -webkit-image-set, bạn có thể muốn sử dụng thuộc tính CSS nền. Nhược điểm của phương pháp này là bạn cần chỉ định kích thước hình ảnh. Kích thước này không xác định 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 CSS nội dung 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 devicePixelRatio. Hãy xem ví dụ này về kỹ thuật trên đang hoạt động, với một phương án dự phòng bổ sung là url() cho các trình duyệt không hỗ trợ image-set.

Tập hợp srcset polyfilling

Một tính năng tiện lợi của srcset là đi kèm với tính năng dự phòng tự nhiên. Trong trường hợp thuộc tính srcset không được triển khai, tất cả trình duyệt đều biết cách 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 polyfill bằng JavaScript.

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

Dưới đây là bản minh hoạ về polyfill đang hoạt động.

Kết luận

Không có viên đạn thần kỳ nào có thể giải quyết vấn đề hình ảnh có DPI cao.

Giải pháp dễ nhất là tránh sử dụng hình ảnh hoàn toàn, thay vào đó 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à nếu bạn có hình ảnh chất lượng cao trên trang web.

Các phương pháp trong JS, CSS và sử dụng phía máy chủ đều có điểm mạnh và điểm yếu. 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 chỉnh, nhưng hiện nay chúng tôi có các phương án dự phòng hợp lý để bạn sử dụng.

Tóm lại, các đề xuất của tôi 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ợ.
  • Đối với hình ảnh nội dung, hãy sử dụng srcset polyfill hoặc sử dụng phương thức dự phòng sử dụng image-set (xem ở trên).
  • Trong 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 nén 2x.