حرکت

  • از clip-path و mask-image برای آشکارسازی‌های هندسی سفارشی و محو شدن‌های تدریجی و روان استفاده کنید.
  • برای جلوه‌های غیرضروری وابسته به پیمایش، به‌جای شنوندگان JS از پویانمایی‌های پیمایش‌محور (animation-timeline: scroll()) استفاده کنید.
  • از View Transitions برای پویانمایی یکپارچه بین حالت‌های چیدمان پیچیده استفاده کنید.

عملکرد

  • ترجیحاً opacity و transform را پویانمایی کنید (ازجمله ویژگی‌های تبدیل تکی، مثلاً translate به‌جای left/right/top/bottom) تا مطمئن شوید پویانمایی‌ها در رشته ترکیب‌کننده باقی می‌مانند.
  • از transition-behavior: allow-discrete، @starting-style، و (برای عناصر لایه بالا مثل <dialog> یا [popover]) overlay برای پویانمایی حالت‌های ورود و خروج گسسته به‌صورت بومی استفاده کنید؛ پویانمایی ورود و خروج عنصر و پویانمایی عناصر به و از لایه بالا را ببینید.
.popover-reveal {
  /* Transition discrete display and top-layer overlay alongside opacity */
  transition:
    opacity ease-out,
    display,
    overlay;
  transition-duration: 0.2s;
  transition-behavior: allow-discrete;
}

دسترس‌پذیری

از پُرسمان‌های رسانه‌ای prefers-reduced-motion برای خاموش کردن حرکت سنگین برای کاربرانی که آن را ترجیح می‌دهند استفاده کنید.

  • ارائه سازوکار توقف موقت: به کاربران اجازه دهید تا گردونه‌ها، برنماها، یا دیگر پویانمایی‌های دائمی را که به‌طور خودکار اجرا می‌شوند متوقف کنند.
  • پیش‌فرض کردن نماهای ایستا: پیش‌فرض کردن حالت‌های ایستا و اجازه دادن به کاربران برای موافقت با حرکت را درنظر بگیرید.
  • از حد مجاز چشمک زدن (سه بار در ثانیه) فراتر نروید: هرگز چشمک زدن سریع از روشن به تاریک را اضافه نکنید. این جلوه‌ها می‌تواند باعث تشنج شود.

animation-duration: 0.01ms; را به‌طور سراسری اعمال نکنید زیرا می‌تواند باعث شود برخی پویانمایی‌ها بیشتر ناهماهنگ شوند. درعوض، اولویت‌های حرکت را به‌صورت موردی بااستفاده از یکی از دو رویکرد مدیریت کنید، پُرسمان @media را در قانون تودرتو کنید تا هر دو نسخه باهم نگهداری شوند:

  • با پویانمایی‌هایی که به‌طور پیش‌فرض فعال هستند شروع کنید و بااستفاده از @media (prefers-reduced-motion: reduce) آن‌ها را ملایم‌تر کنید:
/* Good: Dampen spin states or slow down animations for reduced motion */
.spinner {
  animation: spin 1s linear infinite;
  @media (prefers-reduced-motion: reduce) {
    animation: none;
    opacity: 0.5;
  }
}

progress:not([value]) {
  animation: slide 1s infinite linear;
  @media (prefers-reduced-motion: reduce) {
    animation-duration: 20s;
  }
}
  • به‌طور پیش‌فرض بدون پویانمایی شروع کنید و آن‌ها را بااستفاده از @media (prefers-reduced-motion: no-preference) فعال کنید:
/* Good: Default to static or gentle states and opt-in to full motion */
.spinner {
  opacity: 0.5;
  @media (prefers-reduced-motion: no-preference) {
    opacity: 1;
    animation: spin 1s linear infinite;
  }
}

progress:not([value]) {
  animation: slide 20s infinite linear;
  @media (prefers-reduced-motion: no-preference) {
    animation-duration: 1s;
  }
}

کار با رویدادهای پویانمایی و انتقال در جاوا اسکریپت

در هر دو میانای AnimationEvent (animationstart،‏ animationiteration،‏ animationend،‏ animationcancel) و TransitionEvent (transitionrun،‏ transitionstart،‏ transitionend،‏ transitioncancel)،‏ event.target عنصر DOM مبدأ را به شما می‌دهد. برای دسترسی به شیء Animation خاص (Animation،‏ CSSAnimation، یا CSSTransition) که رویداد را راه‌اندازی کرده است، از ویژگی فقط‌خواندنی event.animation استفاده کنید.

وقتی event.animation دردسترس نیست، معمولاً می‌توانید نمونه Animation را با فراخوانی event.target.getAnimations() و فیلتر کردن براساس event.animationName (یا event.propertyName برای انتقال‌ها) پیدا کنید. بااین‌حال، استفاده مستقیم از event.animation ترجیح داده می‌شود تا از چندین سناریو که در آن این جایگزین که event.target.getAnimations() را فیلتر می‌کند طبق انتظار کار نمی‌کند جلوگیری شود:

  • پویانمایی‌های Web Animations API (element.animate()) فاقد animationName هستند، بنابراین هنگام فیلتر کردن event.target.getAnimations() نمی‌توان آن‌ها را براساس نام مطابقت داد.
  • وقتی چند پویانمایی با نام یکسان به یک عنصر پیوست شده باشد، فیلتر کردن براساس animationName برای AnimationEventها مبهم است.
    • توجه داشته باشید که فیلتر کردن روی transitionProperty برای TransitionEventها کار می‌کند زیرا فقط یک رویداد برای هر ویژگی انتقال وجود دارد.
  • event.target همیشه به عنصر DOM مبدأ اشاره می‌کند، بنابراین بین پویانمایی‌هایی که روی خود عنصر اجرا می‌شوند و پویانمایی‌هایی که روی یکی از شبه‌عناصر آن اجرا می‌شوند تفاوتی قائل نمی‌شود — درحالی‌که event.animation.effect.pseudoElement شبه‌عنصر هدف را مستقیماً نمایش می‌دهد.
// Works in most basic CSS animation cases, but fails for WAAPI animations,
// duplicate animation names on the same element, or pseudo-elements.
document.addEventListener('animationstart', (event) => {
  const animation = event.target
    .getAnimations()
    .find((anim) => anim.animationName === event.animationName);
  console.log(animation);
});

// PREFERRED: Directly access the exact Animation object via event.animation
document.addEventListener('animationstart', (event) => {
  const animation = event.animation;
  console.log(animation);
});

می‌توانید بااستفاده از 'animation' in AnimationEvent.prototype (یا 'animation' in TransitionEvent.prototype) پشتیبانی را تشخیص دهید.