اختلاف الأداء

لا شكّ في أنّ الاختلاف والتشابه بين المبدأين المفضّل لديك هو مَن يعشقه. وعند استخدامها بحكمة، يمكن أن تضيف عمقًا ودقة إلى تطبيق الويب، ولكن المشكلة تكمن في أن تنفيذ الاختلاف على نحو جيد قد يكون تحديًا. في هذه المقالة، سنناقش حلاً فعالاً ويعمل على مستوى المتصفحات بنفس القدر من الأهمية.

صورة توضيحية لمنظر المنظر

النص المختصر (TL:DR)

  • لا تستخدِم أحداث التمرير أو background-position لإنشاء صور متحركة لتطبيق اختلاف المنظر.
  • استخدم تحويلات CSS ثلاثية الأبعاد لإنشاء تأثير اختلاف أكثر دقة.
  • بالنسبة إلى Mobile Safari، استخدِم position: sticky لضمان نشر تأثير اختلاف الألوان.

إذا أردت الحصول على الحلّ المطلوب، انتقِل إلى مستودع بيانات GitHub لعيّنات عنصر واجهة المستخدم واحصل على ملف JavaScript المساعد في Parallax. يمكنك مشاهدة عرض توضيحي مباشر لشريط تمرير اختلاف المنظر في مستودع GitHub.

متباينات المشاكل

في البداية، لنلقِ نظرة على طريقتين شائعتين لتحقيق تأثير اختلاف في المنظر، والسبب في ذلك على وجه الخصوص عدم ملاءمتهما لأغراضنا.

سيئ: استخدام أحداث التمرير

والمتطلب الرئيسي للتباين هو أنه يجب أن يتم إقرانه بالتمرير، وبالنسبة إلى كل تغيير فردي في موضع التمرير بالصفحة، يجب أن يتم تحديث موضع العنصر المتباين. رغم أن هذا قد يبدو بسيطًا، إلا أن هناك آلية مهمة للمتصفّحات الحديثة هي قدرتها على العمل بشكل غير متزامن. وهذا ينطبق، في حالتنا الخاصة، على تمرير الأحداث. وفي معظم المتصفّحات، يتم تقديم أحداث التمرير على أنّها "أفضل جهد" ولا يمكن ضمان عرضها في كل إطار من إطار الحركة أثناء التمرير.

توضّح هذه المعلومات المهمة سبب حاجتنا إلى تجنُّب استخدام حل مستنِد إلى JavaScript ينقل العناصر بناءً على أحداث التنقّل: لا تضمن JavaScript أن يتماشى التباين مع موضع التمرير في الصفحة. وفي الإصدارات القديمة من Mobile Safari، كانت أحداث التمرير تظهر في نهاية شريط التمرير، ما جعل من المستحيل إنشاء تأثير تمرير مستند إلى JavaScript. أما الإصدارات الأحدث، فتوفّر أحداث التمرير أثناء الرسم المتحرك، ولكن على غرار Chrome، على أساس "أفضل جهد". إذا كانت سلسلة التعليمات الرئيسية مشغولة بأي عمل آخر، لن يتم تسليم أحداث التمرير على الفور، ما يعني أنّه سيتم فقدان تأثير اختلاف الألوان.

سيئ: جارٍ تحديث background-position

هناك موقف آخر نريد تجنبه وهو الرسم على كل إطار. تحاول الكثير من الحلول تغيير background-position لتقديم مظهر مختلف، ما يجعل المتصفّح يعيد طلاء الأجزاء المتأثرة من الصفحة عند التنقّل في الصفحة، ما قد يؤدي إلى تعطّل الصورة المتحركة بشكل كبير.

إذا أردنا أن نضمن لك حدوث حركة اختلاف الألوان، فإننا نريد شيئًا يمكن تطبيقه كخاصية مسرَّعة (وهذا يعني في الوقت الحالي الالتزام بالإحالات الناجحة والتعتيم) ولا تعتمد على أحداث التمرير.

CSS في التصميم الثلاثي الأبعاد

لقد حقّق كل من Scott Kellum وKeith Clark جهدًا كبيرًا في مجال استخدام CSS ثلاثي الأبعاد لتحقيق حركة اختلاف التباين، وتتمثل التقنية المستخدمة في ما يلي بشكل فعّال:

  • عليك إعداد عنصر يشتمل على الانتقال للأعلى أو للأسفل باستخدام overflow-y: scroll (على الأرجح overflow-x: hidden).
  • طبِّق على العنصر نفسه القيمة perspective وضبط perspective-origin على top left أو 0 0.
  • إلى العناصر الثانوية لهذا العنصر قم بتطبيق ترجمة Z، وقم بتصغيرها مرة أخرى لتقديم حركة اختلاف الألوان دون التأثير في حجمها على الشاشة.

تظهر CSS لهذا المنهج على النحو التالي:

.container {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  perspective: 1px;
  perspective-origin: 0 0;
}

.parallax-child {
  transform-origin: 0 0;
  transform: translateZ(-2px) scale(3);
}

والتي تفترض وجود مقتطف HTML على النحو التالي:

<div class="container">
    <div class="parallax-child"></div>
</div>

ضبط المقياس للمنظور

سيؤدي دفع العنصر الثانوي إلى الخلف إلى تقليل حجمه بالتناسب مع قيمة المنظور. يمكنك حساب مقدار الزيادة في المحتوى باستخدام هذه المعادلة: (المنظور - المسافة) / المنظور. ولأنّنا على الأرجح نرغب في أن يظهر العنصر المتباين مع اختلاف حجمه، ولكنّه يظهر بالحجم الذي كتبناه، لذا يجب توسيعه بهذه الطريقة، بدلاً من تركه كما هو.

في حالة الرمز أعلاه، يكون المنظور 1 بكسل، ومسافة Z parallax-child هي -2 بكسل. وهذا يعني أنه يجب تكبير العنصر بمقدار 3x، وهي القيمة التي يتم إدخالها في الرمز البرمجي: scale(3).

بالنسبة إلى المحتوى الذي لا يتم تطبيق القيمة translateZ عليه، يمكنك استبدال القيمة صفر. وهذا يعني أنّ المقياس هو (perspective - 0)/ perspective، حيث يتم تسجيله على القيمة 1، ما يعني أنه لم يتم تحجيمه بالزيادة أو النقصان. مفيد جدًا،

طريقة عمل هذه الطريقة

من المهم أن تكون واضحًا بشأن سبب نجاح ذلك، لأننا سنستخدم هذه المعرفة قريبًا. التمرير هو تحويل فعّال، ولهذا السبب يمكن تسريعه، حيث ينطوي في الغالب على تغيير الطبقات باستخدام وحدة معالجة الرسومات. في تمرير نموذجي، وهو واحد بدون أي فكرة عن المنظور، يحدث التمرير بطريقة 1:1 عند مقارنة عنصر التمرير وعناصره الثانوية. إذا مرّرت عنصرًا لأسفل بمقدار 300px، يتم تحويل عناصره الثانوية بنفس المقدار: 300px.

ومع ذلك، فإن تطبيق قيمة المنظور على عنصر التمرير يعبث بهذه العملية؛ فهو يغير المصفوفات التي تستند إليها تحويل التمرير. والآن، قد يؤدي التمرير الذي يبلغ حجمه 300 بكسل إلى تحريك العناصر الثانوية بمقدار 150 بكسل فقط، استنادًا إلى قيمتَي perspective وtranslateZ التي اخترتها. إذا كانت قيمة العنصر translateZ هي 0، فسيتم تمريره بنسبة 1:1 (كما هو معتاد)، ولكن سيتم تمرير العنصر الثانوي الذي يتم دفعه بعيدًا عن مصدر المنظور بمعدل مختلف. النتيجة الصافية: حركة اختلاف المنظر. والأهم من ذلك، يتم التعامل مع هذه العملية تلقائيًا كجزء من جهاز التمرير الداخلي في المتصفّح، ما يعني عدم الحاجة إلى الاستماع إلى أحداث scroll أو تغيير background-position.

الذباب في المره: Mobile Safari

هناك تحذيرات بشأن كل تأثير، وأحد أهم تنبيهات التحولات هو الحفاظ على التأثيرات ثلاثية الأبعاد على العناصر الثانوية. إذا كان هناك عناصر في التسلسل الهرمي بين العنصر ذو المنظور وعناصره الفرعية المتباينة، فإن المنظور ثلاثي الأبعاد يكون "مسطّحًا"، مما يعني فقدان التأثير.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>

في رمز HTML أعلاه، تُعتبر علامة .parallax-container جديدة، وستنظِّم قيمة perspective بشكل فعّال وسنفقد تأثير اختلاف الألوان. يكون الحلّ، في معظم الحالات، واضحًا إلى حدّ ما: يمكنك إضافة transform-style: preserve-3d إلى العنصر، ما يؤدي إلى نشر أيّ تأثيرات ثلاثية الأبعاد (مثل قيمة منظورنا) تم تطبيقها على مستوى الشجرة.

.parallax-container {
  transform-style: preserve-3d;
}

في حالة Mobile Safari، تكون الأمور أكثر تعقيدًا. يعمل تطبيق overflow-y: scroll على عنصر الحاوية من الناحية الفنية، ولكن مقابل إمكانية تمرير عنصر التمرير. الحل هو إضافة السمة -webkit-overflow-scrolling: touch، لكنّها ستخفّف أيضًا من مستوى perspective ولن يكون هناك أي تناقض.

من منظور التحسين التدريجي، لا يعتبر هذا على الأرجح مشكلة كبيرة. إذا لم نستطع المقارن في كل موقف، سيستمر تطبيقنا في العمل، ولكن سيكون من الجيد إيجاد حل بديل.

position: sticky لإنقاذه.

في الواقع، هناك بعض المساعدة على شكل علامة position: sticky، وهي تسمح بـ "تثبيت" العناصر أعلى إطار العرض أو عنصر رئيسي معيّن أثناء الانتقال للأعلى أو للأسفل. المواصفات، مثل معظمها، كبيرة إلى حد ما، لكنها تحتوي على جوهرة صغيرة مفيدة داخل:

قد لا يبدو ذلك وكأنه يعني الكثير للوهلة الأولى، ولكن النقطة الرئيسية في تلك الجملة هي عندما تشير بالضبط إلى كيفية حساب مدى لصق أحد العناصر: "يتم حساب الإزاحة بالإشارة إلى أقرب أصل يتضمن مربع تمرير". بعبارة أخرى، يتم حساب المسافة لتحريك العنصر الثابت (لكي يظهر مرفقًا بعنصر آخر أو إطار العرض) قبل تطبيق أي عمليات تحويل أخرى، وليس بعد. هذا يعني أنه، إلى حد كبير مثل مثال التمرير السابق، إذا تم حساب الإزاحة عند 300 بكسل، فهناك فرصة جديدة لاستخدام المنظور (أو أي تحويل آخر) لمعالجة قيمة الإزاحة البالغة 300 بكسل قبل تطبيقها على أي عناصر ثابتة.

من خلال تطبيق position: -webkit-sticky على العنصر المتباين، يمكننا "عكس" تأثير التسوية لـ -webkit-overflow-scrolling: touch بشكل فعّال. ويضمن ذلك أن يشير العنصر المتباين إلى أقرب أصل يتضمن مربّع تمرير، وهو .container في هذه الحالة. بعد ذلك، وعلى غرار ما حدث سابقًا، تطبّق .parallax-container القيمة perspective، ما يؤدي إلى تغيير إزاحة التمرير المحسوبة وإنشاء تأثير اختلاف المنظر.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>
.container {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

.parallax-container {
  perspective: 1px;
}

.parallax-child {
  position: -webkit-sticky;
  top: 0px;
  transform: translate(-2px) scale(3);
}

وهذا يؤدي إلى استعادة تأثير اختلاف المنظر في Mobile Safari، وهو ما يمثل خبرًا ممتازًا في جميع الأحوال.

تنبيهات بشأن تحديد مواضع الإعلانات الثابتة

مع ذلك، هناك فرق، إذ إنّ position: sticky يغيّر آليات الاختلاف. يحاول الموضع اللاصق، حسنًا، لصق العنصر في حاوية التمرير، في حين أن الإصدار غير اللاصق لا يحدث ذلك. هذا يعني أن الاختلاف مع اللاصق ينتهي به الأمر هو عكس ذلك الذي ليس له:

  • مع position: sticky، كلما كان العنصر أقرب إلى z=0 أقل تحركه.
  • بدون استخدام position: sticky، كلما اقترب العنصر من z=0 زاد مقدار تحريكه.

إذا كان كل هذا يبدو تجريديًا بعض الشيء، فألقِ نظرة على هذا العرض التوضيحي من إعداد "روبرت فلاك"، والذي يوضح كيف تتصرف العناصر بشكل مختلف مع تحديد المواقع الثابتة وبدونها. لمعرفة الفرق، يجب استخدام Chrome Canary (وهو الإصدار 56 في وقت الكتابة) أو Safari.

لقطة شاشة لمنظور اختلاف المنظر

عرض توضيحي من إعداد "روبرت فلاك" يوضّح كيفية تأثير position: sticky في اختلاف المتباين

أخطاء متنوعة وأساليب بديلة

وكما هو الحال مع أي شيء، لا تزال هناك كُتل ونتوءات يجب أن تتم التغلب عليها:

  • الدعم الثابت غير ثابت. لا يزال الدعم قائمًا في Chrome، ولكن متصفّح Edge يفتقر إلى الدعم الكامل، كما أنّ Firefox يحتوي على أخطاء بسيطة عند دمج الثابت مع تغييرات المنظور. وفي مثل هذه الحالات، يمكن إضافة رمز برمجي صغير لإضافة position: sticky فقط (الإصدار -webkit- المسبَق) عند الحاجة، وهو الإصدار المخصص للمتصفح Mobile Safari فقط.
  • التأثير "لا يعمل فقط" في Edge. يحاول Edge التعامل مع التمرير على مستوى نظام التشغيل، وهو أمر جيد بشكل عام، ولكنه في هذه الحالة يمنعه من اكتشاف تغييرات المنظور أثناء التمرير. لحلّ هذه المشكلة، يمكنك إضافة عنصر ثابت الموضع، إذ إنّه يبدو أدّى إلى تبديل Edge إلى طريقة تمرير خارج نظام التشغيل، ويضمن أن يأخذ في الاعتبار التغييرات في المنظور.
  • "أصبح محتوى الصفحة ضخمًا جدًا". تراعي العديد من المتصفحات المقياس عند تحديد حجم محتوى الصفحة، ولكن للأسف لا يراعي Chrome وSafari المنظور المناسب. على سبيل المثال، إذا تم تطبيق مقياس 3x على عنصر، قد تظهر لك أشرطة تمرير وما شابه ذلك، حتى إذا كان العنصر 1x بعد تطبيق perspective. يمكنك حل هذه المشكلة من خلال تغيير حجم العناصر من أسفل يسار الشاشة (باستخدام transform-origin: bottom right)، لأنّ ذلك سيؤدي إلى ظهور العناصر الكبيرة الحجم لتصبح "منطقة سلبية" (عادةً في الجزء العلوي الأيمن) من المنطقة القابلة للتمرير، ولا تسمح لك المناطق القابلة للتمرير برؤية المحتوى في المنطقة السلبية أو الانتقال إليه.

الخلاصة

المنظر هو تأثير ممتع عند استخدامه بعناية. وكما ترون، من الممكن تنفيذه بطريقة أداء عالية، ومقترن بالتمرير، وعبر المتصفحات. وبما أنّ الأمر يتطلّب قدرًا قليلاً من التعابير الحسابية وقليل من النصوص النموذجية للحصول على التأثير المطلوب، أعددنا مكتبة مساعِدة صغيرة وعيّنة يمكنك العثور عليها في مستودع GitHub لنماذج عناصر واجهة المستخدم.

استمتع باللعب وأخبرنا بمدى تطورك.