בד ציור עם DPI גבוה

פול לואיס

מבוא

מסכי HiDPI הם יפהפיים, ובזכותם הכול נראה חלק ונקי יותר. אבל הם גם מציבים למפתחים אתגרים חדשים. במאמר זה נבחן את האתגרים הייחודיים של ציור תמונות על קנבס בהקשר של מסכי HiDPI.

המאפיין devicePixelRatio

נתחיל מההתחלה. בעבר, לפני שהיו לנו מסכי HiDPI, פיקסל היה פיקסל (אם נתעלם משינוי מרחק התצוגה ומשינוי קנה המידה קצת) וזהו זה, לא היה צורך לשנות משהו. אם הגדרת רוחב של 100 פיקסלים, זה כל מה שקיים. לאחר מכן, החלו להופיע כמה טלפונים ניידים מסוג HiDPI עם המאפיין devicePixelRatio מעט מסתורי באובייקט החלון, וניתן להשתמש בו בשאילתות מדיה. התכונה הזאת אפשרה לנו להבין את היחס בין ערכי הפיקסלים (שנקרא ערך פיקסלים לוגי) ב-CSS, למספר הממשי של הפיקסלים שהמכשיר ישתמש בהם בעיבוד. ב-iPhone 4S עם מכשירPixelRatio של 2, תראו שערך לוגי של 100 פיקסלים יהיה שווה ערך למכשיר של 200 פיקסלים.

זה מעניין, אבל מה המשמעות מבחינת המפתחים? בימים הראשונים, כולנו שמנו לב שהתמונות שלנו עברו שדרוג באמצעות המכשירים האלה. יצרנו תמונות ברוחב הפיקסלים ההגיוני של הרכיבים שלנו, וכשהן שורטטו, הן יעברו הגדלה על ידי מכשירPixelRatio והן יהיו מטושטשות.

תמונה שעברה שינוי וטשטוש עקב מכשירPixelRatio
איור 1 – תמונה שעברה שיפור וטשטוש בתמונה עקב PixelRatio של המכשיר

בפועל, הפתרון הוא ליצור תמונות שמותאמות למכשיר (PixelRatio) ולאחר מכן להשתמש ב-CSS כדי להקטין את התצוגה באותה כמות. אותו הדבר נכון גם לגבי קנבס!

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();