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