Wolfenstein فقط CSS پروژه کوچکی است که چند هفته پیش ساختم. این یک آزمایش با تبدیلها و انیمیشنهای سه بعدی CSS بود.
با الهام از دمو FPS و دیگری قلم کد Wolfenstein، تصمیم گرفتم نسخه خودم را بسازم. این بازی بر اساس اپیزود 1 – طبقه 9 از بازی اصلی Wolfenstein 3D ساخته شده است.
ویراستار: این بازی عمداً نیاز به واکنش سریع دارد تا از نمایشگر Game Over جلوگیری شود.
در اینجا یک ویدیوی پخش است:
به طور خلاصه، پروژه من چیزی نیست جز یک انیمیشن طولانی CSS که به دقت فیلمنامه شده است. به علاوه چند نمونه از هک چک باکس.
:checked ~ div { animation-name: spin; }
محیط از چهره های شبکه سه بعدی تشکیل شده است و انیمیشن ها عمدتاً ترجمه و چرخش سه بعدی ساده هستند. هیچ چیز واقعاً فانتزی نیست.
با این حال، حل دو مشکل به ویژه دشوار بود:
- هر زمان که بازیکن بر روی دشمن کلیک کرد، انیمیشن "شلیک سلاح" را پخش کنید.
- وقتی رئیس تندرو آخرین ضربه را خورد، یک حرکت آهسته دراماتیک وارد کنید.
در سطح فنی، این به این معنی بود:
- با علامت زدن کادر بعدی یک انیمیشن را دوباره پخش کنید.
- وقتی یک چک باکس علامت زده می شود، انیمیشن را کم کنید.
در واقع هیچ کدام به درستی در پروژه من حل نشد! من یا در نهایت از راهحلها استفاده کردم یا فقط منصرف شدم.
از سوی دیگر، پس از مدتی حفاری، در نهایت کلید هر دو مشکل را پیدا کردم: تغییر ویژگیهای اجرای انیمیشنهای CSS. در این مقاله به بررسی بیشتر این موضوع خواهیم پرداخت:
- نمونه های تعاملی زیاد
- تشریح ها: هر نمونه چگونه کار می کند (یا کار نمی کند)؟
- پشت صحنه: مرورگرها چگونه حالت های انیمیشن را مدیریت می کنند؟
اجازه دهید من "آجرهای من را پرتاب کن".
مشکل 1: پخش مجدد انیمیشن
مثال اول: "فقط یک چک باکس دیگر"
اولین شهود من "فقط یک چک باکس دیگر اضافه کنید" بود که کار نمی کند:
هر چک باکس به صورت جداگانه کار می کند، اما نه هر دو با هم. اگر یک چک باکس قبلا علامت زده شده باشد، دیگری دیگر کار نمی کند.
در اینجا نحوه کار (یا "کار نمی کند"):
- La
animation-name
of<div>
isnone
به صورت پیش فرض. - کاربر روی یک چک باکس کلیک می کند،
animation-name
شودspin
، و انیمیشن از ابتدا شروع می شود. - پس از مدتی کاربر روی چک باکس دیگر کلیک می کند. یک قانون جدید CSS اعمال می شود، اما
animation-name
is هنوزspin
، یعنی هیچ انیمیشنی اضافه یا حذف نمی شود. انیمیشن به سادگی به پخش خود ادامه می دهد انگار هیچ اتفاقی نیفتاده است.
مثال دوم: شبیه سازی انیمیشن
یک روش کاری شبیه سازی انیمیشن است:
#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2; }
در اینجا چگونه کار می کند:
animation-name
isnone
در ابتدا- کاربر روی "Spin!" کلیک می کند.
animation-name
شودspin1
. انیمیشنspin1
از ابتدا شروع شده است زیرا به تازگی اضافه شده است. - کاربر روی "دوباره چرخش!" کلیک می کند.
animation-name
شودspin2
. انیمیشنspin2
از ابتدا شروع شده است زیرا به تازگی اضافه شده است.
توجه داشته باشید که در مرحله 3، spin1
به دلیل نظم قوانین CSS حذف شده است. اگر "دوباره بچرخ!" ابتدا بررسی می شود.
مثال سوم: "ضمیمه کردن همان انیمیشن"
یکی دیگر از رویکردهای کاری، "ضمیمه کردن همان انیمیشن" است:
#spin1:checked ~ div { animation-name: spin; }
#spin2:checked ~ div { animation-name: spin, spin; }
این شبیه به مثال قبلی است. در واقع می توانید این رفتار را به این شکل درک کنید:
#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2, spin1; }
توجه داشته باشید که وقتی "دوباره بچرخ!" علامت زده می شود، انیمیشن در حال اجرا قدیمی تبدیل به دومین انیمیشن در لیست جدید می شود که می تواند غیر قابل درک باشد. یک پیامد مستقیم این است: ترفند کار نخواهد کرد اگر animation-fill-mode
is forwards
. در اینجا یک نسخه نمایشی است:
اگر تعجب می کنید که چرا این مورد است، در اینجا چند سرنخ وجود دارد:
animation-fill-mode
isnone
به طور پیش فرض، به این معنی است که "انیمیشن در صورت پخش نشدن هیچ تاثیری ندارد".animation-fill-mode: forwards;
به این معنی است که «پس از اتمام پخش انیمیشن، باید برای همیشه در آخرین فریم کلیدی باقی بماند».spin1
تصمیم همیشه نادیده گرفته می شودspin2
به خاطرspin1
بعداً در لیست ظاهر می شود.- فرض کنید کاربر روی «Spin!» کلیک میکند، منتظر چرخش کامل است، سپس روی «دوباره چرخش» کلیک میکند. در این لحظه.
spin1
قبلاً تمام شده است وspin2
تازه شروع می شود
بحث
قانون سرانگشتی: شما نمی توانید یک انیمیشن CSS موجود را "ریستارت" کنید. در عوض، میخواهید یک انیمیشن جدید اضافه و پخش کنید. این ممکن است توسط مشخصات W3C:
هنگامی که یک انیمیشن شروع شد، ادامه می یابد تا زمانی که به پایان برسد یا نام انیمیشن حذف شود.
اکنون با مقایسه دو مثال آخر، فکر میکنم در عمل، «انیمشنهای شبیهسازی» اغلب بهتر عمل میکنند، به خصوص زمانی که پیشپردازنده CSS در دسترس باشد.
مشکل 2: حرکت آهسته
ممکن است کسی فکر کند که کند کردن یک انیمیشن فقط یک موضوع طولانی تر است animation-duration
:
div { animation-duration: 0.5s; }
#slowmo:checked ~ div { animation-duration: 1.5s; }
در واقع، این کار می کند:
... یا این کار را می کند؟
با چند ترفند، بهتر است مشکل را ببینید.
بله، انیمیشن کند شده است. و نه، ظاهر خوبی ندارد. سگ (تقریباً) همیشه وقتی چک باکس را تغییر میدهید «پرش» میکند. علاوه بر این، به نظر می رسد که سگ به جای موقعیت اولیه، به یک موقعیت تصادفی می پرد. چطور؟
درک آن آسان تر خواهد بود اگر دو عنصر سایه را معرفی کنیم:
هر دو عنصر سایه انیمیشن های مشابهی را اجرا می کنند animation-duration
. و تحت تأثیر چک باکس قرار نمی گیرند.
هنگامی که چک باکس را تغییر می دهید، عنصر بلافاصله بین حالت های دو عنصر سایه جابجا می شود.
نقل قول مشخصات W3C:
تغییرات در مقادیر ویژگی های انیمیشن در حالی که انیمیشن در حال اجرا است اعمال می شود به گونه ای که گویی انیمیشن از زمان شروع آن مقادیر را داشته است.
این امر به دنبال بی تابعیت طراحی، که به مرورگرها اجازه می دهد تا به راحتی مقدار متحرک را تعیین کنند. محاسبه واقعی شرح داده شده است اینجا کلیک نمایید و اینجا کلیک نمایید.
تلاشی دیگر
یک ایده این است که انیمیشن فعلی را مکث کنید، سپس یک انیمیشن کندتر اضافه کنید که از آنجا شروع به کار کند:
div {
animation-name: spin1;
animation-duration: 2s;
}
#slowmo:checked ~ div {
animation-name: spin1, spin2;
animation-duration: 2s, 5s;
animation-play-state: paused, running;
}
بنابراین کار می کند:
... یا این کار را می کند؟
با کلیک بر روی "Slowmo!" سرعت آن کاهش می یابد. اما اگر منتظر یک دایره کامل باشید، یک "پرش" خواهید دید. در واقع، زمانی که "Slowmo!" روی آن کلیک می شود.
دلیلش هم اینه که ما نداریم from
فریم کلیدی تعریف شده - و ما نباید. هنگامی که کاربر روی "Slowmo!" کلیک می کند، spin1
در برخی موقعیت ها مکث می شود و spin2
دقیقا از همان موقعیت شروع می شود. ما به سادگی نمی توانیم آن موقعیت را از قبل پیش بینی کنیم... یا می توانیم؟
یک راه حل کاری
ما میتوانیم! با استفاده از یک ویژگی سفارشی، میتوانیم زاویه را در انیمیشن اول ثبت کنیم، سپس آن را به انیمیشن دوم منتقل کنیم:
div {
transform: rotate(var(--angle1));
animation-name: spin1;
animation-duration: 2s;
}
#slowmo:checked ~ div {
transform: rotate(var(--angle2));
animation-name: spin1, spin2;
animation-duration: 2s, 5s;
animation-play-state: paused, running;
}
@keyframes spin1 {
to {
--angle1: 360deg;
}
}
@keyframes spin2 {
from {
--angle2: var(--angle1);
}
to {
--angle2: calc(var(--angle1) + 360deg);
}
}
توجه داشته باشید: @property
در این مثال استفاده شده است که توسط همه مرورگرها پشتیبانی نمی شود.
راه حل "کامل".
یک هشدار به راه حل قبلی وجود دارد: "خروج از slowmo" به خوبی کار نمی کند.
در اینجا راه حل بهتری وجود دارد:
در این نسخه اسلوموشن را می توان به صورت یکپارچه وارد یا خارج کرد. هیچ ویژگی آزمایشی نیز استفاده نمی شود. بنابراین آیا این راه حل کامل است؟ بله و خیر.
این راه حل مانند "تغییر" "دنده" عمل می کند:
- چرخ دنده ها: دو تا هستند
<div>
س یکی پدر و مادر دیگری است. هر دو دارایspin
انیمیشن اما با متفاوتanimation-duration
. حالت نهایی عنصر انباشته شدن هر دو انیمیشن است. - جابجایی: در ابتدا فقط یکی
<div>
انیمیشن خود را در حال اجرا دارد. دیگری مکث می کند. هنگامی که چک باکس تغییر می کند، هر دو انیمیشن حالت خود را عوض می کنند.
در حالی که من واقعاً نتیجه را دوست دارم، یک مشکل وجود دارد: این یک سوء استفاده خوب از آن است spin
انیمیشن، که به طور کلی برای انواع دیگر انیمیشن ها کار نمی کند.
یک راه حل عملی (با JS)
برای انیمیشن های عمومی، می توان با کمی جاوا اسکریپت به عملکرد حرکت آهسته دست یافت:
یک توضیح سریع:
- یک ویژگی سفارشی برای ردیابی پیشرفت انیمیشن استفاده می شود.
- زمانی که چک باکس را تغییر دهید، انیمیشن «بازراهاندازی» میشود.
- کد JS درست را محاسبه می کند
animation-delay
برای اطمینان از انتقال یکپارچه من توصیه می کنم این مقاله اگر با مقادیر منفی آشنا نیستیدanimation-delay
.
شما می توانید این راه حل را به عنوان ترکیبی از "راه اندازی مجدد انیمیشن" و رویکرد "تغییر دنده" مشاهده کنید.
در اینجا مهم است که پیشرفت انیمیشن را به درستی پیگیری کنید. در صورتی که راه حل ها امکان پذیر است @property
در دسترس نیست. به عنوان مثال، این نسخه استفاده می کند z-index
برای پیگیری پیشرفت:
نکته جانبی: در ابتدا سعی کردم یک نسخه فقط CSS ایجاد کنم اما موفق نشدم. در حالی که 100٪ مطمئن نیستم، فکر می کنم به این دلیل است animation-delay
is متحرک نیست.
در اینجا نسخه ای با حداقل جاوا اسکریپت وجود دارد. فقط "ورود آهسته" کار می کند.
لطفاً اگر موفق به ایجاد یک نسخه فقط CSS کار میشوید، به من اطلاع دهید!
Slow-mo Any Animation (با JS)
در نهایت، من می خواهم راه حلی را به اشتراک بگذارم که برای (تقریبا) هر انیمیشنی، حتی با چندین انیمیشن پیچیده کار می کند. @keyframes
:
اساسا، شما باید یک ردیاب پیشرفت انیمیشن اضافه کنید، سپس با دقت محاسبه کنید animation-delay
برای انیمیشن جدید با این حال، گاهی اوقات بدست آوردن مقادیر صحیح ممکن است مشکل باشد (اما ممکن است).
مثلا:
animation-timing-function
نیستlinear
.animation-direction
نیستnormal
.- مقادیر متعدد در
animation-name
مختلف باanimation-duration
'شنanimation-delay
's
این روش نیز شرح داده شده است اینجا کلیک نمایید برای Web Animations API.
تشکر و قدردانی
من این مسیر را پس از مواجهه با پروژه های فقط CSS شروع کردم. برخی بودند آثار هنری ظریف، و برخی بودند ابزارهای پیچیده. موارد مورد علاقه من آنهایی هستند که شامل اشیاء سه بعدی هستند، به عنوان مثال، این توپ پرتاب و این مکعب بسته بندی.
در ابتدا نمی دانستم اینها چگونه ساخته می شوند. بعدها خواندم و چیزهای زیادی از آن یاد گرفتم این آموزش زیبا توسط آنا تودور.
همانطور که مشخص شد، ساخت و متحرک سازی اشیاء سه بعدی با CSS تفاوت چندانی با انجام آن ندارد بلندر، فقط با طعم کمی متفاوت.
نتیجه
در این مقاله رفتار انیمیشن های CSS را در زمانی که یک animate-*
دارایی تغییر یافته است به خصوص ما راه حل هایی را برای "بازپخش یک انیمیشن" و "انیمیشن آهسته" کار کردیم.
امیدوارم این مقاله برای شما جالب باشد. لطفا بگو به چه می اندیشی!