النمطين دول بيجمعوا اللي فات: sticky عشان المشهد يثبت، ومعاه driven أو triggered عشان يتحرك.
Horizontal Scroll: انزل لتحت، يمشي بالعرض
اعمل scroll لتحت… والكروت تمشي بالعرض
خلصنا ✓
متصفحك لسه مش بيدعم scroll-driven animations، فالديمو ثابت. جرّبه على Chrome أو Edge أو Safari حديث.
التركيبة ٣ طبقات:
.wrap ← طويل (مثلًا 400vh): ده اللي بيدّي مسافة scroll└── .stage ← sticky top:0 وارتفاعه قد الشاشة: بيثبت طول ما الـ wrap معدّي └── .track ← صف كروت عرضه أكبر من الشاشة: بيتحرك translateXhorizontal.css
.wrap { height: 400vh; view-timeline: --h block; }.stage { position: sticky; top: 0; height: 100vh; overflow: hidden; direction: ltr; /* في صفحة RTL الـ track بيتصف من اليمين، فلازم الـ stage نفسه LTR */}.track { display: flex; width: 400%; animation: slide linear both; animation-timeline: --h; animation-range: contain 0% contain 100%;}@keyframes slide { to { transform: translateX(-75%); } }view-timeline: --hبيدّي اسم للـ timeline بتاع الـ.wrap، والـ.track(ابنه) بيستخدمه.contain= الفترة اللي الـ wrap مالي الشاشة فيها، وهي نفس الفترة اللي الـ stage ثابت فيها. فالحركة بتبدأ أول ما يثبت وبتخلص قبل ما يفك.direction: ltrعلى الـ stage مش الـ track: في صفحة عربي، العنصر الأعرض من أبوه بيتصف من اليمين ويطلع لشمال، فالـ track هيبدأ وإنت شايف آخر كارت. وقعنا في دي في الديمو.-75%لأن عندنا ٤ كروت: نتحرك ٣ كروت من ٤ عشان آخر كارت يبقى قدامك.
Scrollytelling: المشهد ثابت والخطوات بتحكي
الخطوة ١
١. أول عمود هو اللي بيكبر
٢. دلوقتي التاني
٣. والتالت في الآخر
بتشوفه في المقالات التفاعلية: رسمة أو خريطة ثابتة، والكلام بيعدّي جنبها أو تحتها، وكل فقرة بتغيّر المشهد.
story.ts
const scene = document.querySelector<HTMLElement>(".scene")!;const io = new IntersectionObserver( (entries) => { for (const e of entries) { if (e.isIntersecting) scene.dataset.step = (e.target as HTMLElement).dataset.step; } }, // خط وهمي في نص الشاشة: الخطوة اللي تعدّيه تبقى الحالية { rootMargin: "-50% 0px -50% 0px" },);document.querySelectorAll(".step").forEach((s) => io.observe(s));story.css
.scene { position: sticky; top: 0; height: 60vh; }.step { min-height: 80vh; } /* مسافة كفاية بين الخطوات */.scene[data-step="2"] .bar-2 { height: 100%; background: var(--accent); }- الفقرة ٢ ← IntersectionObserver: عدّى الخط
- IntersectionObserver ← المشهد الثابت: غيّر الخطوة
خلّصت الفصل لما
🧠 اختبر نفسك
١) ليه الـ stage لازم يبقى sticky؟
عشان يفضل ثابت قدامك طول ما الـ wrap الطويل بيعدّي، والكروت هي اللي تتحرك.
٢) rootMargin -50% من فوق وتحت بيعمل إيه؟
بيخلّي الـ root خط رفيع في نص الشاشة، فالخطوة اللي تعدّي النص بس هي اللي تتحسب.