الوحدة 4 · الحركة والمعمارية
الحركات المرتبطة بالتمرير
17 دقراءة 4 دبيئة تجريب الكود
الدرس في لمحة
قُد حركات CSS بموضع التمرير باستخدام خطوط زمن scroll() وview()، واختر نطاقات الحركة، وأطلقها كتحسين تدريجي ريثما يكتمل دعم Firefox.
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
جدول Waypoint طويل: يومان، وأربعة مسارات، وستون جلسة. يريد الفريق لمستين صغيرتين: شريط تقدّم رفيع تحت الرأس يبيّن إلى أين وصلت في الجدول، وبطاقات متحدّثين تنساب إلى مجال الرؤية حين تصل إليها بالتمرير. الطريقة القديمة كانت مستمعًا لحدث scroll يقرأ scrollY، ويجري بعض الحسابات، ويكتب الأنماط، ستين مرة في الثانية، على الـ main thread، متنافسًا مع كل ما عداه.
الحركات المرتبطة بالتمرير (scroll-driven animations) تترك هذا العمل لـ CSS. تكتب حركة @keyframes عادية وتستبدل ساعتها: بدل الزمن، يأتي تقدّمها من التمرير.
نوعان من خطوط الزمن
خط زمن تقدّم التمرير، scroll()، يمضي من 0% إلى 100% بينما تُمرَّر حاوية التمرير من الأعلى إلى الأسفل. scroll(root) هي المستند؛ وscroll(nearest)، وهي الافتراضية، أقرب سلف قابل للتمرير.
خط زمن تقدّم الظهور، view()، يتتبّع عنصرًا واحدًا وهو يعبر المنطقة المرئية من حاوية التمرير (الـ scrollport): من لحظة ظهور حافته من الأسفل إلى لحظة مغادرته من الأعلى.
@keyframes grow {
from { scale: 0 1; }
to { scale: 1 1; }
}
.progress {
position: fixed;
inset: 0 0 auto;
height: 3px;
background: var(--track-design);
transform-origin: 0 50%;
animation: grow linear both;
animation-timeline: scroll(root);
}في أعلى الصفحة يكون حجم الشريط صفرًا؛ وفي أسفلها يمتدّ بالعرض الكامل؛ وفي أيّ موضع بينهما يكون متناسبًا تمامًا. وlinear مهمة هنا: مع ease الافتراضية سيندفع الشريط في المنتصف ويزحف عند الطرفين. وboth تُبقي أول keyframe وآخرها مطبّقين خارج النطاق.
الظهور التدريجي مع النطاقات
لبطاقات المتحدّثين، خط زمن الظهور هو الساعة المناسبة، وanimation-range تختار أيّ جزء من مسار البطاقة يشغّل الحركة:
@keyframes reveal {
from { opacity: 0; translate: 0 24px; }
to { opacity: 1; translate: 0 0; }
}
.speaker {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% cover 30%;
}النطاقات المسمّاة تصف مسار العنصر: entry وهو يعبر الحافة السفلية، وcontain وهو داخل المنطقة بالكامل (أو يغطّيها بالكامل إن كان أطول منها)، وexit وهو يعبر الحافة العلوية، وcover للرحلة كلها من أول بكسل يدخل إلى آخر بكسل يخرج. وentry 0% cover 30% تعني «ابدأ حين تبدأ البطاقة بالدخول، وانتهِ حين تقطع 30% من رحلتها الكاملة»، فتكون كل بطاقة ظاهرة بالكامل قبل وقت كافٍ من وصول المستخدم إليها.
أطلقها كتحسين
هذه صورة الدعم في أكتوبر 2026: الحركات المرتبطة بالتمرير موجودة في Chromium منذ الإصدار 115 (2023) وفي Safari منذ الإصدار 26 (سبتمبر 2025). ولدى Firefox تطبيق خلف إعداد تجريبي (flag)، وهي من مجالات التركيز في Interop 2026، لكن نسخة Firefox المستقرة لا تدعمها بعد، لذا فالميزة ليست ضمن Baseline.
وهذا يحدّد طريقة كتابتك لها. الـ keyframes الخاصة بالظهور تبدأ من opacity: 0، وفي متصفّح يفهم animation-timeline لا مشكلة في ذلك. أما في متصفّح لا يفهمها، فإن animation: reveal linear both بلا خط زمن حركة زمنية بمدة صفر تنتهي عند آخر keyframe، فتظل البطاقات ظاهرة. لكن لا تعتمد على هذه الدقيقة الخفيّة؛ اجعل النيّة صريحة:
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
.speaker {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% cover 30%;
}
}
}المتصفّحات غير الداعمة ومستخدمو تقليل الحركة يحصلون على الشبكة العادية الظاهرة بالكامل. والمتصفّحات الداعمة تحصل على الظهور التدريجي.
وتستطيع أيضًا تسمية خطوط الزمن (view-timeline-name: --speaker) ليتبعها عنصر آخر، واستخدام timeline-scope لجعل الاسم مرئيًا في مستوى أعلى من الشجرة. الجأ إلى ذلك حين يجب أن يقود موضع تمرير عنصر ما عنصرًا آخر؛ أما للتأثيرات المكتفية بذاتها، فتكفي scroll() وview() بلا أسماء.
دورك الآن
في التمرين أضف شريط تقدّم القراءة والظهور التدريجي للمتحدّثين، بالترتيب الصحيح، ومحميّين بـ @supports. تقرأ الفحوصات خطوط الزمن والنطاقات المحسوبة. وبعدها ستتراجع خطوة عن التأثيرات المنفردة لتنظّم ملف الأنماط كله بحيث يستطيع فريق أن يعمل عليه.
أهم النقاط
animation-timeline: scroll()تربط تقدّم الحركة بمقدار ما مُرِّرت حاوية التمرير؛ وview()تربطه بمرور عنصر عبر المنطقة المرئية.animation-rangeتختار أيّ جزء من خط الزمن يشغّل الحركة، مثلentry 0% cover 30%لظهور تدريجي ينتهي بعد قليل من ظهور العنصر.- الاختصار
animationيعيد ضبطanimation-timeline، لذا أعلن خط الزمن بعد الاختصار. - الحركات المرتبطة بالتمرير تعمل في Chromium وSafari 26 لكن ليس في نسخة Firefox المستقرة بعد، لذا غلّفها في
@supportsوتأكّد من أن المحتوى ظاهر بالكامل دونها.