Canvas แบบ DPI สูง

บทนำ

หน้าจอ HiDPI นั้นยอดเยี่ยมมาก เพราะทำให้ทุกอย่างดูราบรื่นและสะอาดตา แต่ก็มีความท้าทายใหม่ๆ เกิดขึ้นกับนักพัฒนาซอฟต์แวร์ด้วยเช่นกัน ในบทความนี้ เราจะมาพิจารณาความท้าทายเฉพาะของการวาดภาพในผืนผ้าใบในบริบทของหน้าจอ HiDPI

พร็อพเพอร์ตี้ devicePixelRatio

มาเริ่มกันตั้งแต่ต้น สมัยก่อนที่เรายังไม่มีหน้าจอ HiDPI พิกเซลก็คือพิกเซล (หากไม่สนใจการซูมและการปรับขนาดสักพัก) และคุณก็ไม่จำเป็นต้องเปลี่ยนแปลงอะไรเลย ถ้าคุณตั้งค่าความกว้าง 100 พิกเซล เท่ากับขนาดนั้นได้เลย จากนั้นโทรศัพท์มือถือ HiDPI รุ่นแรกๆ ก็เริ่มปรากฏขึ้นพร้อมพร็อพเพอร์ตี้ devicePixelRatio ที่อึมครึมเล็กน้อยบนออบเจ็กต์ window และพร้อมใช้งานใน Media Query พร็อพเพอร์ตี้นี้ช่วยให้เราเข้าใจอัตราส่วนของค่าพิกเซล (ซึ่งเราเรียกว่าค่าพิกเซลเชิงตรรกะ) ใน CSS ที่แปลงเป็นจำนวนพิกเซลจริงที่อุปกรณ์จะใช้เมื่อแสดงผล ในกรณีของ iPhone 4S ซึ่งมี devicePixelRatio เท่ากับ 2 คุณจะเห็นค่าตรรกะ 100 พิกเซลเท่ากับค่าอุปกรณ์ 200 พิกเซล

น่าสนใจ แต่การเปลี่ยนแปลงนี้ส่งผลต่อนักพัฒนาแอปอย่างไร ในช่วงแรก เราทุกคนเริ่มสังเกตเห็นว่าอุปกรณ์เหล่านี้ปรับขนาดรูปภาพของเรา เราสร้างรูปภาพด้วยขนาดพิกเซลเชิงตรรกะขององค์ประกอบ และเมื่อวาดรูปภาพออกมา ระบบจะปรับขนาดรูปภาพด้วย devicePixelRatio และรูปภาพจะเบลอ

รูปภาพที่กำลังเพิ่มขนาดและเบลอเนื่องจาก devicePixelRatio
รูปที่ 1 - รูปภาพที่ปรับขนาดและเบลอเนื่องจาก devicePixelRatio

วิธีแก้ไขโดยเบื้องต้นก็คือการสร้างรูปภาพที่ขยายขนาดขึ้นโดย devicePixelRatio จากนั้นใช้ CSS เพื่อลดขนาดลงเท่าๆ กัน ซึ่งสำหรับ 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();