لا شكّ في أنّ اختلاف المتباينات ما زال في حياتنا، سواء أحببتها أو أكرهها. وعند استخدامها بحكمة، يمكن أن تضيف عمقًا ودقة إلى تطبيق الويب، ولكن المشكلة تكمن في أن تنفيذ الاختلاف على نحو جيد قد يكون تحديًا. في هذه المقالة، سنطرح حلًا يحقق أداءً جيدًا ويعمل على جميع المتصفّحات.
النص المختصر (TL:DR)
- لا تستخدِم أحداث التمرير أو
background-position
لإنشاء صور متحركة لتطبيق اختلاف المنظر. - استخدِم عمليات التحويل الثلاثية الأبعاد في CSS لإنشاء تأثير تمويه أكثر دقة.
- بالنسبة إلى Safari على الأجهزة الجوّالة، استخدِم
position: sticky
لضمان انتشار تأثير التمويه.
إذا كنت تريد استخدام حلّ جاهز، انتقِل إلى مستودع GitHub الخاص بـ "عيّنات عناصر واجهة المستخدم" واحصل على ملف Parallax helper JS. يمكنك الاطّلاع على عرض تجريبي مباشر لشريط التمرير المتغير المدى في مستودع GitHub.
أدوات حلّ المشاكل
في البداية، لنلقِ نظرة على طريقتَين شائعتَين لتحقيق أثر التمويه العميق ، وعلى وجه الخصوص، سبب عدم ملاءمتها لأغراضنا.
سيئ: استخدام أحداث التمرير
الشرط الأساسي لتأثير التمويه هو أن يكون مرتبطًا بميزة الانتقال إلى الأسفل أو للأعلى في الصفحة، أي أنّه يجب تعديل موضع العنصر الذي يعرض تأثير التمويه عند إجراء أي تغيير على موضع الانتقال إلى الأسفل أو للأعلى في الصفحة. على الرغم من أنّ ذلك يبدو بسيطًا، إلا أنّ إحدى الآليات المهمة في المتصفّحات الحديثة هي قدرتها على العمل بشكل غير متزامن. وهذا ينطبق، في حالتنا الخاصة، على تمرير الأحداث. في معظم المتصفّحات، يتم عرض أحداث الانتقال للأسفل أو للأعلى باستخدام "أقصى جهد" ولا يمكن ضمان عرضها في كل لقطة من المتحرّك لانتقال للأسفل أو للأعلى.
توضّح لنا هذه المعلومة المهمة سبب ضرورة تجنُّب استخدام حلّ مستند إلى JavaScript ينقل العناصر استنادًا إلى أحداث الانتقال إلى الأسفل أو للأعلى: لا تضمن JavaScript أن يظلّ تأثير التمويه المتغير متوافقًا مع موضع الانتقال إلى الأسفل أو للأعلى في الصفحة. في الإصدارات القديمة من Mobile Safari، كانت أحداث التمرير تُرسَل في الواقع في نهاية عملية التمرير، ما جعل من المستحيل تطبيق أثر التمرير القائم على JavaScript. تُرسِل الإصدارات الأحدث أحداث التمرير أثناء عرض الصور المتحركة، ولكن على أساس "أحسن ما يمكن"، تمامًا مثل Chrome. إذا كانت السلسلة المُهمّة مشغولة بأي عمل آخر، لن يتم إرسال أحداث التمرير على الفور، ما يعني فقدان تأثير التمويه.
خطأ: جارٍ تعديل background-position
هناك موقف آخر نريد تجنبه وهو الرسم على كل إطار. تحاول العديد من الحلول
تغيير background-position
لتوفير مظهر التماثل البصري، ما يؤدي بدوره إلى إعادة رسم المتصفح للأجزاء المتأثرة من الصفحة عند التمرير، وقد يؤدي ذلك
إلى تكاليف باهظة تؤدي إلى إيقاف عرض الصور المتحركة بشكل كبير.
إذا أردنا تحقيق تأثير التماثل البصري، نحتاج إلى عنصر يمكن تطبيقه كعنصر متسارع (يعني ذلك اليوم الالتزام بعمليات التحويل والشفافية)، ولا يعتمد على أحداث الانتقال.
CSS في 3D
أجرى كلّ من سكوت كليمون وكيث كلارك عملاً هامًا في مجال استخدام CSS 3D لتحقيق الحركة التماثلية، والتقنية التي يستخدمانها هي على النحو التالي:
- اضبط عنصرًا يحتوي على عنصر آخر للانتقال إلى الأعلى أو الأسفل باستخدام
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>
تعديل المقياس للمنظور
سيؤدي دفع العنصر الثانوي للخلف إلى تصغيره بما يتناسب مع قيمة perspective. يمكنك حساب مقدار الزيادة في المحتوى باستخدام هذه المعادلة: (المنظور - المسافة) / المنظور. ولأنّنا على الأرجح نرغب في أن يظهر العنصر المتباين مع اختلاف حجمه، ولكنّه يظهر بالحجم الذي كتبناه، لذا يجب توسيعه بهذه الطريقة، بدلاً من تركه كما هو.
في حالة الرمز أعلاه، تكون المنظور 1 بكسل، وتكون المسافة Z لملف
parallax-child
هي -2 بكسل. وهذا يعني أنّه يجب
تكبير حجم العنصر بمقدار 3 أضعاف، وهي القيمة التي تم إدخالها في الرمز:
scale(3)
.
بالنسبة إلى أي محتوى لم يتم تطبيق قيمة translateZ
عليه، يمكنك
استبدال القيمة صفرًا. وهذا يعني أنّ المقياس هو (perspective - 0) /
perspective، ما يؤدي إلى الحصول على قيمة 1، ما يعني أنّه لم يتم تعديله
للتكبير أو التصغير. هذا مفيد جدًا.
آلية عمل هذا النهج
من المهم توضيح سبب نجاح هذه الطريقة، لأنّنا سنستخدم هذه
المعرفة قريبًا. التمرير هو تحويل فعّال، ولهذا السبب يمكن تسريعه، إذ ينطوي غالبًا على تغيير الطبقات باستخدام وحدة معالجة الرسومات. في التمرير
العادي، الذي لا يتضمن أيّ شعور بالمنظور، يتم التمرير
بنسبة 1:1 عند مقارنة عنصر التمرير بعناصره الفرعية.
إذا تحرّكت للأسفل بعنصر 300px
، يتم نقل العناصر الفرعية للأعلى
بالمقدار نفسه: 300px
.
ومع ذلك، يؤدي تطبيق قيمة منظور على عنصر التمرير إلى تعطيل
هذه العملية، إذ يغيّر المصفوفات التي تستند إليها عملية التحويل.
والآن، يمكن أن يؤدي التمرير الذي يبلغ حجمه 300 بكسل إلى تحريك العناصر الثانوية بمقدار 150 بكسل فقط، استنادًا إلى قيمتَي
perspective
وtranslateZ
التي اخترتها. إذا كانت قيمة
translateZ
للعنصر هي 0، سيتم التمرير فيها بنسبة 1:1 (كما كان من قبل)، ولكن سيتم التمرير في عنصر تابع
تم دفعه في محور Z بعيدًا عن نقطة الأصل في المنظور بمعدل مختلف. النتيجة النهائية: تأثير التمويه. ومن المهم جدًا أن يتم التعامل مع ذلك
كجزء من آلية الانتقال الداخلي للمتصفّح تلقائيًا، ما يعني أنّه ليس هناك
حاجة إلى الاستماع إلى أحداث scroll
أو تغيير background-position
.
مشكلة في متصفّح 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);
}
يعيد هذا الإجراء تأثير التمويه في Safari للأجهزة الجوّالة، ما يشكّل خبرًا رائعًا من جميع النواحي.
تحذيرات بشأن المواضع الثابتة
مع ذلك، هناك فرق، لأنّ "position: sticky
" يغيّر
آليات الاختلاف. يحاول وضع العنصر في موضع ثابت تثبيت العنصر في
الحاوية التي يتم التمرير فيها، في حين لا يفعل ذلك الإصدار غير الثابت. وهذا يعني أنّ تأثير التماثل المرئي الذي يتضمّن عناصر ثابتة يكون عكس تأثير التماثل المرئي الذي لا يتضمّن عناصر ثابتة:
- مع
position: sticky
، كلما كان العنصر أقرب إلى z=0، كان التحرك أبطأ. - بدون استخدام
position: sticky
، كلما اقترب العنصر من z=0 زاد مقدار تحريكه.
إذا كان كلّ ذلك يبدو مجردًا، يمكنك الاطّلاع على هذا العرض التوضيحي الذي أعدّه "روبرت فلاك"، والذي يشرح كيفية تغيُّر سلوك العناصر مع وضعها في موضع ثابت أو بدونه. لمعرفة الفرق، تحتاج إلى Chrome Canary (الإصدار 56 في وقت كتابة هذه المقالة) أو Safari.
عرض توضيحي من إعداد "روبرت فلاك" يعرض كيفية تأثير
position: sticky
في التمرير بزاوية متناوبة.
أخطاء متنوعة وأساليب بديلة
ومع ذلك، لا تزال هناك بعض المشاكل التي يجب حلّها:
- التوافق مع العناصر الثابتة غير متّسق. لا يزال يتم تنفيذ الميزة في Chrome، ولا يتوفّر الإصدار المتوافق مع Edge على الإطلاق، ويواجه Firefox أخطاء في الرسم عند دمج العناصر الثابتة مع عمليات التحويل المنظور. في مثل
هذه الحالات، من المفيد إضافة رمز صغير لإضافة
position: sticky
(الإصدار الذي يحتوي على البادئة-webkit-
) فقط عند الحاجة، وهو مخصّص لمتصفّح Safari على الأجهزة الجوّالة فقط. - لا يعمل هذا التأثير "ببساطة" في Edge. يحاول Edge التعامل مع الانتقال للأعلى أو للأسفل على مستوى نظام التشغيل، وهو أمر جيد بشكل عام، ولكن في هذه الحالة يمنع ذلك التطبيق من رصد تغييرات المنظور أثناء الانتقال للأعلى أو للأسفل. لحلّ هذه المشكلة، يمكنك إضافة عنصر في موضع ثابت، لأنّ هذا الإجراء يبدو أنّه ينقل Edge إلى طريقة لفّ الصفحة لا تعتمد على نظام التشغيل، ويضمن مراعاة التغييرات في المنظور.
- "أصبح محتوى الصفحة ضخمًا جدًا" تراعي العديد من المتصفحات المقياس عند تحديد حجم محتوى الصفحة، ولكن للأسف لا يراعي Chrome وSafari المنظور. وبالتالي،
إذا تم تطبيق مقياس 3x على عنصر، قد
تظهر لك أشرطة التمرير وما شابه ذلك، حتى إذا كان العنصر بمقياس 1x بعد تطبيق
perspective
. يمكنك حلّ هذه المشكلة من خلال تصغير العناصر من الزاوية اليمنى السفلى (باستخدامtransform-origin: bottom right
)، ما يؤدي إلى تكبير العناصر الكبيرة جدًا لتظهر في "المنطقة السلبية" (عادةً في أعلى يمين) المنطقة التي يمكن التمرير فيها. ولا تسمح لك مناطق التمرير أبدًا بالاطّلاع على المحتوى في المنطقة السلبية أو الانتقال إليه.
الخاتمة
يُعدّ تأثير التمويه من التأثيرات الممتعة عند استخدامه بعناية. وكما ترون، من الممكن تنفيذه بطريقة أداء جيدة وارتباط بالتمرير وعبر المتصفحات. بما أنّ هذا الإجراء يتطلّب بعض الحسابات الرياضية وقليلًا من الخطوات الأساسية للحصول على التأثير المطلوب، أعددنا مكتبة مساعدة صغيرة ومعاينة يمكنك العثور عليهما في مستودع GitHub الخاص بـ "عيّنات عناصر واجهة المستخدم".
يُرجى تجربة هذه الميزة وإعلامنا برأيك.