Kodokon kodokon.com

التفاعلات الحديثة: scroll-snap وانتقالات العرض

نسّق التمرير المغناطيسي، وانتقالات الحالة التي يحرّكها المتصفح، والاحترام الصارم لـ prefers-reduced-motion.

11 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

يشترك في المحاذاة عند التمرير طرفان: تحدّد الحاوية منفذ محاذاة (snapport) (مساحتها المرئية، مُقلَّصة بـ scroll-padding) واستراتيجيةً عبر scroll-snap-type - proximity لا يحاذي إلا قرب نقطة محاذاة، بينما mandatory يضمن أن تحطّ دائمًا على واحدة، حتى بعد تمرير برمجي. ويعلن الأبناء عن منطقة محاذاتهم بـ scroll-snap-align (قابلة للضبط عبر scroll-margin). دقيقة قلّ من يعرفها: أثناء تمرير سريع، قد يتخطى المتصفح عدة نقاط محاذاة بشكل مشروع؛ ويجبره scroll-snap-stop: always على التوقف عند كل نقطة، وهذا أساسي لدوّار على نمط القصص.

CSS
.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 1rem;
}
.carousel > article {
  flex: 0 0 80%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}
دوّار مغناطيسي: تتمركز كل شريحة بنفسها ولا يمكن لأي تمرير أن يتخطى واحدة.

تحرّك واجهة View Transitions API تغيّر حالة DOM دون أي مكتبة. يلتقط document.startViewTransition(callback) لقطة بصرية للحالة الحالية، ثم يشغّل ردّ ندائك (الذي يطفر DOM)، ثم يحرّك الانتقال بين الحالتين القديمة والجديدة. وفي الخفاء، يبني المتصفح شجرةً من العناصر الزائفة فوق الصفحة: يحتوي ::view-transition على عناصر ::view-transition-group، يحمل كل منها ::view-transition-old (اللقطة، وهي محتوى مستبدَل يشبه صورة) و::view-transition-new (التمثيل الحي). والافتراضي: تلاشٍ متقاطع. وتكون الصفحة غير تفاعلية أثناء الانتقال.

JAVASCRIPT
function applyUpdate(updateDom) {
  if (!document.startViewTransition) {
    updateDom();
    return;
  }
  document.startViewTransition(() => updateDom());
}
تحسين تدريجي: بلا دعم، يُطبَّق التحديث ببساطة دون حركة.

تأتي القوة الحقيقية من view-transition-name: يُستخرَج العنصر المسمّى إلى مجموعة انتقال خاصة به. فإن وُجد عنصر بنفس الاسم قبل الانتقال وبعده، يستوفي المتصفح تلقائيًا الموضع والحجم بين الاثنين - وهو تأثير التحوّل من صورة مصغّرة إلى عرضها بملء الشاشة، دون سطر واحد من JavaScript للحركة. ويجب أن يكون كل اسم فريدًا على الصفحة في أي لحظة، وإلا هُجِر الانتقال. وتُنسَّق العناصر الزائفة بحركات CSS عادية.

CSS
.hero-thumb { view-transition-name: hero; }

::view-transition-old(hero),
::view-transition-new(hero) {
  animation-duration: 0.4s;
  animation-timing-function: ease-in-out;
}
تُعترَض مجموعة hero المسمّاة وتُخصَّص مدة حركتها.

يبقى واجب إمكانية الوصول: يشير prefers-reduced-motion: reduce إلى مستخدم حسّاس للحركة (اضطرابات دهليزية، صداع نصفي). التقليل لا يعني الإزالة: يبقى تلاشي الشفافية مقبولًا؛ إنما الحركات الكبيرة والتكبيرات وتأثيرات المنظر (parallax) هي ما يجب تحييده. أنت تعرف مسبقًا التجاوز الدفاعي الشامل في CSS (مُدد شبه صفرية، تناولناه في الوحدة السابقة)؛ وتضيف انتقالات العرض جبهة ثانية: من جانب JavaScript، استعلم عن window.matchMedia("(prefers-reduced-motion: reduce)") قبل إطلاق انتقال أو تمرير متحرك.

JAVASCRIPT
const reduceMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (reduceMotion || !document.startViewTransition) {
  updateDom();
} else {
  document.startViewTransition(() => updateDom());
}
ضمانة JavaScript: يُطبَّق التحديث دون تحوّل عندما يطلب المستخدم حركة أقل.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. ماذا يفعل scroll-snap-stop: always على عنصر في دوّار؟
    • يعطّل قصور التمرير باللمس
    • يفرض توقفًا عند كل نقطة محاذاة، حتى أثناء تمرير سريع
    • يحجب التمرير على المحور العمودي
  2. ماذا يلتقط document.startViewTransition() قبل تشغيل ردّ ندائك؟
    • نسخة عميقة من DOM الحالي
    • لقطة بصرية للحالة القديمة، تُعاد في ::view-transition-old
    • الـ HTML المُسلسَل للصفحة بأكملها
  3. كيف تحترم prefers-reduced-motion قبل إطلاق انتقال عرض من JavaScript؟
    • لا يمكنك: هذا التفضيل قابل للقراءة في CSS فقط
    • اختبر window.matchMedia('(prefers-reduced-motion: reduce)').matches وطبّق التحديث دون startViewTransition عند تطابقه
    • مرّر الخيار { motion: 'reduce' } إلى startViewTransition