كم عدد البكسلات حقًا في اللوحة؟
منذ الإصدار 84 من Chrome، يتيح ResizeObserver قياس مربّع جديد يُسمى devicePixelContentBox
، والذي يقيس أبعاد العنصر بالبكسل المادي. يتيح ذلك عرض رسومات دقيقة بدقة بكسل، خاصةً في سياق الشاشات ذات الكثافة العالية.
الخلفية: وحدات بكسل CSS ووحدات بكسل اللوحة ووحدات البكسل الفعلية
على الرغم من أنّنا غالبًا ما نعمل باستخدام وحدات طول مجردة مثل em
أو %
أو vh
، إلا أنّ الأمر كله يعتمد على البكسل. عندما نحدّد حجم عنصر أو موضعه في CSS، سيحوّل محرّك تنسيق المتصفّح هذه القيمة في النهاية إلى وحدات بكسل (px
). وهذه هي "وحدات بكسل CSS"، التي لها سجلّ كبير ولا ترتبط إلا بعلاقة فضفاضة بوحدات البكسل التي تظهر على شاشتك.
لفترة طويلة، كان من المعقول إلى حدٍ ما تقدير كثافة بكسل الشاشة لأي مستخدم باستخدام 96 نقطة لكل بوصة (PPI)، ما يعني أنّ أي شاشة معيّنة ستحتوي على 38 بكسل تقريبًا لكل سنتيمتر. بمرور الوقت، زاد حجم الشاشات و/أو صغر أو بدأت تحتوي على عدد أكبر من البكسل في المساحة السطحية نفسها. إضافةً إلى ذلك، فإنّ الكثير من المحتوى على الويب يحدّد أبعاده، بما في ذلك أحجام الخطوط، بوحدة px
، ما يؤدي إلى ظهور نص غير مقروء على هذه الشاشات ذات الكثافة العالية (HiDPI). وكإجراء مضاد، تخفي المتصفّحات كثافة البكسل الفعلية للشاشة وتتظاهر بدلاً من ذلك بأنّ المستخدم لديه شاشة بدقة 96 نقطة لكل بوصة. تمثّل وحدة px
في CSS حجم بكسل واحد على هذه الشاشة الافتراضية التي تبلغ دقتها 96 نقطة لكل بوصة، ومن هنا جاء اسم "بكسل CSS". لا تُستخدَم هذه الوحدة إلا للقياس والوضع. قبل إجراء أيّ عملية عرض فعلية، تتم تحويل الصورة إلى وحدات بكسل مادية.
كيف يمكننا الانتقال من هذا العرض الافتراضي إلى شاشة المستخدم الفعلية؟ يجب ملء الحقل "devicePixelRatio
". توضّح لك هذه القيمة الشاملة عدد البكسلات الحقيقية التي تحتاج إليها لإنشاء بكسل واحد في CSS. إذا كانت قيمة devicePixelRatio
(dPR) هي 1
، يعني ذلك أنّك تعمل على شاشة بكثافة 96 نقطة في البوصة تقريبًا. إذا كانت لديك شاشة بدقة عالية، من المحتمل أن يكون لديك كثافة بكسل 2
. على الهواتف، من الشائع أن تظهر قيم dPR أعلى (وأغرب) مثل 2
أو 3
أو حتى 2.65
. من المهمّ ملاحظة أنّ هذه القيمة دقيقة، ولكنّها لا تسمح لك باستخراج قيمة النقاط لكل بوصة الفعلية للشاشة. يعني "معامل الكثافة النوعية للبكسل" الذي يبلغ 2
أنّه سيتم ربط بكسل واحد من CSS ببكسلَين فعليَّين بالضبط.
1
وفقًا لمتصفّح Chrome…يبلغ عرضها 3440 بكسل ويبلغ عرض منطقة العرض 79 سم.
ويؤدي ذلك إلى الحصول على دقة 110 نقطة في البوصة. قريب من 96، ولكن ليس تمامًا.
وهذا هو السبب أيضًا في أنّ رمز <div style="width: 1cm; height: 1cm">
لن يُظهر حجمه بدقة 1 سم على معظم الشاشات.
أخيرًا، يمكن أن تتأثر دقة الصورة المعروضة أيضًا بميزة التكبير/التصغير في المتصفّح. في حال التكبير، يزيد المتصفّح من قيمة dPR المُبلّغ عنها، ما يؤدي إلى عرض كل العناصر بحجم أكبر. في حال وضع علامة في المربّع devicePixelRatio
في DevTools Console أثناء التكبير/التصغير، يمكنك رؤية القيم الكسورية تظهر.
لنضيف عنصر <canvas>
إلى المزيج. يمكنك تحديد عدد البكسلات التي تريد أن تتضمّنها اللوحة باستخدام السمتَين width
وheight
. وبالتالي، سيكون <canvas width=40 height=30>
لوحة بحجم 40 × 30 بكسل. ومع ذلك، لا يعني ذلك أنّه سيتم عرضه بدقة 40 x 30 بكسل. ستستخدم اللوحة تلقائيًا السمتَين width
وheight
لتحديد حجمها الأساسي، ولكن يمكنك تغيير حجم اللوحة بشكل عشوائي باستخدام جميع خصائص CSS التي تعرفها وتفضّلها. استنادًا إلى كل ما تعلمناه حتى الآن، قد يتبادر إلى ذهنك أنّ هذا الإجراء قد لا يكون مثاليًا في كل السيناريوهات. قد ينتهي الأمر بتغطية بكسل واحد على اللوحة لعدة وحدات بكسل فعلية، أو جزء بسيط من وحدة بكسل فعلية. ويمكن أن يؤدي ذلك إلى ظهور عناصر مرئية غير مرغوب فيها.
باختصار: عناصر اللوحة لها حجم محدّد لتحديد المنطقة التي يمكنك الرسم عليها. إنّ عدد بكسل اللوحة مستقل تمامًا عن حجم عرض اللوحة، والذي يتم تحديده بالبكسل في CSS. لا يتطابق عدد وحدات البكسل في CSS مع عدد وحدات البكسل الفعلية.
دقة رائعة
في بعض السيناريوهات، من المستحسن الحصول على تعيين دقيق من وحدات بكسل اللوحة إلى وحدات البكسل الفعلية. إذا تمّ تحقيق هذا الربط، يُعرف باسم "دقة بكسل مثالية". إنّ المعالجة الدقيقة للبكسل أمر بالغ الأهمية لعرض النص بوضوح، خاصةً عند استخدام المعالجة الدقيقة للوحدات الفرعية للبكسل أو عند عرض الرسومات التي تتضمّن خطوطًا محاذية بإحكام ذات سطوع متغيّر.
لتحقيق ما يقرب من لوحة بدقة بكسل على الويب، كان هذا النهج هو المفضّل بشكل أو بآخر:
<style>
/* … styles that affect the canvas' size … */
</style>
<canvas id="myCanvas"></canvas>
<script>
const cvs = document.querySelector('#myCanvas');
// Get the canvas' size in CSS pixels
const rectangle = cvs.getBoundingClientRect();
// Convert it to real pixels. Ish.
cvs.width = rectangle.width * devicePixelRatio;
cvs.height = rectangle.height * devicePixelRatio;
// Start drawing…
</script>
قد يتساءل القارئ الذكي عما يحدث عندما لا تكون dPR قيمة صحيحة. هذا سؤال جيد ويمثّل جوهر هذه المشكلة بالكامل. بالإضافة إلى ذلك، إذا حدّدت موضع عنصر أو حجمه باستخدام النسب المئوية أو vh
أو قيم غير مباشرة أخرى، من المحتمل أن يتم تحويلها إلى قيم بكسل كسرية في CSS. يمكن أن ينتهي عنصر يحتوي على margin-left: 33%
بمستطيل على النحو التالي:
تكون وحدات البكسل في CSS افتراضية بالكامل، لذا من الناحية النظرية، لا بأس بوجود أجزاء من وحدات البكسل، ولكن كيف يحدّد المتصفّح عملية الربط بوحدات البكسل الفعلية؟ لأنّ وحدات البكسل الحقيقية الكسورية غير متوفّرة.
ربط البكسل
يُعرف الجزء من عملية تحويل الوحدات الذي يهتم بمحاذاة العناصر مع وحدات البكسل الفعلية باسم "تثبيت البكسل"، وهو ينفّذ ما يُفترض به: يُثبّت قيم وحدات البكسل الكسورية إلى قيم وحدات البكسل الصحيحة. تختلف طريقة حدوث ذلك باختلاف المتصفّح. إذا كان لدينا عنصر بعرض 791.984px
على شاشة يكون فيها dPR = 1، قد يعرض أحد المتصفّحات العنصر بحجم 792px
بكسل فعلي، بينما قد يعرضه متصفّح آخر بحجم 791px
. هذا خطأ ببكسل واحد فقط، ولكن يمكن أن يكون هذا الخطأ ضارًا بالصور التي يجب أن تكون دقيقة بدقة بكسل. ويمكن أن يؤدي ذلك إلى التمويه أو ظهور المزيد من العناصر المرئية، مثل تأثير موري.
devicePixelContentBox
يوفّر لك devicePixelContentBox
مربّع محتوى العنصر بوحدات وحدات البكسل على الجهاز (أي وحدات البكسل الفعلية). وهي جزء من ResizeObserver
. على الرغم من أنّ ResizeObserver متاح الآن في جميع المتصفحات الرئيسية منذ Safari 13.1، لا تتوفّر السمة devicePixelContentBox
إلا في الإصدار 84 من Chrome والإصدارات الأحدث في الوقت الحالي.
كما هو موضّح في مقالة ResizeObserver
: يشبه document.onresize
للعناصر، سيتمّ استدعاء دالة ردّ الاتصال الخاصة بعنصر ResizeObserver
قبل الطلاء وبعد التنسيق. وهذا يعني أنّ مَعلمة entries
في دالة الاستدعاء ستحتوي على أحجام جميع العناصر المرصودة قبل رسمها مباشرةً. في سياق مشكلة اللوحة الموضّحة أعلاه، يمكننا استخدام هذه الفرصة لتعديل عدد البكسل على اللوحة، ما يضمن لنا الحصول على تعيين دقيق بين وحدات البكسل في اللوحة ووحدات البكسل الفعلية.
const observer = new ResizeObserver((entries) => {
const entry = entries.find((entry) => entry.target === canvas);
canvas.width = entry.devicePixelContentBoxSize[0].inlineSize;
canvas.height = entry.devicePixelContentBoxSize[0].blockSize;
/* … render to canvas … */
});
observer.observe(canvas, {box: ['device-pixel-content-box']});
تسمح لك سمة box
في عنصر الخيارات الخاص بـ observer.observe()
بتحديد المقاسات التي تريد مراقبتها. وبالتالي، على الرغم من أنّ كل ResizeObserverEntry
سيقدّم دائمًا borderBoxSize
وcontentBoxSize
وdevicePixelContentBoxSize
(شرط أن يكون المتصفّح متوافقًا مع ذلك)، لن يتمّ استدعاء دالة الاستدعاء إلا إذا تغيّر أيّ من مقاييس مربّع المرصودة.
باستخدام هذه السمة الجديدة، يمكننا أيضًا إضافة تأثيرات متحركة إلى حجم اللوحة وموقعها (ما يضمن بشكل فعّال قيمًا كسرية للبكسل)، ولن تظهر أي تأثيرات موير على العرض. إذا كنت تريد الاطّلاع على تأثير Moiré في الأسلوب باستخدام getBoundingClientRect()
، وكيفية السماح لك بتجنّبه باستخدام السمة ResizeObserver
الجديدة، يمكنك الاطّلاع على العرض التجريبي في Chrome 84 أو الإصدارات الأحدث.
رصد الميزات
للتحقّق مما إذا كان متصفح المستخدم متوافقًا مع devicePixelContentBox
، يمكننا مراقبة أي عنصر والتحقّق مما إذا كان الموقع الإلكتروني متوفّرًا في ResizeObserverEntry
:
function hasDevicePixelContentBox() {
return new Promise((resolve) => {
const ro = new ResizeObserver((entries) => {
resolve(entries.every((entry) => 'devicePixelContentBoxSize' in entry));
ro.disconnect();
});
ro.observe(document.body, {box: ['device-pixel-content-box']});
}).catch(() => false);
}
if (!(await hasDevicePixelContentBox())) {
// The browser does NOT support devicePixelContentBox
}
الخاتمة
تُعدّ البكسلات موضوعًا معقّدًا بشكلٍ مفاجئ على الويب، ولم يكن بإمكانك حتى الآن معرفة العدد الدقيق للبكسلات الفعلية التي يشغلها العنصر على شاشة المستخدم. يمنحك موقع devicePixelContentBox
الجديد على ResizeObserverEntry
هذه المعلومة ويسمح لك بإنشاء صور بدقة بكسل باستخدام <canvas>
. تتوفّر devicePixelContentBox
في الإصدار 84 من Chrome والإصدارات الأحدث.