داستان‌سرایی با پیمایش

«داستان‌سرایی با پیمایش» تکنیک محبوبی است که برای ایجاد تجربه‌های وب جذاب و فراگیر استفاده می‌شود. این تکنیک شامل متحرک‌سازی عناصر در صفحه هنگام پیمایش کاربر است که به‌طور مؤثری داستانی را روایت می‌کند یا کاربر را درطول یک روایت راهنمایی می‌کند. با «پویانمایی‌های پیمایش‌محور CSS»، می‌توانید این جلوه‌ها را مستقیماً در CSS ایجاد کنید، بدون اینکه نیازی به تکیه بر جاوا اسکریپت باشد. پویانمایی‌ها با موقعیت پیمایش کنترل می‌شوند، نه با ساعت زمان‌محور، که این امر تضمین می‌کند همیشه با پیمایش کاربر همگام باشند.

نحوه پیاده‌سازی

برای ایجاد تجربه داستان‌سرایی پیمایشی، به دو مجموعه عنصر نیاز دارید: یکی برای ردیابی موقعیت پیمایش و دیگری برای پویانمایی شدن.

ابتدا، view-timeline نام‌گذاری‌شده‌ای را روی عناصری که می‌خواهید ردیابی کنید تعریف کنید. این‌ها به‌عنوان محرک پویانمایی‌های شما عمل خواهند کرد.

#tracked {
  section:nth-child(1){ view-timeline: --tl-1 block; }
  section:nth-child(2){ view-timeline: --tl-2 block; }
  section:nth-child(3){ view-timeline: --tl-3 block; }
  section:nth-child(4){ view-timeline: --tl-4 block; }
  section:nth-child(5){ view-timeline: --tl-5 block; }
}

سپس، پویانمایی‌ها را روی عناصری که می‌خواهید پویانمایی شوند اعمال کنید و آن‌ها را بااستفاده از ویژگی animation-timeline به خطوط زمانی‌ای که به‌تازگی ایجاد کرده‌اید پیوند دهید.

#animated {
  section {
    animation: animate-in auto linear both, animate-out auto linear forwards;
    animation-range: entry 25% cover 50%, exit 50% exit 75%;
  }

  section:nth-child(1){ animation-timeline: --tl-1; }
  section:nth-child(2){ animation-timeline: --tl-2; }
  section:nth-child(3){ animation-timeline: --tl-3; }
  section:nth-child(4){ animation-timeline: --tl-4; }
  section:nth-child(5){ animation-timeline: --tl-5; }
}

برای اینکه animation-timeline بتواند به خطوط زمان نام‌گذاری‌شده ارجاع دهد، باید در یک محدوده باشند. می‌توانید از ویژگی timeline-scope در یک عنصر والد مشترک استفاده کنید تا خطوط زمان را برای همه عناصری که به آن‌ها نیاز دارند دردسترس قرار دهید. عنصر :root اغلب انتخاب خوبی برای این کار است.

html {
  timeline-scope: --tl-1, --tl-2, --tl-3, --tl-4, --tl-5;
}

درنهایت، می‌توانید از ویژگی animation-range برای مشخص کردن محدوده دقیق خط زمان که پویانمایی باید در آن اجرا شود استفاده کنید. این کار به شما کنترل دقیقی بر زمان فعال شدن پویانمایی‌ها و نحوه پیشرفت آن‌ها می‌دهد.

#animated section {
  animation-range: entry 25% cover 50%, exit 50% exit 75%;
}

کد نمونه

html {
  timeline-scope: --tl-1, --tl-2, --tl-3, --tl-4, --tl-5;
}

#tracked {
  section:nth-child(1){ view-timeline: --tl-1 block; }
  section:nth-child(2){ view-timeline: --tl-2 block; }
  section:nth-child(3){ view-timeline: --tl-3 block; }
  section:nth-child(4){ view-timeline: --tl-4 block; }
  section:nth-child(5){ view-timeline: --tl-5 block; }
}

@keyframes animate-in {
  from { scale: 0.5; opacity: 0; transform: rotateY(-180deg); }
  to { transform: rotateY(0deg); }
}
@keyframes animate-out {
  to { translate: 100% 0; opacity: 0; }
}

#animated {
  section {
    animation: animate-in auto linear both, animate-out auto linear forwards;
    animation-range: entry 25% cover 50%, exit 50% exit 75%;
    backface-visibility: hidden;
  }

  section:nth-child(1){ animation-timeline: --tl-1; }
  section:nth-child(2){ animation-timeline: --tl-2; }
  section:nth-child(3){ animation-timeline: --tl-3; }
  section:nth-child(4){ animation-timeline: --tl-4; }
  section:nth-child(5){ animation-timeline: --tl-5; }
}

/* MANDATORY Copy-Paste Safety: Disable continuous storytelling motion for sensitive users */
@media (prefers-reduced-motion: reduce) {
  #animated section {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

روال‌های مطلوب

هنگام استفاده از پویانمایی‌های پیمایش‌محور، مهم است که چند روال مطلوب را دنبال کنید تا تجربه‌ای روان و دسترس‌پذیر را تضمین کنید:

  • باید تشخیص ویژگی را اضافه کنید: همه مرورگرها از پویانمایی‌های پیمایش‌محور پشتیبانی نمی‌کنند. از @supports ((animation-timeline: scroll()) and (animation-range: 0% 100%)) برای بررسی پشتیبانی و ارائه بازگشت به روش پیش‌فرض برای مرورگرهایی که از آن پشتیبانی نمی‌کنند استفاده کنید.
    • بررسی (animation-range: 0% 100%) باید در اینجا گنجانده شود تا مرورگرهایی که فقط پشتیبانی جزئی دارند فیلتر شوند.
    • از بسته scroll-timeline-polyfill برای راهبرد جایگزین استفاده نکنید زیرا ویژگی‌های آن کامل نیست و مشکلات شناخته‌شده زیادی دارد.
    • اگر پویانمایی فقط تزئینی درنظر گرفته می‌شود، «ارتقای پیش‌رو» را انتخاب کنید و گزینه جایگزین ارائه ندهید.
  • باید به اولویت‌های کاربر احترام بگذارید: برخی‌از کاربران ترجیح می‌دهند حرکت کمتری در وب داشته باشند. از پُرسمان رسانه prefers-reduced-motion برای غیرفعال کردن یا کاهش پویانمایی‌ها برای این کاربران استفاده کنید.
  • باید سعی کنید فقط دارایی‌های CSS کارا را پویانمایی کنید: برای داشتن روان‌ترین پویانمایی‌ها، فقط دارایی‌هایی را پویانمایی کنید که رشته ترکیب‌کننده مرورگر می‌تواند آن‌ها را مدیریت کند، مثل transform و opacity. پویانمایی کردن ویژگی‌های دیگر مثل width یا height می‌تواند به مشکلات عملکردی منجر شود.
  • باید از ترتیب صحیح اعلام استفاده کنید: هنگام استفاده از ویژگی خلاصه animation، animation-timeline و animation-range را بعداز آن اعلام کنید تا از بازنشانی خط زمان توسط خلاصه جلوگیری شود.

هنگام استفاده از ویژگی view-timeline برای ایجاد پویانمایی پیمایش‌محور:

  • باید از CSS <dashed-ident> برای نام استفاده کنید.
  • اختیاری محور موردنظر برای ردیابی را به‌طور صریح مشخص کنید: وقتی محور پیش‌فرض block را هدف قرار نمی‌دهید (مثلاً در پیمایشگر افقی)، به‌طور صریح مشخص کنید که کدام محور با view-timeline-axis ردیابی شود.
  • باید مطمئن شوید که دامنه جستجو کار می‌کند: وقتی عنصری که view-timeline را اعلام می‌کند، جد درخت مسطح عنصر پویانمایی‌شده نیست، بااستفاده از timeline-scope در جد مشترک، نمایان بودن نام view-timeline را بالا ببرید.

راهبردهای جایگزین

برای مرورگرهایی که از پویانمایی‌های پیمایش‌محور پشتیبانی نمی‌کنند، می‌توانید از یک جایگزین برای بازآفرینی جلوه‌های دیداری استفاده کنید. جایگزین‌ها معمولاً با شنونده پیمایش (برای جلوه‌های ScrollTimeline) یا IntersectionObserver API (برای جلوه‌های ViewTimeline) ساخته می‌شوند.

در مرورگرهایی که پشتیبانی داخلی از پویانمایی‌های پیمایش‌محور دارند، همیشه از پیاده‌سازی CSS بومی استفاده کنید زیرا عملکرد بهتری دارند.

توجه داشته باشید که همه جلوه‌ها را نمی‌توان بااستفاده از رویکرد جایگزین بازآفرینی کرد.

به‌طور خاص برای این مورد استفاده، دستورگان زیر برای مرورگرهایی که از پویانمایی‌های پیمایش‌محور پشتیبانی نمی‌کنند، جایگزین را اعمال می‌کند. این کد از IntersectionObserver برای ردیابی رؤیت‌پذیری هر عنصر #tracked section استفاده می‌کند و ویژگی transform مربوط به #animated section را براساس آن به‌روز می‌کند.

const animatedSections = document.querySelectorAll('#animated section');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const sectionIndex = Array.from(document.querySelectorAll('#tracked section')).indexOf(entry.target);
    if (sectionIndex !== -1) {
      const animatedSection = animatedSections[sectionIndex];
      const ratio = entry.intersectionRatio;

      // Animate-in
      animatedSection.style.opacity = ratio;
      animatedSection.style.transform = `scale(${0.5 + ratio * 0.5}) rotateY(${-180 + ratio * 180}deg)`;

      // Animate-out
      if (ratio < 0.5) {
        animatedSection.style.translate = `${(0.5 - ratio) * 2 * 100}% 0`;
      } else {
        animatedSection.style.translate = '0 0';
      }
    }
  });
}, { threshold: Array.from({length: 101}, (_, i) => i / 100) });

document.querySelectorAll('#tracked section').forEach(section => {
  observer.observe(section);
});

و CSS همراه:

#animated section {
  opacity: 0;
  transform: scale(0.5)  rotateY(-180deg);
  backface-visibility: hidden;
}

/* MANDATORY Copy-Paste Safety: Ensure content remains fully visible and legible for assistive technologies or users with motion sensitivities */
@media (prefers-reduced-motion: reduce) {
  #animated section {
    opacity: 1 !important;
    transform: none !important;
    translate: 0 0 !important;
  }
}

این جایگزین، پویانمایی دقیق‌تر و پیمایش‌محوری را برای مرورگرهایی که از ویژگی CSS بومی پشتیبانی نمی‌کنند ارائه می‌دهد و تجربه سازگارتری را برای همه کاربران تضمین می‌کند. بااستفاده از مجموعه‌ای از آستانه‌ها برای IntersectionObserver، می‌توانیم موقعیت پیمایش را با دقت بیشتری ردیابی کنیم و پویانمایی روان‌تری ایجاد کنیم.