مقدمه

وب یک رسانه‌ی زنده و پویاست و عناصر تشکیل‌دهنده‌ی صفحات وب می‌توانند افکت‌‌های بصری (visual effects) مختلفی را دریافت کنند و به شیوه‌های متنوعی نمایش داده شوند. محو تدریجی عناصر (fade-out)، نمایش اسلایدی منوها و تغییر تدریجی رنگی به رنگ دیگر نمونه‌هایی از کارهایی هستند که در وب بر خلاف رسانه‌های چاپی سنتی، قادر به انجام آنها هستیم. در CSS دو راه برای انجام کارهایی از این دست و به طور کلی متحرک‌سازی عناصر وجود دارد: یکی استفاده از ترنزیشن‌ها (transitions) و دیگری خلق انیمیشن‌ها (animations). در درس قبلی با انوع تبدیلات (transformations) در CSS و روش‌های اعمال آنها آشنا شدیم. دیدیم که تبدیلات چرخش یا دوران (rotation)، انتقال یا جابجایی (translation)، تغییر مقیاس (scaling) و تغییر شکل (skewing) بسیار کاربردی و مفید هستند اما این تبدیلات در ترکیب با ترنزیشن‌ها و انیمیشن‌های CSS است که قدرت واقعی خود را نشان می‌دهند. ترنزیشن و انیمیشن هر دو روش‌هایی برای متحرک‌سازی عناصر هستند اما با وجود شباهت‌هایی که دارند، کاملاً یکسان نیستند و کار متحرک‌سازی عناصر را به شکل متفاوتی انجام می‌دهند. در ادامه، ابتدا در مورد ترنزیشن‌ها و سپس در مورد انیمیشن‌های CSS صحبت می‌کنیم.

ترنزیشن‌های CSS

با استفاده از یک ترنزیشن CSS، می‌توانیم از مرورگر بخواهیم که وقتی مقدار یک پراپرتی تغییر می‌کند، این تغییر نه به صورت آنی بلکه به صورت تدریجی انجام شود. برای مثال، اگر لینک‌هایی با رنگ آبی در صفحه داشته باشیم که با بردن ماوس روی آنها (hover) رنگ آنها به قرمز تغییر می‌کند، با استفاده از یک ترنزیشن می‌توانیم ترتیبی دهیم که هنگام بردن ماوس روی لینک‌ها، تغییر رنگ از آبی به قرمز با طی یک روند تدریجی و گذشتن از رنگ‌های بین این دو رنگ مثل بنفش صورت گیرد و عکس این اتفاق با برداشتن ماوس از روی لینک رخ دهد. استفاده‌ی صحیح و به‌جا از ترنزیشن‌ها می‌تواند موجب تقویت حس تعامل‌پذیری (interactivity) صفحه شود و هنگام رخ‌دادن یک تغییر، کاربر را متوجه آن بکند.

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

 Copy Icon CSS
.fancy-button{
  background: blue;
}

.fancy-button:hover{
  background: red;
  transform: scale(1.1);
}

وقتی کاربر ماوس را روی دکمه ببرد، دو اتفاق رخ می‌دهد: یکی اینکه رنگ پس‌زمینه بلافاصله از آبی به قرمز تغییر می‌کند و دیگری اینکه دکمه با یک فاکتور 1.1 تغییر اندازه می‌دهد. این تغییرات آنی، بار ویژوال جالبی ندارد. اما با استفاده از پراپرتی transition می‌توانیم این مورد را اصلاح کنیم:

 Copy Icon CSS
.fancy-button{
  background: blue;
  transition: 500ms;
}

.fancy-button{
  background: red;
  transform: scale(1.1);
}

حالا اگر ماوس را روی دکمه ببریم، تغییر سایز و رنگ پس‌زمینه‌ی این عنصر نه به‌صورت آنی، بلکه به‌صورت تدریجی و در طی یک بازه‌ی زمانی نیم‌ثانیه‌ای رخ می‌دهد. اگر با مفهوم انیمیشن‌ها آشنا باشید، این دو وضعیت دکمه (یعنی پس‌زمینه‌ی آبی و سایز با مقیاس 1 در مقابل پس‌زمینه‌ی قرمز و سایز با مقیاس 1.1) را می‌توانید معادل دو keyframe در یک انیمیشن تصور کنید. نیازی به تعریف همه‌ی وضعیت‌های بین این دو keyframe نداریم و خود مرورگر از یک وضعیت به وضعیت دیگر حرکت می‌کند. برای مثال، تغییر رنگ پس‌زمینه با عبور از همه‌ی رنگ‌های بین رنگ آبی و قرمز صورت می‌گیرد.

این ساده‌ترین مثال ممکن از یک ترنزیشن است که باعث می‌شود هر پراپرتیِ پشتیبانی‌کننده از ترنزیشن (و انیمیشن) ظرف نیم ثانیه به مقدار جدید خود برسد. امکان استفاده از ترنزیشن‌های چندمرحله‌ای هم وجود دارد. مثلاً در کد بالا، می‌توانیم ترتیبی دهیم که ابتدا تغییر رنگ پس‌زمینه انجام شود و پس از کامل شدن آن، تغییر سایز عنصر رخ دهد.

پراپرتی transition یک لیست comma-separated از ترنزیشن‌ها را دریافت می‌کند؛ یعنی لیستی از ترنزیشن‌ها که با کاما از هم جدا شده‌اند. وقتی مانند مثال بالا، فقط یک بازه‌ی زمانی به عنوان مقدار پراپرتی transition تعیین شده باشد، مقدار پیش‌فرض all برای این پراپرتی در نظر گرفته می‌شود؛ یعنی تغییر تدریجی برای همه‌ی پراپرتی‌ها اعمال می‌شود. اما می‌توانیم به جای این کار، پراپرتی‌های مشخصی را تعیین کنیم و برای هر کدام از آنها یک بازه‌ی زمانی و یک میزان تأخیر (delay) تعیین کنیم.

برای مثال، فرض کنید می‌خواهیم تغییر رنگ در 500 میلی‌ثانیه و تغییر سایز در 500 میلی‌ثانیه و با 500 میلی‌ثانیه تأخیر رخ دهد. در این صورت داریم:

 Copy Icon CSS
.fancy-button{
  background: blue;
  transition: background 500ms, transform 500ms 500ms;
}

.fancy-button:hover{
  background: red;
  transform: scale(1.1);
}

وقتی یک ترنزیشن را به روش بالا ایجاد کنیم، اولین آرگومان به نام ترنزیشن اختصاص دارد، دومین آرگومان بازه‌ی زمانی (duration) است و آرگومان سوم هم میزان تأخیر (delay) را مشخص می‌کند. اجرای مثال بالا باعث می‌شود که در صورت رخ دادن hover، ابتدا رنگ پس‌زمینه‌ی دکمه طی 500 میلی ثانیه تغییر کند، در حالی که سایز دکمه در این مدت تغییری نمی‌کند. بعد از تغییر رنگ، تغییر سایز دکمه با یک تأخیر 500 میلی‌ثانیه‌ای شروع شده و ظرف 500 میلی‌ثانیه تکمیل می‌شود.

پراپرتی transition همانند پراپرتی‌های border، font و background یک پراپرتی اختصاری (shorthand) است. پراپرتی transition مثال بالا معادل پراپرتی‌های زیر است:

Copy Icon CSS
transition-property: backgroundr, transform;
transition-duration: 500ms, 500ms;
transition-delay: 0ms, 500ms;

مقادیر زمانی

مقادیر زمانی را برای ترنزیشن‌ها (و انیمیشن‌ها) می‌توانیم بااستفاده از واحدهای ثانیه (s) یا میلی‌ثانیه (ms) تعیین کنیم. امکان استفاده از مقادیر اعشاری هم وجود دارد؛ یعنی مثلاً به جای 500ms می‌توانیم از مقدار معادل 0.5s استفاده کنیم.

توابع Easing

اگر مثال‌های بالا را اجرا کرده باشید، شاید متوجه شده باشید که نرخ وقوع ترنزیشن یک نرخ خطی (linear rate) نیست. در واقع، شروع ترنزیشن آهسته است، در میانه‌ی راه سرعت آن افزایش می‌یابد و در انتهای مسیر مجدداً از سرعتش کاسته می‌شود. این به‌خاطر تابع زمان‌بندی (timing function) پیش‌فرض ترنزیشن است که ease نام دارد. اما توابع زمان‌بندی دیگری هم وجود دارد. این توابع که رسماً توابع easing نامیده می‌شوند را می‌توان با یک نمودار دو بعدی نمایش داد که محور افقی یا X آن نشان‌دهنده‌ی زمان و محور عمودی یا Y نشان‌دهنده‌ی پیشرفت ترنزیشن است.

نمودار توابع easing به صورت منحنی‌های پیوسته و همواری است که از آنها با عنوان Cubic Bezier Curves یاد می‌شود. یک چنین منحنی یا خمی با تعیین چهار نقطه‌ی کنترلی روی نمودار تعیین می‌شود. یک روش مناسب برای درک نحوه‌ی ترسیم منحنی از روی چهار نقطه این است که فرض کنیم یک خط مستقیم بین نقطه‌ی اول و آخر رسم شده و سپس، نقاط دوم و سوم خط را به سمت بالا و پایین خم می‌کنند تا یک منحنی ایجاد شود.

در یک منحنی Cubic Bezier برای یک تابع easing در CSS، اولین نقطه همیشه (0, 0) است و نشان‌دهنده‌ی وضعیت شروع انیمیشن یا ترنزیشن است. نقطه‌ی پایانی هم همیشه (1, 1) است و وضعیت پایانی را نشان می‌دهد. در مورد دو نقطه‌ی میانی هم مختص X همیشه باید بین صفر و یک باشد؛ در غیر این صورت، یک تابع easing معتبر در نظر گرفته نمی‌شود.

از آنجایی که مختصات اولین و آخرین نقطه همیشه (0, 0) و (1, 1) است، تنها چیزی که باید مشخص شود، مختصات دو نقطه‌ی میانی است که انحنا را ایجاد می کنند.

Copy Icon CSS
transition-timing-function: cubic-bezier(.17, .67, .9, .6);

نمودار این تابع به صورت زیر خواهد بود.

نمودار منحنی بیزی

به جای تعیین نقاط میانی با استفاده از تابع cubic-bezier() می‌توانیم از توابع easing درونی (built-in) استفاده کنیم. در جدول زیر، نام این توابع به همراه مقادیر متناظر در تابع cubic-bezier() و نمودار هر یک آورده شده است:

نام تابع مقادیر متناظر در تابع cubic-bezier() نمودار
linear 0.0, 0.0, 1.0, 1.0 منحنی خطی
ease 0.25, 1.0, 0.25, 1.0 منحنی ease
ease-in 0.42, 0.0, 1.0, 1.0 منحنی ease-in
ease-out 0.0, 0.0, 0.58, 1.0 منحنی ease-out
ease-in-out 0.42, 0.0, 0.58, 1.0 منحنی ease-in-out

توابع easing را می‌توان به صورت توابع گام (step function) نیز تعیین کرد. یک تابع گام، ترنزیشن را به گام‌های برابر در یک جهت مشخص تقسیم می‌کند. توابع گام به جای یک ترنزیشن نرم و هموار مانند Bezier Curve، از یک گام به گام دیگر پرش می‌کنند و وضعیت‌های میانی را نادیده می‌گیرند. توابع گام به صورت steps(step-count, direction) تعیین می‌شوند که در آن step-count تعداد گام‌هاست و direction هم می‌تواند یکی از مقادیر زیر باشد:

  • jump-start, start: تغییر در وضعیت در ابتدای هر گام و با شروع ترنزیشن رخ می‌دهد. با توجه به اینکه اولین پرش بلافاصله رخ می‌دهد، وضعیت اولیه‌ی ترنزیشن عملاً از بین می‌رود.
  • jump-end, end: تغییر وضعیت در پایان هر گام رخ می‌دهد و با پایان ترنزیشن به پایان می‌رسد. با این مقدار، وضعیت نهایی ترنزیشن از بین می‌رود.
  • jump-none: وضعین شروع و پایان ترنزیشن حفظ می‌شود. گام اول وضعیت ابتدایی است و گام آخر وضعیت نهایی.
  • jump-both: وضعیت ابتدایی و انتهایی ترنزیشن هر دو از بین می‌روند.

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

انیمیشن‌های CSS

همانطور که دیدیم، ترنزیشن‌های CSS امکان انتقال تدریجی بین دو وضعیت را ممکن می‌کنند. اما انیمیشن‌های CSS امکان انتقال تدرجی بین هر تعداد وضعیت را فراهم می‌کنند. همانند ترنزیشن‌ها، انیمیشن‌ها نیز به‌واسطه‌ی پراپرتی‌هایی که مقدارشان تغییر می‌کند، مشخص می‌شوند. اما بر خلاف ترنزیشن‌ها که با استفاده از یک پراپرتی با نام transition تعریف می‌شوند، برای تعریف انیمیشن‌ها باید از یک at-rule با نام @keyframes استفاده کرد. @keyframes شامل تعریف پراپرتی‌هایی است که در هر مرحله یا وضعیت از انیمیشن باید تغییر کنند و مرورگر به طور خودکار بین این وضعیت‌ها را متحرک می‌کند.

هر قاعده‌ی @keyframes دارای یک نام است و شامل دو یا چند بلاک از پراپرتی‌های CSS است. هر بلاک با یک مقدار درصدی برچسب‌گذاری شده که نشان‌دهنده‌ی کسری از زمان کلی انیمیشن است. در مثال زیر، رنگ پس‌زمینه‌ی عنصر از قرمز به آبی و سپس به سبز تغییر می‌کند.

Copy Icon CSS
@keyframes colors {
  0% {
      background: red;
  }
  
  50% {
      background: blue;
  }
  
  100% {
      background: green;
  }
}  

در اینجا یک قاعده‌ی @keyframes با نام colors تعریف شده است. هر عنصری که این انیمیشن را دریافت کند، در وضعیت ابتدایی دارای رنگ پس‌زمینه‌ی قرمز خواهد بود. در نقطه‌ی میانی انیمیشن، رنگ پس‌زمینه‌ی عنصر به آبی تغییر می‌کند و بالاخره در وضعیت پایانی، عنصر مورد نظر دارای رنگ پس‌زمینه‌ی سبز خواهد بود. همانند آنچه در مورد ترنزیشن‌ها دیدیم، انتقال بین هر یک از این سه وضعیت به صورت تدریجی رخ خواهد داد.

به جای 0% می‌توان از کلمه کلیدی from و به‌جای 100% هم می‌توان از کلمه کلیدی to استفاده کرد.

اما انیمیشن تعریف‌شده را چطور می‌توانیم روی یک عنصر اعمال کنیم؟ برای این کار، از یک پراپرتی CSS با نام animation استفاده می‌شود.

 Copy Icon CSS
.box{
  animation: colors 2s;
  width: 10rem;
  height: 10rem;
}

پراپرتی animation یک پراپرتی shorthand برای مقداردهی چند پراپرتی مربوط به انیمیشن‌هاست و می‌تواند مقادیر را به فرم‌های مختلفی دریافت کند. برای مثال:

Copy Icon CSS
animation: color 2s ease-in-out 2s both;
animation: pulse 2s linear infinite;          

وقتی از پراپرتی animation استفاده می‌کنیم، ترتیب مقادیر اهمیتی ندارد و می‌توانیم مقادیر را به هر ترتیبی وارد کنیم و مرورگر می‌تواند تشخیص دهد که هر مقدار برای کدام پراپرتی است. اما در این مورد یک استثنا وجود دارد که به duration و delay مربوط است. همیشه اولین مقدار زمانی برای duration و دومی برای delay در نظر گرفته می‌شود.

همانطور که در بالا هم اشاره شد، پراپرتی animation یک پراپرتی shorthand یا اختصاری برای چند پراپرتی مربوط به انیمیشن‌هاست. در ادامه، تعدادی از مهمترین و پرکاربردترین این پراپرتی‌ها را معرفی می‌کنیم.

پراپرتی animation-name

پراپرتی animation-name مشخص‌کننده‌ی نام یا شناسه‌ی قاعده‌ی @keyframes است و نشان می‌دهد که کدام انیمیشن باید روی عنصر اعمال شود.

پراپرتی animation-duration

پراپرتی animation-duration همانطور که نامش نشان می‌دهد، مشخص‌کننده‌ی مدت‌زمان انیمیشن است و می‌تواند مقداری بر حسب ثانیه (s) یا میلی‌ثانیه (ms) دریافت کند.

پراپرتی animation-timing-function

پراپرتی animation-easing-function تابع easing را برای انیمیشن تعیین می‌کند. در مورد توابع easing در بخش مربوط به ترنزیشن‌ها صحبت کردیم.

پراپرتی animation-delay

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

پراپرتی animation-fill-mode

اتفاق جالبی که با اجرای مثال قبلی رخ می‌دهد، این است که رنگ پس‌زمینه‌ی عنصر مورد نظر در طی 2 ثانیه از قرمز به آبی و سپس سبز تغییر می‌کند و سپس، عنصر ناپدید می‌شود. علت این امر آن است که به طور پیش‌فرض، پراپرتی‌هایی که در طول اجرای انیمیشن اعمال می‌شوند، با اتمام انیمیشن دیگر اعمال نمی‌شوند. در مثال بالا، بعد از پایان انیمیشن، پراپرتی background که در بلاک @keyframes تعریف شده، روی عنصر اعمال نمی‌شود و چون ما رنگ پس‌زمینه را در قاعده‌ی مربوط به این عنصر تعیین نکرده‌ایم، از مقدار initial برلی پراپرتی background-color که transparent است، استفاده شده و لذا عنصر دیده نمی‌شود. اگر همانند کد زیر رنگی مثل زرد را در قاعده‌ی مربوط به عنصر برای پس‌زمینه تعیین کنیم، بعد از پایان انیمیشن، عنصر با رنگ پس‌زمینه‌ی زرد نمایش داده می‌شود.

 Copy Icon CSS
@keyframes color {
  from {
      background: red;
  }
  
  to {
      background: blue;
  }
}  

.animate{
  background: yellow;
  animation: color 2s;
  animation-delay: 2s;
  width: 10rem;
  height: 10rem;
}

با اجرای این کد خواهید دید که باکس ما در ابتدا پس‌زمیه‌ی زرد دارد. سپس، انیمیشن با 2 ثانیه تأخیر شروع شده و رنگ پس‌زمینه را در طی ۲ ثانیه از قرمز به آبی تغییر می‌دهد و با پایان انیمیشن، با توجه به استایل background: yellow در قاعده‌ی مربوط به عنصر (قاعده‌ی با انتخابگر .animate) رنگ زرد برای پس‌زمینه‌ی عنصر تعیین می‌شود.

این رفتار پیش‌فرض را می‌توان با استفاده از پراپرتی animation-fill-mode تغییر داد. این پراپرتی می‌تواند یکی از مقادیر none، forwards، backwards و both را دریافت کند. اجازه دهید ببینیم تخصیص هر یک از این مقادیر به پراپرتی animation-fill-mode چه تغییری در نتیجه‌ی مثال بالا ایجاد می‌کند.

  • none: عنصر برای ۲ ثانیه زرد است. سپس، بلافاصله به قرمز تغییر می‌کند و در طول ۲ ثانیه‌ی بعدی به آبی می‌رسد. بعد از پایان انیمیشن، پس‌زمینه بلافاصله به رنگ زرد برمی‌گردد. یعنی استفاده از مقدار none باعث می‌شود که استایل‌های تعریف‌شده در @keyframes تنها در زمان اجرای انیمیشن اعمال شوند که همان رفتار پیش‌فرض است.
  • forwards: عنصر برای ۲ ثانیه زرد است. سپس، بلافاصله به قرمز تغییر می‌کند و در طول ۲ ثانیه‌ی بعدی به آبی می‌رسد. بعد از پایان انیمیشن، عنصر همچنان آبی می‌ماند. بنابراین، استفاده از مقدار forwards باعث می‌شود که بعد از پایان انیمیشن، استایل‌های آخرین keyframe انیمیشن روی عنصر اعمال شوند.
  • backwards: عنصر بلافاصله قرمز می‌شود و به مدت دو ثانیه بدون تغییر می‌ماند. سپس، ظرف ۲ ثانیه به آبی می‌رسد. با اتمام انیمیشن، رنگ پس‌زمینه بلافاصله به زرد برمی‌گردد. بنابراین، استفاده از مقدار backwards باعث می‌شود که در طول دروه‌ی تأخیر انیمیشن، استایل‌های اولین keyframe انیمیشن روی عنصر اعمال شوند.
  • both: عنصر بلافاصله قرمز می‌شود و برای ۲ ثانیه بدون تغییر می‌ماند. سپس، در طی ۲ ثانیه به آبی می‌رسد. وقتی انیمیشن به پایان رسید، عنصر آبی می‌ماند. بنابراین، استفاده از مقدار both تأثیرات استفاده از forwards و backwards را به طور همزمان دارد.

پراپرتی animation-iteration-count

در حالت پیش‌فرض، انیمیشن فقط یک بار اجرا می‌شود. اما با تخصیص یک عدد به پراپرتی animation-iteration-count می‌توانیم تریبی دهیم که انیمیشن چندین بار اجرا شود. تخصیص مقدار infinity هم باعث می‌شود که انیمیشن دائماً تکرار شود. جالب است که پراپرتی animation-iteration-count می‌تواند مقادیر غیر صحیح هم دریافت کند. مثلاً مقدار 0.5 باعث می‌شود که انمیشن یک بار و فقط تا میانه‌ی راه اجرا شود.

پراپرتی animation-direction

انیمیشن‌ها در حالت پیش‌فرض رو‌به جلو اجرا می‌شوند؛ یعنی از استایل‌های تعریف‌شده در بلاک from یا 0% به سمت استایل‌های تعریف‌شده در بلاک to یا 100% حرکت می‌کنند. این رفتار پیش‌فرض را می‌توان با استفاده از پراپرتی animation-direction تغییر داد. مقدار پییش‌فرض این پراپرتی normal است اما می‌توانیم با تخصیص مقدار reverse جهت اجرای انیمیشن را معکوس کنیم. پراپرتی animation-direction می‌تواند یک مقدار دیگر با نام alternate-reverse را هم دریافت کند که باعث می‌شود انیمیشن ابتدا رو‌به‌عقب (مثل reverse) و سپس روبه جلو (مثل normal) اجرا شود.

پراپرتی animation-play-state

از پراپرتی animation-play-state برای تعیین وضعیت (اجرا یا توقف) انیمیشن استفاده می‌شود. به این پراپرتی می‌توان یکی از دو مقدار running و paused را اختصاص داد که اولی به معنای اجرا و دومی به معنای توقف انیمیشن است. با دستکاری پراپرتی animation-play-state توسط جاوااسکریپت، می‌توانیم یک انیمیشن را متوقف یا اجرا کنیم. وقتی یک انیمیشنِ در حال اجرا را متوقف کنیم و سپس، مجدداً‌ آن را اجرا کنیم، انیمیشن از ابتدا اجرا نمی‌شود، بلکه از نقطه‌ای که متوقف شده بود، ادامه می‌یابد.

اعمال چند انمیشن روی یک عنصر

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

 Copy Icon CSS
@keyframes color {
  from {
      background-color: red;
  }
  
  to {
      background-color: blue;
  }
}
  
@keyframes spin {
  from {
      transform: rotate(0);
  }
  
  to {
      transform: rotate(360deg);
  }
}  

.animate{
  width: 10rem;
  height: 10rem;
  animation: color 5s alternate infinite, spin 1s linear infinite;
}

در نتیجه‌ی اجرای کد بالا، رنگ عنصر هر 5 ثانیه از قرمز به آبی تغییر می‌کند و به طور همزمان، هر یک ثانیه یک چرخش کامل روی عنصر رخ می‌دهد.

پیامدهای مربوط به کارایی

قدرت و انعطاف‌پذیری ترنزیشن‌ها و انیمیشن‌ها را دیدیم اما معمولاً (نه همیشه!) قدرت زیاد با مسئولیت زیاد همراه است. استفاده‌ی بیش از حد از انیمیشن‌ها و ترنزیشن‌ها یا به‌کارگیری آنها روی پراپرتی‌های پرهزینه می‌تواند باعث افت چشمگیر کارایی (performance) صفحه شود. پراپرتی‌های CSS را می‌توانیم در سه گروه پراپرتی‌های Layout، پراپرتی‌های Paint و پراپرتی‌های Composite قرار دهیم. در ادامه، خواهیم دید که استفاده از انیمیشن‌ها و ترنزیشن‌ها روی کدام‌یک از این نوع پراپرتی‌ها هزینه‌ی بیشتری در ارتباط با کارایی به همراه دارد.

پراپرتی‌های Layout

پراپرتی‌های Layout آن دسته از پراپرتی‌ها هستند که به طرح‌بندی صفحه و نحوه‌ی چیدمان عناصر و محتوای آنها در صفحه مربوط هستند. پراپرتی‌هایی مانند width، height، padding و margin در این گروه قرار می‌گیرند. اعمال انیمیشن و ترنزیشن روی پراپرتی‌های Layout با بیشترین هزینه‌ی کارایی ممکن همراه است.

برای مثال، اگر حاشیه یا margin یک عنصر را متحرک کنیم، فضای اشغال‌شده توسط عنصر برای هر فریم از انیمیشن تغییر می‌کند و این امر روی چیدمان عناصر اطراف آن عنصر نیز تأثیر می‌گذارد. بنابراین، در هر فریم از یک انیمیشن، محاسبات مجدد زیادی باید انجام شود که طبیعتاً این واکنش‌های زنجیره‌ای، تأثیر منفی محسوسی روی کارایی صفحه خواهد داشت.

پراپرتی‌های Paint

پراپرتی‌های Paint آن دسته از پراپرتی‌های CSS هستند که به نحوه‌ی نقاشی عناصر در صفحه مربوط هستند. پراپرتی‌هایی مثل color و background-image در این گروه جای می‌گیرند. پراپرتی‌های Paint تأثیری روی چیدمان و طرح‌بندی صفحه ندارند و بنابراین، به اندازه‌ی پراپرتی‌های Layout پرهزینه نیستند. اما با این وجود، استفاده‌ی بی‌رویه از آنها می‌تواند روی کارایی صفحه (به‌خصوص در دستگاه‌های موبایل) تأثیر بگذارد.

پراپرتی‌های Composite

پراپرتی‌هایی مثل transform و opacity پراپرتی‌های Composite یا ترکیبی هستند که نسبت به سایر انواع پراپرتی‌ها، به‌کارگیری انیمیشن روی آنها هزینه‌های کارایی کمتری دارد. به علاوه، GPU یا واحد پرازش گرافیکی کامپیوتر در پیاده‌سازی انمیشن‌ها و ترنزیشن‌ها روی این گروه از پراپرتی‌ها به CPU‌ کمک می‌کند و باعث می‌شود که انیمیشن به شکل نرم‌تری اجرا شود.

سعی کنید تعداد انیمیشن‌های همزمان را محدود کنید. حتی انیمیشن‌هایی که هزینه‌ی کارایی زیادی ندارند، در صورت استفاده‌ی همزمان با انیمیشن‌های دیگر، مشکلاتی را در ارتباط با کارایی ایجاد می‌کنند؛ به‌خصوص اگر یک یا چند مورد از این انیمیشن‌ها به لحاظ کارایی وضعیت خوبی نداشته باشند. وقتی قصد دارید چند انیمیشن را به طور همزمان روی یک عنصر اعمال کنید، بهتر است آنها را یکی‌یکی تست کنید تا ببینید کدام‌یک از آنها تأثیر مخربی روی کارایی دارد.

انیمیشن‌ها و مسائل مربوط به Accessibility

برخی از افراد دارای نوعی از اختلالات دهلیزی یا تشنجی هستند که می‌توانند با حرکت سریع عناصر یا چشمک زدن عناصر صفحه، فعال شوند. هنگام طراحی انیمیشن‌ها باید این محدودیت را در این افراد در نظر داشته باشبد. در همین راستا، برخی از سیستم‌عامل‌های مدرن به کاربران امکان می‌دهند که نمایش انیمیشن‌ها را غیرفعال یا محدود کنند اما این تنظیمات در حالت پیش‌فرض روی انیمیشن‌های CSS اعمال نمی‌شود و این انیمیشن‌ها فارغ از این تنظیمات، اجرا می‌شوند.

کاری که باید در این مورد انجام دهیم، استفاده از یک Media Query با نام prefers-reduced-motion است. هنگام بررسی مدیا کوئری‌ها در این مورد بیشتر توضیح می‌دهیم اما کسانی که با مفهوم Media Query و سینتکس آن آشنا هستند، می‌توانند مثال زیر را بررسی کنند.

 Copy Icon CSS
@keyframes spin {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(360deg);
    }
}         

.loader{
  width: 10rem;
  height: 10rem;
  background: skyblue;
  animation: spin 500ms linear infinite;
}

نتیجه‌ی اجرای این کد، مربعی است که خیلی سریع (هر 500 میلی‌ثانیه یک دور) می‌چرخد و این چرخش سریع برای افراد با محدودیت‌های ذکر شده خوشایند نیست. این وضعیت را با استفاده از مدیا کوئری prefers-reduced-motion اصلاح می‌کنیم.

 Copy Icon CSS
@keyframes spin {
  from {
      transform: rotate(0deg);
  }
  
  to {
      transform: rotate(360deg);
  }
}  

.loader{
  width: 10rem;
  height: 10rem;
  background: skyblue;
  animation: spin 500ms linear infinite;
}

به این ترتیب، این انیمیشن برای هر کاربری که نمایش انیمیشن‌ها را غیرفعال کرده باشد، اجرا نخواهد شد.