بدء استخدام الصور المتحركة المستندة إلى التمرير في CSS

1. قبل البدء

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

يتضمّن الإصدار 115 من Chrome مجموعة من فئات JavaScript وخصائص CSS التي تتيح لك إنشاء رسوم متحركة مستندة إلى التمرير بشكل تصريحي. تعمل واجهات برمجة التطبيقات الجديدة هذه بالتزامن مع واجهات برمجة التطبيقات الحالية Web Animations وCSS Animations.

يعلّمك هذا الدرس التطبيقي حول الترميز كيفية إنشاء صور متحركة مستندة إلى التمرير باستخدام CSS. من خلال إكمال هذا الدرس العملي، ستتعرّف على العديد من خصائص CSS الجديدة التي تقدّمها هذه الميزة الرائعة، مثل scroll-timeline وview-timeline وanimation-timeline وanimation-range.

أهداف الدورة التعليمية

  • كيفية إنشاء تأثير خلفية متوازية باستخدام مخطط زمني للتمرير في CSS
  • كيفية إنشاء شريط تقدّم باستخدام "المخطط الزمني للتمرير" في CSS
  • كيفية إنشاء تأثير كشف الصورة باستخدام "المخطط الزمني للعرض" في CSS
  • كيفية استهداف أنواع مختلفة من نطاقات "المخطط الزمني للعرض" في CSS

المتطلبات

إحدى مجموعات الأجهزة التالية:

  • إصدار حديث من Chrome (الإصدار 115 أو إصدار أحدث) على ChromeOS أو macOS أو Windows مع ضبط علامة "ميزات تجريبية لمنصة الويب" على "مفعّلة"
  • فهم أساسي للغة HTML
  • فهم أساسي للغة CSS، لا سيما الرسوم المتحركة في CSS

2. طريقة الإعداد

يتوفّر كل ما تحتاج إليه لهذا المشروع في مستودع GitHub. للبدء، استنسِخ الرمز وافتحه في بيئة التطوير المفضّلة لديك.

  1. افتح علامة تبويب متصفّح جديدة وانتقِل إلى https://github.com/googlechromelabs/io23-scroll-driven-animations-codelab.
  2. أنشئ نسخة طبق الأصل من المستودع.
  3. افتح الرمز في بيئة التطوير المتكاملة (IDE) المفضّلة لديك.
  4. نفِّذ الأمر npm install لتثبيت التبعيات.
  5. نفِّذ الأمر npm start وانتقِل إلى http://localhost:3000/.
  6. بدلاً من ذلك، إذا لم يكن لديك npm مثبَّتًا، افتح الملف src/index.html في Chrome.

3- مزيد من المعلومات حول المخططات الزمنية للصور المتحركة

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

باستخدام الصور المتحركة المستندة إلى التمرير، يمكنك الوصول إلى نوعَين جديدَين من المخططات الزمنية:

  • مخطط زمني لمستوى التقدّم في التمرير
  • عرض المخطط الزمني لمستوى التقدّم

في CSS، يمكن ربط هذه المخططات الزمنية بحركة باستخدام السمة animation-timeline. اطّلِع على معنى هذه المخططات الزمنية الجديدة وأوجه الاختلاف بينها.

مخطط زمني لمستوى التقدّم في التمرير

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

يمثّل موضع التمرير للأعلى أو للأسفل في البداية نسبة% 0 من مستوى التقدّم، ويمثّل موضع التمرير للأعلى أو للأسفل في النهاية نسبة% 100 من مستوى التقدّم. في التمثيل المرئي التالي، لاحظ أنّ عدّاد التقدم يرتفع من% 0 إلى% 100 أثناء التمرير للأسفل في محتوى قابل للتمرير.

عرض المخطط الزمني لمستوى التقدّم

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

يبدأ "المخطّط الزمني لتقدّم العرض" من اللحظة التي يبدأ فيها عنصر ما في التقاطع مع أداة التمرير وينتهي عندما يتوقف العنصر عن التقاطع مع أداة التمرير. في التمثيل المرئي التالي، لاحظ أنّ عدّ النسبة المئوية يبدأ من 0% عندما يدخل العنصر حاوية التمرير ويصل إلى 100% عند خروجه من حاوية التمرير.

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

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

نطاقات "المخطّط الزمني للعرض" المتاحة التي يمكنك استهدافها هي cover وcontain وentry وexit وentry-crossing وexit-crossing. سيتم توضيح هذه النطاقات لاحقًا في هذا الدرس التطبيقي حول الترميز، ولكن إذا كنت لا تستطيع الانتظار، يمكنك استخدام الأداة المتوفّرة على https://goo.gle/view-timeline-range-tool لمعرفة ما يمثّله كل نطاق.

4. إنشاء تأثير خلفية اختلاف المنظر

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

لتنفيذ ذلك، عليك اتّخاذ خطوتَين:

  1. أنشئ رسمًا متحركًا ينقل موضع صورة الخلفية.
  2. ربط الصورة المتحركة بمستوى تقدّم التمرير في المستند

إنشاء الصورة المتحركة

  1. لإنشاء الصورة المتحركة، استخدِم مجموعة عادية من إطارات المفاتيح. فيها، غيِّر موضع الخلفية من% 0 عموديًا إلى %100:

src/styles.css

@keyframes move-background {
  from {
    background-position: 50% 0%;
  }
  to {
    background-position: 50% 100%;
  }
}
  1. الآن، اربط هذه الإطارات الرئيسية بعنصر النص الأساسي:

src/styles.css

body {
  animation: 1s linear move-background;
}

باستخدام هذا الرمز، تتم إضافة الرسم المتحرّك move-background إلى عنصر النص الأساسي. تم ضبط السمة animation-duration على ثانية واحدة، ويتم استخدام linear لتغيير السرعة.

أسهل طريقة لإنشاء مخطط زمني لـ "شريط تقدّم التمرير" هي استخدام الدالة scroll(). يؤدي ذلك إلى إنشاء "المخطط الزمني لتقدّم التمرير" مجهول الهوية الذي يمكنك ضبطه كقيمة للسمة animation-timeline.

تقبل الدالة scroll() الوسيطتَين <scroller> و<axis>.

القيم المقبولة للوسيطة <scroller> هي ما يلي:

  • nearest: تستخدم حاوية التمرير الأقرب إلى العنصر (الإعداد التلقائي).
  • root: تستخدم إطار عرض المستند كحاوية التمرير.
  • self: تستخدِم العنصر نفسه كحاوية التمرير.

القيم المقبولة للوسيطة <axis> هي ما يلي:

  • block: تستخدم مقياس التقدّم على طول محور الكتلة لحاوية التمرير (الإعداد التلقائي).
  • inline: تستخدم مقياس التقدّم على طول المحور المضمّن لحاوية التمرير.
  • y: تستخدِم مقياس التقدّم على طول المحور y لحاوية التمرير.
  • x: تستخدم مقياس التقدّم على طول المحور x لحاوية التمرير.

لربط الصورة المتحركة بمحتوى التمرير الرئيسي على محور الكتلة، يجب تمرير القيمتَين root وblock إلى scroll(). وبالتالي، تكون القيمة scroll(root block).

  • اضبط scroll(root block) كقيمة للسمة animation-timeline في النص الأساسي.
  • بالإضافة إلى ذلك، بما أنّه لا يمكن التعبير عن animation-duration بالثواني، اضبط المدة على auto. إذا لم تحدّد animation-duration، سيتم ضبط القيمة التلقائية على auto.

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll(root block);
}

بما أنّ أداة التمرير الرئيسية هي أيضًا أداة التمرير الأقرب للعنصر الأساسي، يمكنك أيضًا استخدام القيمة nearest:

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll(nearest block);
}

بما أنّ nearest وblock هما القيمتان التلقائيتان، يمكنك حتى اختيار إغفالهما. في هذه الحالة، يمكن تبسيط الرمز إلى ما يلي:

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll();
}

التحقّق من التغييرات

إذا سارت الأمور على ما يرام، من المفترض أن يظهر لك ما يلي:

إذا لم يكن كذلك، اطّلِع على فرع solution-step-1 من الرمز.

5- إنشاء شريط تقدّم لمعرض الصور

تتضمّن الصفحة لوحة عرض دوّارة أفقية تحتاج إلى شريط تقدّم للإشارة إلى الصورة التي يتم عرضها حاليًا.

يبدو الترميز الخاص بلوحة العرض الدوّارة على النحو التالي:

src/index.html

<div class="gallery">
  <div class="gallery__scrollcontainer" style="--num-images: 3;">
    <div class="gallery__progress"></div>
    <div class="gallery__entry">
      ...
    </div>
    <div class="gallery__entry">
      ...
    </div>
    <div class="gallery__entry">
      ...
    </div>
  </div>
</div>

تمّت إضافة إطارات المفاتيح لشريط التقدّم، وهي تبدو على النحو التالي:

src/styles.css

@keyframes adjust-progress {
  from {
    transform: scaleX(calc(1 / var(--num-images)));
  }
  to {
    transform: scaleX(1);
  }
}

يجب إرفاق هذه الصورة المتحركة بـ .عنصر gallery__progress يتضمّن مخططًا زمنيًا لتقدّم التمرير كما هو موضّح في الخطوة السابقة، يمكنك تحقيق ذلك من خلال إنشاء "المخطط الزمني لتقدّم التمرير" المجهول باستخدام الدالة scroll():

src/styles.css

.gallery__progress {
  animation: linear adjust-progress;
  animation-duration: auto;
  animation-timeline: scroll(nearest inline);
}

على الرغم من أنّ هذا الجزء من الرمز قد يبدو وكأنّه سيعمل، إلا أنّه لا يعمل بسبب طريقة عمل عمليات البحث التلقائية عن حاويات التمرير باستخدام nearest. عند البحث عن أقرب محتوى قابل للتمرير، لن يأخذ العنصر في الاعتبار سوى العناصر التي يمكن أن تؤثر في موضعه. بما أنّ .gallery__progress يتم وضعه بشكل ثابت، فإنّ العنصر الرئيسي الأول الذي سيحدّد موضعه هو العنصر .gallery لأنّه تم تطبيق position: relative عليه. هذا يعني أنّ العنصر .gallery__scrollcontainer، وهو محتوى قابل للتمرير الذي يجب استهدافه، لا يتم أخذه في الاعتبار على الإطلاق أثناء عملية البحث التلقائي هذه.

لحلّ هذه المشكلة، أنشئ "المخطط الزمني لتقدّم التمرير" باسم معيّن في العنصر .gallery__scrollcontainer واربط .gallery__progress به باستخدام هذا الاسم.

لإنشاء مخطط زمني مسمّى لتقدّم التمرير على أحد العناصر، اضبط السمة scroll-timeline-name في CSS على حاوية التمرير على قيمة من اختيارك. يجب أن تبدأ القيمة بـ --.

بما أنّ معرض الصور يتيح التمرير أفقيًا، عليك أيضًا ضبط السمة scroll-timeline-axis. القيم المسموح بها هي نفسها القيم المسموح بها في الوسيطة <axis> للدالة scroll().

أخيرًا، لربط الحركة بالجدول الزمني لتقدّم التمرير، اضبط السمة animation-timeline في العنصر الذي يجب تحريكه على القيمة نفسها المستخدَمة في المعرّف scroll-timeline-name.

  • غيِّر ملف styles.css ليشمل ما يلي:

src/styles.css

.gallery__scrollcontainer {
  /* Create the gallery-is-scrolling timeline */
  scroll-timeline-name: --gallery-is-scrolling;
  scroll-timeline-axis: inline;
}

.gallery__progress {
  animation: linear adjust-progress;
  animation-duration: auto;
  /* Set gallery-is-scrolling as the timeline */
  animation-timeline: --gallery-is-scrolling;
}

التحقّق من التغييرات

إذا سارت الأمور على ما يرام، من المفترض أن يظهر لك ما يلي:

إذا لم يكن كذلك، اطّلِع على فرع solution-step-2 من الرمز.

6. تحريك صور المعرض عند دخولها إلى إطار العرض والخروج منه

إعداد "مخطط زمني لتقدّم المشاهدة" مجهول الهوية

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

لإنشاء "مخطط زمني لتقدّم المشاهدة"، يمكنك استخدام الدالة view(). الوسيطتان المقبولتان هما <axis> و<view-timeline-inset>.

  • يكون <axis> هو نفسه من المخطط الزمني لتقدّم التمرير ويحدّد المحور الذي يجب تتبّعه.
  • باستخدام <view-timeline-inset>، يمكنك تحديد إزاحة (موجبة أو سالبة) لتعديل الحدود عندما يُعتبر أحد العناصر معروضًا أو غير معروض.
  • تكون إطارات المفاتيح في مكانها، لذا ما عليك سوى ربطها. لإجراء ذلك، أنشئ "مخططًا زمنيًا لعرض التقدم" على كل عنصر .gallery__entry.

src/styles.css

@keyframes animate-in {
  from {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
  to {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
}

.gallery__entry {
  animation: linear animate-in;
  animation-duration: auto;
  animation-timeline: view(inline);
}

تقييد نطاق "المخطط الزمني لتقدّم المشاهدة"

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

ويرجع ذلك إلى أنّ النطاق التلقائي للمخطط الزمني لتقدّم المشاهدة هو النطاق الكامل. ويُعرف ذلك باسم نطاق cover.

  1. لاستهداف النطاق entry فقط من العنصر، استخدِم السمة animation-range في CSS للحدّ من وقت تشغيل الصورة المتحركة.

src/styles.css

.gallery__entry {
  animation: linear fade-in;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry 0% entry 100%;
}

يتم تشغيل الحركة الآن من entry 0% (الموضوع على وشك الدخول إلى أداة التمرير) إلى entry 100% (الموضوع دخل بالكامل إلى أداة التمرير).

في ما يلي نطاقات "عرض المخطط الزمني" المحتملة:

  • cover: يمثّل النطاق الكامل للمخطط الزمني لمستوى تقدّم المشاهدة.
  • entry: يمثّل النطاق الذي يدخل خلاله المربّع الرئيسي إلى نطاق ظهور مستوى تقدّم العرض.
  • exit: يمثّل النطاق الذي يخرج خلاله المربّع الرئيسي من نطاق ظهور تقدّم العرض.
  • entry-crossing: يمثّل النطاق الذي يعبر خلاله المربع الرئيسي حافة الحدود النهائية.
  • exit-crossing: يمثّل النطاق الذي يعبر خلاله المربع الرئيسي حافة حدود البداية.
  • contain: يمثّل النطاق الذي يكون فيه المربّع الرئيسي إما مضمّنًا بالكامل في نطاق ظهور مستوى تقدّم العرض ضمن منفذ التمرير أو يغطّيه بالكامل. يعتمد ذلك على ما إذا كان العنصر أطول أو أقصر من محتوى قابل للتمرير.

استخدِم الأداة المتوفّرة على https://goo.gle/view-timeline-range-tool لمعرفة ما يمثّله كل نطاق وكيف تؤثّر النسب المئوية في موضعَي البدء والانتهاء.

  1. بما أنّ نطاقَي البدء والانتهاء متطابقان هنا ويتم استخدام الإزاحات التلقائية، يمكنك تبسيط animation-range إلى اسم نطاق رسوم متحركة واحد:

src/styles.css

.gallery__entry {
  animation: linear animate-in;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry;
}
  • لتتلاشى الصور أثناء خروجها من محتوى قابل للتمرير، يمكنك اتّباع الخطوات نفسها التي اتّبعتها في الحركة animate-in، ولكن استخدِم نطاقًا مختلفًا.

src/styles.css

@keyframes animate-out {
  from {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
  to {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
}

.gallery__entry {
  animation: linear animate-in, linear animate-out;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry, exit;
}

سيتم تطبيق الإطارات الرئيسية animate-in على النطاق entry، والإطارات الرئيسية animate-out على النطاق exit.

التحقّق من التغييرات

إذا سارت الأمور على ما يرام، من المفترض أن يظهر لك ما يلي:

إذا لم يكن كذلك، اطّلِع على فرع solution-step-3 من الرمز.

7. تحريك صور المعرض عند دخولها إلى منفذ التمرير وخروجها منه، باستخدام مجموعة واحدة من إطارات المفاتيح

حالة مجموعة واحدة من إطارات المفاتيح

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

يبدو شكل الإطارات الرئيسية على النحو التالي:

@keyframes keyframes-name {
  range-name range-offset {
    ...
  }
  range-name range-offset {
    ...
  }
}
  1. اجمع بين الإطارات الرئيسية الخاصة بالتلاشي والظهور على النحو التالي:

src/styles.css

@keyframes animate-in-and-out {
  entry 0% {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
  entry 90% {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }

  exit 10% {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
  exit 100% {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
}
  1. عندما تتوفّر معلومات النطاق في إطارات المفاتيح، لن تحتاج إلى تحديد animation-range بشكل منفصل بعد الآن. أرفِق الإطارات الرئيسية كسمة animation.

src/styles.css

.gallery__entry {
  animation: linear animate-in-and-out both;
  animation-duration: auto;
  animation-timeline: view(inline);
}

التحقّق من التغييرات

إذا سارت الأمور على ما يرام، من المفترض أن تحصل على النتيجة نفسها التي حصلت عليها في الخطوة السابقة. إذا لم يكن كذلك، اطّلِع على فرع solution-step-4 من الرمز.

8. تهانينا!

لقد أكملت هذا الدرس التطبيقي حول الترميز وأصبحت تعرف الآن كيفية إنشاء "المخططات الزمنية لتقدّم التمرير" و"المخططات الزمنية لتقدّم العرض" في CSS.

مزيد من المعلومات

المصادر: