Canvas có DPI cao

Giới thiệu

Màn hình HiDPI rất đẹp, giúp mọi thứ trông mượt mà và rõ ràng hơn. Tuy nhiên, chúng cũng đặt ra một loạt thách thức mới cho nhà phát triển. Trong bài viết này, chúng ta sẽ tìm hiểu những thách thức riêng biệt khi vẽ hình ảnh trong canvas trong bối cảnh màn hình HiDPI.

Thuộc tính devicePixelRatio

Hãy bắt đầu từ đầu. Trước khi có màn hình HiDPI, một pixel là một pixel (nếu chúng ta bỏ qua việc thu phóng và điều chỉnh theo tỷ lệ một chút) và đó là tất cả, bạn không thực sự cần thay đổi gì cả. Nếu bạn đặt chiều rộng của một thành phần là 100px thì đó là tất cả những gì bạn cần làm. Sau đó, một số thiết bị di động HiDPI đầu tiên bắt đầu xuất hiện với thuộc tính devicePixelRatio hơi khó hiểu trên đối tượng cửa sổ và có thể sử dụng trong truy vấn nội dung đa phương tiện. Thuộc tính này cho phép chúng ta hiểu tỷ lệ giữa cách các giá trị pixel (chúng ta gọi là giá trị pixel logic) trong – giả sử – CSS sẽ chuyển đổi thành số pixel thực tế mà thiết bị sẽ sử dụng khi hiển thị. Trong trường hợp của iPhone 4S, có devicePixelRatio là 2, bạn sẽ thấy rằng giá trị logic 100px tương đương với giá trị thiết bị 200px.

Điều này rất thú vị, nhưng điều đó có ý nghĩa gì đối với chúng ta, những nhà phát triển? Ban đầu, chúng tôi nhận thấy rằng các thiết bị này đang tăng kích thước hình ảnh. Chúng ta đã tạo hình ảnh ở chiều rộng pixel hợp lý của các phần tử và khi được vẽ ra, các hình ảnh đó sẽ được tăng tỷ lệ theo devicePixelRatio và sẽ bị mờ.

Hình ảnh được tăng tỷ lệ và làm mờ do devicePixelRatio
Hình 1 – Hình ảnh được nâng cấp và làm mờ do devicePixelRatio

Giải pháp thực tế cho vấn đề này là tạo hình ảnh được điều chỉnh theo tỷ lệ devicePixelRatio, sau đó sử dụng CSS để điều chỉnh hình ảnh theo tỷ lệ tương tự. Điều này cũng đúng với canvas!

function setupCanvas(canvas) {
  // Get the device pixel ratio, falling back to 1.
  var dpr = window.devicePixelRatio || 1;
  // Get the size of the canvas in CSS pixels.
  var rect = canvas.getBoundingClientRect();
  // Give the canvas pixel dimensions of their CSS
  // size * the device pixel ratio.
  canvas.width = rect.width * dpr;
  canvas.height = rect.height * dpr;
  var ctx = canvas.getContext('2d');
  // Scale all drawing operations by the dpr, so you
  // don't have to worry about the difference.
  ctx.scale(dpr, dpr);
  return ctx;
}

// Now this line will be the same size on the page
// but will look sharper on high-DPI devices!
var ctx = setupCanvas(document.querySelector('.my-canvas'));
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 200);
ctx.stroke();