انیمیشن های اسکرول محور • Josh W. Comeau

یکی از بهترین راهها برای افزودن شخصیت به وبسایتهای ما، متحرک کردن چیزها هنگام پیمایش است. به عنوان مثال، من اخیرا انیمیشن Whimsical scrolling زیر را ایجاد کردم انیمیشن ها(در برگه جدید باز می شود) صفحه اصلی:
از لحاظ تاریخی، ما نیاز به استفاده از جاوا اسکریپت برای این نوع افکت داشتیم، اما یک API جدید هیجان انگیز، جدول زمانی انیمیشن، انجام کارهای مشابه در CSS بومی را ممکن می کند! ✨
من چند ماه است که این API جدید را آزمایش کرده ام و صادقانه بگویم، همینطور است خیلی خوبه این بر روی CSS های اولیه موجود به روشی واقعا زیبا و طبیعی ساخته شده است. در واقع، اگر با انیمیشن های فریم کلیدی CSS آشنایی دارید، از قبل بیشتر چیزهایی را که باید بدانید، می دانید!
در این پست وبلاگ، دقیقاً نحوه عملکرد این API جدید را به شما نشان میدهم و برخی از کارهای پیشرفتهتر را که میتوانیم با آن انجام دهیم را بررسی خواهم کرد. من همچنین برخی از مسائلی را که باید مراقب آنها بود به اشتراک بگذارم.
در CSS، میتوانیم از انیمیشنهای فریم کلیدی برای درونیابی هموار بین دو قطعه CSS استفاده کنیم.
به عنوان مثال، فرض کنید انیمیشن فریم کلیدی زیر را داریم:
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
ما میتوانیم از این انیمیشن فریم کلیدی برای محو کردن یک عنصر روی یک عنصر خاص استفاده کنیم مدت زمان:
.elem {
animation: fadeIn 1000ms;
}
در اینجا مفهوم اصلی API Timeline Animation آمده است: چه می شود اگر یک فریم کلیدی را روی یک نگاشت کنیم فاصله اسکرول و نه a مدت?
این را بررسی کنید:
کد زمین بازی
style>
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.elem {
background: goldenrod;
width: 100px;
height: 100px;
animation: fadeIn;
animation-timeline: view();
}
style>
p>
👇 Scroll down here 👇
p>
div class="elem">div>
به جای انتقال 0٪ (opacity: 0;) تا 100% (opacity: 1;) برای مدت معینی استفاده می کنیم موقعیت نمای عنصر به عنوان ورودی اسکرول کردن به پایین از طریق انیمیشن فریم کلیدی میگذرد.
بیایید این را تجسم کنیم. ما می توانیم پیشرفت آیتم را از طریق viewport به صورت درصد اندازه گیری کنیم. سعی کنید در این قسمت پیمایش کنید تا این اندازه گیری را ببینید:
ما این درصد پیشرفت اسکرول را می گیریم و آن را روی انیمیشن فریم کلیدی خود اعمال می کنیم! 🤯
یک دقیقه طول کشید تا ذهنم را پیرامون این مفهوم بچرخانم، زیرا بیش از یک دهه را صرف فکر کردن به فریم های کلیدی CSS به عنوان یک چیز مبتنی بر مدت زمان کرده ام. اما واقعاً وقتی یک انیمیشن کی فریم جدید را با @keyframes، ما مشخص نمی کنیم چی درصد اشاره دارد. در تئوری ما می توانیم استفاده کنیم هر نوع مقدار ورودی که از 0٪ تا 100٪ متغیر است!
بنابراین وقتی تنظیم کردیم animation-timeline: view()، ما رفتار را تغییر می دهیم animation ویژگی به طوری که بر اساس پیشرفت عنصر از طریق viewport است تا زمان.
این اساسیترین راه برای استفاده از این API خط زمانی انیمیشن جدید است، اما همانطور که در این پست وبلاگ خواهیم دید، میتوانیم رفتار را به روشهای مختلفی سفارشی کنیم!
پیوند به این عنوانتوابع زمان
ما میتوانیم یک منحنی کاهش سفارشی را برای انیمیشنهای اسکرولمحور خود اعمال کنیم animation خلاصه نویسی، درست مثل هر انیمیشن کی فریم دیگری!
برای مثال، اگر میخواهیم انیمیشن اسکرولمحور ما دارای یک منحنی سبک «آسانی» باشد، میتوانیم آن را مانند هر انیمیشن فریم کلیدی دیگری تنظیم کنیم:
کد زمین بازی
style>
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.box {
--super-ease-out:
cubic-bezier(0.15, 0.75, 0.35, 1);
animation: spin var(--super-ease-out);
animation-timeline: view();
}
style>
p>
👇 Scroll down here 👇
p>
div class="wrapper">
div class="box">div>
div>
من استفاده می کنم cubic-bezier برای اغراق در پیش فرض ease-out تابع زمان همانطور که می بینید، جعبه زمانی که برای اولین بار وارد ویوپورت می شود، به سرعت می چرخد، اما با نزدیک شدن به بالا، سرعت آن کاهش می یابد.
ما حتی می توانیم به لطف تابع همگام سازی linear() از easing مبتنی بر فنر استفاده کنیم:
کد زمین بازی
style>
@keyframes spin {
0% {
transform: rotate(180deg);
}
100% {
transform: rotate(0deg);
}
}
.box {
--spring: linear(0, 0.01, 0.04 1.8%, 0.161 3.7%, 0.81 10.6%, 1.038, 1.181 16.4%, 1.223, 1.247 19.3%, 1.253 20.2% 21.1%, 1.232, 1.19 25.4%, 1.058 30.8%, 1.001 33.5%, 0.958 36.5%, 0.945, 0.938 39.6%, 0.936 41.6%, 0.941 43.8%, 0.999 53.9%, 1.01 56.7%, 1.015 59.7% 64.3%, 1.001 74.2%, 0.996 79.6%, 1.001);
animation: spin var(--spring);
animation-timeline: view();
}
style>
p>
👇 Scroll down here 👇
p>
div class="wrapper">
div class="box">div>
div>
یه جورایی یه چیز نفرین شده 😂. من فقط این را به اشتراک می گذارم تا نشان دهم این API جدید چقدر قدرتمند است، نه به این دلیل که فکر می کنم ایده خوبی است!
را گروه کاری CSS، تیمی که زبان CSS را مدیریت می کند به نظر من، برای استفاده مجدد/توسعه انیمیشنهای CSS با فریم کلیدی برای انیمیشنهای اسکرولمحور شایسته اعتبار زیادی است. آنها به جای اختراع یک چیز کاملاً جدید از ابتدا، آن را بر روی ساختارهای موجود ساختند، که به این معنی است که می توان یکسری از مهارت ها و دانش از قبل موجود ما را بلافاصله به کار برد!
در مثالهایی که تاکنون دیدهایم، پیشرفت اسکرول عنصر را در کل آن اندازهگیری میکنیم کل سفر از طریق منظره زمانی شروع می شود که راس عنصر وارد Viewport می شود و پس از خروج آخرین پیکسل از viewport به پایان می رسد.
این چیزی است که ما می توانیم سفارشی کنیم! را animation-range ویژگی به ما امکان می دهد تعیین کنیم که محدوده باید چه زمانی شروع/پایان شود:
.elem {
animation: fadeIn;
animation-timeline: scroll();
animation-range: cover; /* 👈 default value */
}
اگر مقدار پیش فرض را تغییر دهیم contain، فقط بعد از آماده شدن آیتم شروع به اندازه گیری می کنیم کاملا در ویوپورت:
این می تواند در مواردی که می خواهیم ببینیم مفید باشد کل انیمیشن با cover، انیمیشن شروع/پایان مییابد تا زمانی که عنصر به سختی قابل مشاهده باشد.
در اینجا یک مثال با استفاده از عناصر کشویی خارج از صفحه آورده شده است:
کد زمین بازی
style>
@keyframes slideIn {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0%);
}
}
.shape {
animation: slideIn backwards;
animation-timeline: view();
animation-range: contain;
}
style>
p>
👇 Scroll down here 👇
p>
div class="wrapper">
div class="shape">
img alt="" src="/img/shape-circle.svg" />
div>
div class="shape">
img alt="" src="/img/shape-square.svg" />
div>
div class="shape">
img alt="" src="/img/shape-triangle.svg" />
div>
div class="shape">
img alt="" src="/img/shape-star.svg" />
div>
div>
پیوند به این عنوانورودی و خروجی
علاوه بر cover و contain، نیز وجود دارد entry و exit.
به عنوان مثال:
کد زمین بازی
style>
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.box {
animation: spin linear;
animation-timeline: view();
animation-range: entry;
}
style>
p>
👇 Scroll down here 👇
p>
div class="wrapper">
div class="box">div>
div>
را entry این محدوده زمانی شروع می شود که عنصر سر خود را به داخل درگاه نمایش فرو می برد و پس از اینکه پیکسل پایینی عنصر وارد درگاه دید شود به پایان می رسد. ما می توانیم آن را به این صورت تجسم کنیم:
یک الگوی رایج طراحی این است که عناصر با ورود محو می شوند. را entry طیف وسیعی از انیمیشن ها برای این مناسب است!
کد زمین بازی
style>
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
img {
animation: fadeIn linear;
animation-timeline: view();
animation-range: entry;
}
style>
p>
👇 Scroll down here 👇
p>
img
alt="A capybara sleeping peacefully on the grass"
src="/img/capybara/a.jpg"
/>
img
alt="A capybara chewing on some vegetation, with some white birds in the background"
src="/img/capybara/b.jpg"
/>
img
alt="A baby capybara walking on top of an adult capybara"
src="/img/capybara/c.jpg"
/>
img
alt="A close-up shot of a capybara, with a human nearby"
src="/img/capybara/d.jpg"
/>
به همین ترتیب exit زمانی اعمال خواهد شد که عنصر متقاطع شود از بالا در ویوپورت، خارج شدن از دید:
ما می توانیم از هر دو استفاده کنیم entry و exit روی همان عنصر با مشخص کردن انبوه انیمیشن های فریم کلیدی ما این کار را با دادن مقادیر جدا شده با کاما برای هر ویژگی انیمیشن انجام می دهیم:
کد زمین بازی
style>
img {
animation:
fadeIn linear,
fadeOut linear;
animation-timeline: view(), view();
animation-range: entry, exit;
}
style>
p>
👇 Scroll down here 👇
p>
img
alt="A capybara sleeping peacefully on the grass"
src="/img/capybara/a.jpg"
/>
img
alt="A capybara chewing on some vegetation, with some white birds in the background"
src="/img/capybara/b.jpg"
/>
img
alt="A baby capybara walking on top of an adult capybara"
src="/img/capybara/c.jpg"
/>
img
alt="A close-up shot of a capybara, with a human nearby"
src="/img/capybara/d.jpg"
/>
پیوند به این عنوانمحدوده درصد
این یک نحو طولانی دارد که به ما امکان می دهد دقیقاً کنترل کنیم که محدوده انیمیشن کجا شروع و به پایان می رسد. این را بررسی کنید:
کد زمین بازی
style>
@keyframes slideIn {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0%);
}
}
.shape {
animation: slideIn backwards;
animation-timeline: view();
animation-range-start: cover 0%;
animation-range-end: cover 50%;
}
style>
p>
👇 Scroll down here 👇
p>
div class="wrapper">
div class="shape">
img alt="" src="/img/shape-circle.svg" />
div>
div class="shape">
img alt="" src="/img/shape-square.svg" />
div>
div class="shape">
img alt="" src="/img/shape-triangle.svg" />
div>
div class="shape">
img alt="" src="/img/shape-star.svg" />
div>
div>
در این زمین بازی من مشخص می کنم که animation-range باید دقیقاً از ابتدای محدوده “پوشش” شروع شود (درست زمانی که اولین پیکسل وارد درگاه دید می شود) و باید زمانی پایان یابد که عنصر در محدوده “پوشش” 50٪ باشد (زمانی که عنصر یک ضربه در وسط پنجره دید باشد):
این فوق العاده مفید است زیرا به این معنی است که ما می توانیم تصمیم بگیریم دقیقا زمانی که انیمیشن اسکرول شروع و پایان می یابد. ✨
همچنین روش فشرده تری برای نوشتن این مطلب وجود دارد. ما می توانیم هر چهار مقدار را به animation-range دارایی:
.shape {
animation: slideIn backwards;
animation-timeline: view();
/* Combine start/end in one property: */
animation-range: cover 0% cover 50%;
}
من شخصاً این نحو کوتاه شده را کاملاً گیج کننده می دانم و ترجیح می دهم به جای آن از آن استفاده کنم animation-range-start و animation-range-end. اما شما می توانید از هر چیزی که ترجیح می دهید استفاده کنید!
در نهایت، اگر ما حتی نیاز داریم بیشتر کنترل کنید، ما می توانیم محدوده های مختلف انیمیشن را با هم ترکیب و مطابقت دهیم:
.shape {
animation: slideIn backwards;
animation-timeline: view();
animation-range-start: contain 0%;
animation-range-end: exit 50%;
}
تا اینجا در نظر گرفتیم مشاهده کنید جدولهای زمانی پیشرفت که یک آیتم را در هنگام حرکت در پنجره مشاهده میکند. API جدول زمانی انیمیشن به ما یک چیز بدوی دیگر می دهد، پیمایش نمودارهای پیشرفت
به جای تمرکز بر یک عنصر واحد، پیمایش نمودارهای پیشرفت به پیشرفت کلی اسکرول اشاره دارد. اساساً، نقشهبرداری میکند که کاربر چقدر از کل منطقه اسکرول موجود پیمایش کرده است.
راستش نمی توانم به آن فکر کنم لحن برای این کار از موارد استفاده کنید. اصلیترین چیزی که به ذهن میرسد آن دسته از شاخصهای پیشرفت است که گاهی اوقات در وبلاگها یا وبسایتهای خبری مشاهده میکنیم:
کد زمین بازی
style>
@keyframes expand {
from {
transform: scaleX(0);
}
}
.readingIndicator {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 20px;
background: red;
transform-origin: left center;
animation: expand linear;
animation-timeline: scroll();
}
style>
div class="readingIndicator">div>
article>
h2>What is Lorem Ipsum?h2>
p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since 1966, when designers at Letraset and James Mosley, the librarian at St Bride Printing Library in London, took a 1914 Cicero translation and scrambled it to make dummy text for Letraset's Body Type sheets. It has survived not only many decades, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised thanks to these sheets and more recently with desktop publishing software including versions of Lorem Ipsum.p>
h2>Why do we use it?h2>
p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).p>
h2>Where does it come from?h2>
p>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.p>
p>Read more on a href="https://www.lipsum.com/>Lipsum.com
همانطور که در این وب سایت پیمایش می کنید، یک نوار قرمز رنگ در بالای صفحه ظاهر می شود که نشان می دهد تا چه حد در مقاله پیش رفته اید.
این یک احساس است کمی برای من کمی زائد است زیرا ما نوارهای اسکرول برای این منظور داریم. اما من نمی توانم به دلایل دیگری که بخواهید اندازه گیری کنید فکر کنم به طور کلی پیشرفت اسکرول
تاکنون، تمام نمونههایی که دیدهایم شامل اندازهگیری پیشرفت پیمایش یک عنصر به عنوان درصد و اعمال آن بر روی یک انیمیشن فریم کلیدی است. از همین عنصر.
معمولاً این همان چیزی است که ما می خواهیم، اما در برخی موارد می خواهیم عنصر we را از هم جدا کنیم اندازه گیری از عنصر ما من زنده می شوم. موقعیت اسکرول یک عنصر می تواند از انیمیشن فریم کلیدی عنصر دیگر عبور کند.
در اینجا به نظر می رسد:
کد زمین بازی
style>
main {
timeline-scope: --tracked-elem;
}
.content {
view-timeline: --tracked-elem;
}
.square {
animation: fadeIn backwards, fadeOut forwards;
animation-timeline: --tracked-elem, --tracked-elem;
animation-range: entry, exit;
}
style>
p>
👇 Scroll down here 👇
p>
main>
div class="left col">
div class="square">div>
div>
div class="right col">
p class="content">← A square appears!p>
div>
main>
در این مثال، .square از موقعیت یابی چسبنده استفاده می کند تا به هیچ وجه در قسمت دید حرکت نکند. ما در حال پیگیری هستیم .content عنصر (بند سمت راست) و با استفاده از پیشروی محو شدن آن .square داخل و خارج
در اینجا نحوه انجام آن آمده است: در موردی که می خواهیم ردیابی کنیم (.content) تنظیم کردیم view-timeline: --tracked-elem. “–tracked-elem” یک متغیر است و من می توانم نام آن را هر چه بخواهم بگذارم. این یک “خط زمانی بررسی پیشرفت” جدید ایجاد می کند. همه عناصر دیگر می توانند از طریق viewport در پیشرفت این عنصر مشترک شوند.
روی عنصری که می خواهیم متحرک سازی کنیم (.square) تنظیم کردیم animation-timeline به --tracked-elem و نه view(). بنابراین، ما از موجود استفاده می کنیم نام برد به جای اینکه تاریخچه پیشرفت خود را مقداردهی اولیه کند، تاریخچه پیشرفت را مشاهده کنید.
در اینجا یک اشتباه بزرگ وجود دارد: نام متغیرهایی که ایجاد می کنیم مانند --tracked-elem، جهانی نیستند. آنها را فقط می توان توسط عنصری که آن را ایجاد می کند مشخص کرد (.contentدر این مورد) و جانشینان آن.
این برای ما یک مشکل است. عنصری که میخواهیم متحرک کنیم، فرزند عنصری نیست که میخواهیم ردیابی کنیم:
main>
div class="left col">
div class="square">div>
div>
div class="right col">
p class="content">← A square appears!p>
div>
main>
خوشبختانه، افراد خوب در CSSWG این مشکل را پیش بینی کردند و به ما یک دریچه فرار دادند: timeline-scope دارایی
این خاصیت در اصل به ما اجازه می دهد من اعلام می کنم متغیر سطح بالاتر که سپس در جایی پایین درخت تخصیص داده می شود. در اینجا کد کامل با نظرات اضافه شده برای روشن شدن آنچه در جریان است آمده است:
style>
main {
/* Instantiate a new variable here: */
timeline-scope: --tracked-elem;
}
.content {
/*
Create a new view progress timeline and
assign it to the variable:
*/
view-timeline: --tracked-elem;
}
.square {
animation: fadeIn backwards, fadeOut forwards;
/* Reference the named view progress timeline: */
animation-timeline: --tracked-elem, --tracked-elem;
animation-range: entry, exit;
}
style>
main>
div class="left col">
div class="square">div>
div>
div class="right col">
p class="content">← A square appears!p>
div>
main>
اضافه می کنم timeline-scope اعلامیه به زیرا نزدیکترین جد مشترک است. این تضمین می کند --tracked-elem هم برای عنصری که میخواهیم ردیابی کنیم و هم برای عنصری که میخواهیم متحرک کنیم در دسترس خواهد بود.
ما چند نمونه از کارهایی که می توانیم با جدید انجام دهیم را دیده ایم animation-timeline API، اما ما فقط میتوانیم موارد زیادی را در یک پست وبلاگ پوشش دهیم. 😅
در یک سال و نیم گذشته، من بر ایجاد منبع نهایی انیمیشن متمرکز بوده ام. این مجموعه ای جامع از مهارت های مورد نیاز برای ایجاد انواع جلوه های فانتزی با استفاده از HTML/CSS، JavaScript، SVG و Canvas است.
نام دارد انیمیشن های عجیب و غریب.
این دوره به 4 بخش اصلی تقسیم شده است. بخش 3 همه چیز در مورد تعاملات پیشرفته کاربر است و ما در حال شروع هستیم بسیار عمیق تر در انیمیشن های اسکرول محور همچنین مجموعه ای از چیزهای جالب دیگر مانند View Transitions و جلوه های ردیابی مکان نما، مانند این پسر کوچک را پوشش می دهد:
من خیلی هیجان زده هستم که تمام اسرار انیمیشنم را به اشتراک بگذارم 😄. در اینجا می توانید اطلاعات بیشتری کسب کنید:
آخرین به روز رسانی از
19 ژوئن 2026



