بغض النظر عمّا إذا كانت تعجبك، فإنّ تأثير التمويه سيبقى رائجًا. وعند استخدامه بحكمة، يمكنه إضافة عمق ودقة إلى تطبيق الويب. ومع ذلك، تكمن المشكلة في أنّ تنفيذ التأثير المتوازي بطريقة فعّالة قد يكون أمرًا صعبًا. في هذه المقالة، سنطرح حلًا يحقق أداءً جيدًا ويعمل على جميع المتصفّحات.
الملخّص
- لا تستخدِم أحداث الانتقال للأعلى أو للأسفل أو
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 الخاص بـ "عيّنات عناصر واجهة المستخدم".
يُرجى تجربة هذه الميزة وإعلامنا برأيك.