Canvas có DPI cao

Giới thiệu

Màn hình HiDPI rất đáng yêu, giúp mọi thứ trở nên mượt mà và gọn gàng hơn. Tuy nhiên, những thay đổi này cũng đặt ra một loạt thách thức mới cho các nhà phát triển. Trong bài viết này, chúng ta sẽ tìm hiểu những thử thách độc đáo của việc vẽ hình ảnh trên 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 chúng ta 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à chia tỷ lệ một chút) và thế là xong, bạn thực sự không cần phải thay đổi bất cứ điều gì xung quanh. Nếu bạn thiết lập chiều rộng 100px thì đó là tất cả những gì cần thiết. Sau đó, một vài thiết bị di động HiDPI đầu tiên xuất hiện với thuộc tính devicePixelRatio hơi bí ẩn trên đối tượng cửa sổ và có sẵn để sử dụng trong các truy vấn nội dung nghe nhìn. Những gì thuộc tính này cho phép chúng tôi làm được là tìm hiểu tỷ lệ giữa các giá trị pixel (mà chúng tôi gọi là giá trị pixel logic) trong đó CSS chuyển sang 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 đó thú vị, nhưng điều đó có ý nghĩa gì đối với các nhà phát triển của chúng tôi? Khi mới bắt đầu, tất cả chúng ta đều bắt đầu nhận thấy rằng hình ảnh của chúng ta được tăng kích thước nhờ các thiết bị này. Chúng tôi đã tạo hình ảnh theo chiều rộng pixel hợp lý của các phần tử và khi chúng được vẽ, hình ảnh sẽ được tăng cường bằng devicePixelRatio và sẽ bị mờ.

Hình ảnh được tăng cường và làm mờ do tỷ lệ pixel của thiết bị
Hình 1 – Hình ảnh được tăng cường và làm mờ theo tỷ lệ devicePixelRatio

Trên thực tế, giải pháp cho vấn đề này là tạo hình ảnh tăng tỷ lệ theo devicePixelRatio và sau đó sử dụng CSS để thu nhỏ hình ảnh theo cùng một lượng và điều này cũng đúng đối 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();