Canvas แบบ DPI สูง

เกริ่นนำ

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

พร็อพเพอร์ตี้ PixelRatio ของอุปกรณ์

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

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

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

วิธีแก้ไขโดยแท้จริงคือการสร้างรูปภาพที่ปรับขนาดตาม 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();