IlmHamroh
JavaScript Full-stack/6-qism. CSS: harakat, zamonaviy CSS va dizayn13/57-dars17 daqiqa
Mundarija (32)

CSS scroll-driven animatsiyalar: animation-timeline, scroll() va view()

Qisqacha: Scroll-driven animatsiyada @keyframes soniyalar bo'yicha emas, skroll bo'yicha o'ynaydi: skroll 0% da — animatsiya boshida, 100% da — oxirida. Xususiyat animation-timeline: scroll() animatsiyani skroll konteyner holatiga, view() esa elementning ekranda ko'rinish darajasiga bog'laydi. Bu imkoniyat hali Baseline emas — Firefox'da yo'q, shuning uchun uni @supports (animation-timeline: view()) ichida, bezak sifatida yozing.

Bu darsda

  • Vaqt va skroll "timeline"lari farqini tushuntira olasiz.
  • scroll() bilan o'qish progress chizig'ini, view() bilan paydo bo'luvchi kartalarni JavaScript'siz yasaysiz.
  • animation-range bilan animatsiya skrollning qaysi qismida o'ynashini belgilaysiz.
  • Nomli timeline (scroll-timeline, view-timeline, timeline-scope) nima uchun kerakligini bilasiz.
  • @supports va prefers-reduced-motion bilan xavfsiz zaxira yozasiz.

Oldin bilishingiz kerak: @keyframes va animation, Skroll xulqi va scroll-snap, Brauzer mosligi va @supports, prefers-reduced-motion.

1. Nega bu kerak?

O'tgan darsda scroll snap bilan skrollni "magnitli" qildik. Endi skrollga animatsiya bog'laymiz.

«Bahor» blogida oshpaz Rustam akaning uzun maqolasi bor: "Oshni qanday damlash kerak". U ikki narsani so'radi:

  1. Sahifa tepasida ingichka chiziq bo'lsin — o'quvchi maqolaning qancha qismini o'qiganini ko'rsin. Xuddi yuklab olish ko'rsatkichi kabi.
  2. Retsept bosqichlari sahifaga "suzib" kirsin — o'quvchi skroll qilganda har bosqich asta paydo bo'lsin.

Bir necha yil oldin buning yagona yo'li JavaScript edi. Skroll har o'zgarganda kod ishga tushardi, foizni hisoblab, CSS o'zgaruvchisiga yozardi (CSS o'zgaruvchilari 2 darsidagi usul). Paydo bo'lish uchun esa IntersectionObserver kerak edi — "element ekranga kirdimi?" deb kuzatadigan JavaScript vositasi (uni keyinroq o'rganamiz, hozir bilish shart emas). Kod ko'p, telefonda sekinlashish xavfi bor.

Bugun buni bir necha qator CSS qiladi. Kalit so'z — timeline (vaqt chizig'i).

2. Timeline: animatsiyaning "soati"

2.1 Vaqt o'rniga skroll

@keyframes darsida har animatsiyaning bitta "soati" bor edi — vaqt. animation-duration: 2s degani: 0 soniyada from, 2 soniyada to. Bu soat hujjat timeline'i (document timeline) deyiladi va u o'z-o'zidan yuradi.

Scroll-driven animatsiya (skrollga bog'langan animatsiya) soatni almashtiradi. Endi animatsiya o'rnini soniyalar emas, skroll o'rni belgilaydi:

flowchart LR
  subgraph V["Oddiy animatsiya"]
    direction TB
    V1["0 s"] --> V2["1 s"] --> V3["2 s"]
  end
  subgraph S["Scroll-driven"]
    direction TB
    S1["Skroll 0%"] --> S2["Skroll 50%"] --> S3["Skroll 100%"]
  end
  V3 --> K["@keyframes: from → to"]
  S3 --> K

Nimaga qarang: @keyframes bir xil qoladi, faqat unga "vaqt"ni kim berishi o'zgaradi. Foydalanuvchi skrollni to'xtatsa, animatsiya ham to'xtaydi. Orqaga qaytarsa — animatsiya ham orqaga o'ynaydi. Xuddi video pleyerdagi slayderni barmoq bilan surish kabi: kadrni siz boshqarasiz, soat emas.

2.2 Ikki xil skroll soati

Funksiya Nimani o'lchaydi Qachon
scroll() Konteyner qancha skroll qilingani Progress chizig'i, fon o'zgarishi
view() Element konteyner oynasida qanchalik ko'ringani Paydo bo'lish, parallaks

Birinchisi — butun sahifaning holati, ikkinchisi — bitta elementning "sahna"dagi holati.

3. scroll(): o'qish progress chizig'i

3.1 Birinchi misol

html
<style>
  .maqola {
    height: 240px;
    overflow-y: auto;
    border: 2px solid #2f6b3a;
    background-color: #fffaf0;
    font-family: system-ui, sans-serif;
  }
  .progress {
    position: sticky;
    top: 0;
    height: 6px;
    background-color: #b4461a;
    transform-origin: left;
    animation: tolish linear both;
    animation-timeline: scroll();
  }
  @keyframes tolish {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
  }
  .maqola > :not(.progress) {
    padding-inline: 16px;
  }
</style>
<article class="maqola" tabindex="0" aria-label="Maqola">
  <div class="progress"></div>
  <h2>Oshni qanday damlash kerak</h2>
  <p>1. Qozonni qizdirib, yog'ni tutunga qadar qizdiring.</p>
  <p>2. Go'shtni qo'shib, qizarguncha qovuring.</p>
  <p>3. Piyoz va sabzini somon qilib to'g'rab soling.</p>
  <p>4. Zirvak 20 daqiqa qaynasin, tuz va ziravor.</p>
  <p>5. Guruchni yuvib, tekis qilib soling.</p>
  <p>6. Suv guruchdan bir barmoq yuqori bo'lsin.</p>
  <p>7. Suv singgach, guruchni tepa qilib yig'ing.</p>
  <p>8. Qopqog'ini yopib, 20 daqiqa damlang.</p>
  <p>Yoqimli ishtaha! Rustam aka, «Bahor».</p>
</article>

Nimaga qarang: ramka ichida pastga skroll qiling — tepadagi qizg'ish chiziq chapdan o'ngga o'sadi. Yarmiga kelganda chiziq ham yarmida. Eng pastda — to'liq. Tepaga qaytsangiz, chiziq qisqaradi. Bitta ham JavaScript qatori yo'q.

Chrome'da o'lchadik: skroll 25% da transform — scaleX(0.25) atrofida, 50% da — aynan 0.5, 100% da — 1.

3.2 Kod qatorma-qator

  • animation: tolish linear both; — odatdagi animatsiya, lekin davomiylik yo'q. Scroll-driven animatsiyada soniya ma'nosiz: davomiylikni skroll uzunligi belgilaydi. Yozilmasa, animation-duration ning qiymati auto bo'ladi.
  • linear — skroll bilan to'g'ri proporsiya: 10% skroll → 10% chiziq. ease bersangiz, chiziq boshida sekin, o'rtada tez o'sardi.
  • both — skroll boshidan oldin va oxiridan keyin ham chetki holatlar saqlansin (@keyframes darsidagi animation-fill-mode).
  • animation-timeline: scroll(); — "soat — eng yaqin skroll konteyner".
  • transform: scaleX() — width o'rniga. Rendering pipeline darsidan eslang: transform layout'ni qayta hisoblatmaydi. Chrome bunday scroll-driven animatsiyani to'g'ridan-to'g'ri kompozitorda, sahifaning boshqa ishlaridan alohida o'ynata oladi — sahifa og'ir bo'lsa ham chiziq silliq qoladi.

3.3 scroll() argumentlari

scroll() ikkita ixtiyoriy argument oladi: qaysi konteyner va qaysi o'q.

Yozuv Ma'nosi
scroll() Eng yaqin skroll qiluvchi ota, vertikal (sukut)
scroll(root) Butun sahifa skrolli (html)
scroll(self) Elementning o'z skrolli
scroll(nearest x) Eng yaqin ota, gorizontal o'q

Haqiqiy blog sahifasida chiziq butun sahifa skrolliga bog'lanadi: animation-timeline: scroll(root); va position: fixed. Natija oynasida biz ichki ramka ishlatdik — oyna balandligi o'zgaruvchan, shuning uchun sahifa skrolli ishonchli emas.

Diqqat: animation qisqa yozuvi animation-timeline ni ham o'z ichiga oladi va uni auto ga qaytaradi (CSS nima darsidagi qisqa yozuv tuzog'i). Shuning uchun animation-timeline doim animation dan keyin yozilsin. Teskari tartibda Chrome'da tekshirdik: animation-timeline auto bo'lib qoldi, chiziq skrollga qaramay birdan to'liq chizildi.

Tekshirib ko'ring: Progress chizig'ida animation: tolish 3s linear both; yozildi — davomiylik 3 soniya. Nima bo'ladi?

Javob

Hech narsa buzilmaydi, agar animation-timeline: scroll() undan keyin yozilgan bo'lsa. Scroll-driven animatsiyada vaqt birligidagi davomiylik e'tiborga olinmaydi — "vaqt"ni skroll beradi. Lekin bu kodni o'qigan odam chalkashadi. Shuning uchun davomiylik yozilmaydi yoki auto qoldiriladi.

4. view(): element sahnaga chiqishi

4.1 Paydo bo'luvchi bosqichlar

view() butun konteynerga emas, elementning o'ziga qaraydi: u konteyner oynasiga (scrollport) kirib kelyaptimi, o'rtadami, chiqib ketyaptimi.

html
<style>
  .retsept {
    height: 240px;
    overflow-y: auto;
    padding-inline: 16px;
    border: 2px solid #2f6b3a;
    font-family: system-ui, sans-serif;
  }
  .bosqich {
    margin-block: 48px;
    padding: 16px;
    border-radius: 12px;
    background-color: #e8f1ec;
    animation: paydo linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 100%;
  }
  @keyframes paydo {
    from { opacity: 0; transform: translateY(24px); }
    to { opacity: 1; transform: none; }
  }
</style>
<div class="retsept" tabindex="0" aria-label="Retsept">
  <p>Pastga skroll qiling.</p>
  <div class="bosqich">1. Qozonni qizdiring</div>
  <div class="bosqich">2. Go'shtni qovuring</div>
  <div class="bosqich">3. Sabzini soling</div>
  <div class="bosqich">4. Guruchni soling</div>
  <div class="bosqich">5. Damlang</div>
</div>

Nimaga qarang: har bosqich ramkaning pastki chetidan kirib kela boshlaganda shaffof va biroz pastda. U to'liq ko'ringan paytda — butunlay aniq va joyida. Orqaga skroll qilsangiz, bosqichlar yana xiralashib pastga tushadi. Ochilgan zahoti birinchi bosqich to'liq ko'rinadi, ikkinchisi pastki chetda endi kirib kelmoqda, qolganlari hali "sahnaga chiqmagan".

4.2 animation-range: skrollning qaysi bo'lagida

view() elementning butun yo'lini o'lchaydi: pastki chetdan kira boshlagan paytdan tepadan butunlay chiqib ketguncha. animation-range shu yo'lning qaysi bo'lagida animatsiya o'ynashini aytadi. Asosiy nomlar:

Nom Qaysi bo'lak
entry Element kira boshlaganidan to'liq ko'ringuncha
exit Chiqa boshlaganidan butunlay chiqib ketguncha
contain Element to'liq ko'rinib turgan butun vaqt
cover Butun yo'l: birinchi pikseldan oxirgisigacha (sukut)

Elementning yo'li va bo'laklari ketma-ket shunday:

flowchart LR
  A["Pastdan kira<br/>boshladi"] -->|entry| B["To'liq<br/>ko'rindi"]
  B -->|contain| C["Tepadan chiqa<br/>boshladi"]
  C -->|exit| D["Butunlay<br/>chiqdi"]

Nimaga qarang: strelkalar ustidagi nomlar — yo'lning bo'laklari. cover esa A dan D gacha bo'lgan butun yo'l.

Maslahat: Yuqoridagi retsept natijasida "Tahrirlab ko'rish" ni bosing va entry 0% entry 100% o'rniga cover yozing. Endi animatsiya butun yo'lga cho'zildi: ramka o'rtasida bosqich hali yarim shaffof, to'liq aniq bo'lishiga esa tepadan chiqib ketayotganda yetadi. Paydo bo'lish uchun entry nega qulayroq ekani shunda ko'rinadi.

Har nom yonidagi foiz — shu bo'lakning qayeri: entry 0% — kirish boshlanishi, entry 100% — kirish tugashi. Chrome'da o'lchadik: 200 pikselli oynada 100 pikselli element. Uning yuqori cheti oyna tepasidan 200 pikselda (endigina kirmoqda) — opacity 0; 150 pikselda — 0.5; 100 pikselda (to'liq kirdi) — 1.

Aralashtirish ham mumkin: animation-range: entry 25% cover 50% — "kirishning choragidan boshlab, butun yo'lning yarmigacha".

Tekshirib ko'ring: Bosqich ekrandan tepaga chiqib ketayotganda xiralashishi kerak. animation-range qanday bo'ladi?

Javob

animation-range: exit 0% exit 100%; — chiqish boshlanganidan tugaguncha. @keyframes ham teskari bo'lsin: from { opacity: 1; } va to { opacity: 0; }. Kirish va chiqishni bitta elementda xohlasangiz — ikkita animatsiya, har biriga o'z animation-range i vergul bilan yoziladi.

5. Nomli timeline

5.1 Qachon kerak

scroll() va view() "anonim": ular eng yaqin ota konteynerni o'zlari topadi. Lekin ba'zan animatsiya qilinadigan element skroll konteynerning ichida emas. Masalan, gorizontal lenta ostida "qancha surildi" chizig'i: chiziq lentaning qo'shnisi, bolasi emas.

Bunda timeline'ga nom beriladi:

  • Konteynerga: scroll-timeline: --lenta x; — "mening gorizontal skrollim --lenta deb atalsin". To'liq yozuvi — scroll-timeline-name va scroll-timeline-axis.
  • Animatsiya qilinadigan elementga: animation-timeline: --lenta;.
  • Umumiy otaga: timeline-scope: --lenta; — "bu nom butun ichki daraxtimga ko'rinsin". Usiz nom faqat konteynerning o'z bolalariga ko'rinadi.

Nom — CSS o'zgaruvchilari kabi -- bilan boshlanadi, lekin o'zgaruvchi emas.

html
<style>
  .blok {
    timeline-scope: --lenta;
    font-family: system-ui, sans-serif;
  }
  .lenta {
    display: flex;
    gap: 12px;
    overflow-x: auto;
    scroll-timeline: --lenta x;
    scroll-snap-type: x mandatory;
  }
  .taom-karta {
    flex: 0 0 70%;
    padding: 24px 16px;
    border-radius: 12px;
    background-color: #e8f1ec;
    scroll-snap-align: start;
  }
  .korsatkich {
    height: 4px;
    margin-block-start: 8px;
    background-color: #2f6b3a;
    transform-origin: left;
    animation: tolish linear both;
    animation-timeline: --lenta;
  }
  @keyframes tolish {
    from { transform: scaleX(0.25); }
    to { transform: scaleX(1); }
  }
</style>
<div class="blok">
  <div class="lenta" tabindex="0" aria-label="Taomlar">
    <article class="taom-karta">Osh</article>
    <article class="taom-karta">Lag'mon</article>
    <article class="taom-karta">Manti</article>
    <article class="taom-karta">Ko'k choy</article>
  </div>
  <div class="korsatkich"></div>
</div>

Nimaga qarang: lentani yon tomonga suring — ostidagi yashil chiziq chorakdan to'liqqa qarab o'sadi. Chiziq lentaning ichida emas, yonida. Uni --lenta nomi va otadagi timeline-scope bog'ladi. scaleX(0.25) dan boshlandi: birinchi kartada ham chiziq ko'rinsin, "4 tadan 1" degan ma'noda.

Chrome'da tekshirdik: xuddi shunday chiziqni .blok dan tashqariga qo'ysak, u timeline'ni topmaydi va skrollga javob bermaydi.

5.2 view-timeline

view() ning nomli varianti — view-timeline: --karta; elementning o'ziga yoziladi. Keyin uning ichidagi istalgan bola animation-timeline: --karta bilan otaning ko'rinishiga bog'lanadi. Masalan, karta kirib kelganda uning ichidagi rasm bir oz kattalashadi, sarlavha esa boshqacha harakatlanadi — ikkalasi ham kartaning bitta "sahna" holatiga qaraydi.

6. Harakatni kamaytirish va zaxira

6.1 Baseline holati

Baseline ma'lumotiga ko'ra scroll-driven animatsiyalar hali Baseline emas. Chrome va Edge'da 115-versiyadan, Safari'da 26-versiyadan bor. Firefox'da esa yo'q: MDN'ning brauzerlar jadvalida u faqat sinov (Nightly) versiyasida ko'rsatilgan, oddiy Firefox'da ishlamaydi. timeline-scope va animation-duration: auto ham shu holatda.

Demak, Firefox foydalanuvchisi uchun dars kodimiz nima qiladi? animation-timeline qatorini tushunmaydi va tashlaydi. Qoladi: animation: paydo linear both — davomiyligi yo'q, ya'ni 0 soniya. both sabab element darhol oxirgi holatda: opacity: 1. Kartalar ko'rinadi, faqat animatsiyasiz. Bu omadli holat. Agar davomiylik yozilgan bo'lsa (animation: paydo 1s ...), Firefox'da kartalar sahifa ochilganda bir marta, skrollga qaramay "paydo bo'lib" o'tardi.

6.2 Xavfsiz andoza

Omadga tayanmaymiz. Scroll-driven kodni ikki shartga o'raymiz:

css
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .bosqich {
      animation: paydo linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 100%;
    }
  }
}
  • @supports (Brauzer mosligi) — brauzer animation-timeline: view() ni tushunmasa, blok umuman qo'llanmaydi. Kartalar oddiy, harakatsiz.
  • prefers-reduced-motion (Harakat etikasi) — parallaks va "suzib kirish" vestibulyar muammosi bor odamga eng ko'p zarar beradigan harakatlar qatorida. Harakat o'chirilgan bo'lsa — kartalar joyida.

Progress chizig'i esa o'zi ma'lumot beradi, harakat kamaytirilganda ham foydali. Uni prefers-reduced-motion dan tashqarida qoldirsa bo'ladi — u foydalanuvchi skrolliga ergashadi, o'zi harakatlanmaydi.

Maslahat: Zaxirani tekshirish uchun Firefox shart emas. Chrome'da @supports shartiga ataylab xato nom yozing (masalan, animation-timelin) — sahifa Firefox'dagi kabi animatsiyasiz ko'rinadi.

6.3 Parallaks — ehtiyotkorlik bilan

Parallaks — orqa fon oldingi kontentdan sekinroq harakatlanishi, chuqurlik hissini beradi. view() bilan:

css
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .bosh-rasm img {
      animation: suzish linear both;
      animation-timeline: view();
    }
  }
}

@keyframes suzish {
  from { transform: translateY(-15%); }
  to { transform: translateY(15%); }
}

Rasm o'z konteyneri ichida skroll yo'nalishiga qarshi biroz suriladi va sekinroq harakatlanayotgandek ko'rinadi. Konteynerga overflow: hidden bering, aks holda rasm chetidan chiqib ketadi. Parallaksni tejab ishlating: sahifada bitta-ikkita joy yetadi.

7. Ko'p uchraydigan xatolar

7.1 animation qisqa yozuvi keyin yozilgan

css
.progress {
  animation-timeline: scroll();
  animation: tolish linear both;
}

Qisqa yozuv animation-timeline ni auto ga qaytardi. Tuzatish: avval animation, keyin animation-timeline.

7.2 Skroll konteyner topilmadi

scroll() eng yaqin skroll qiluvchi otani qidiradi. Ota konteynerda overflow: auto yo'q yoki kontent sig'ib turibdi (skroll yo'q) — animatsiya harakatlanmaydi. Tuzatish: DevTools'da konteynerni tekshiring: haqiqatan skroll bormi? Butun sahifa uchun — scroll(root).

7.3 view() elementi overflow: hidden ichida

Karta overflow: hidden li o'ramda turibdi. overflow: hidden ham skroll konteyner yaratadi — view() aynan shuni "eng yaqin" deb oladi va hech qachon o'zgarmaydigan holatni o'lchaydi. Chrome'da tekshirdik: bunday karta skrollga qaramay doim to'liq ko'rinib turdi. Tuzatish: o'ramga overflow: clip bering (Overflow darsi): u kesadi, lekin skroll konteyner yaratmaydi.

7.4 Kontent zaxirasiz yashirin qoldi

@keyframes dagi from { opacity: 0 } ni animatsiyadan tashqariga, oddiy qoidaga ko'chirishdi: .bosqich { opacity: 0 }. Firefox'da animatsiya yo'q — kartalar abadiy ko'rinmas. Tuzatish: boshlang'ich yashirin holat faqat @keyframes ichida yoki @supports ichida bo'lsin.

8. Mashqlar

Mashqlarni kurs/mashqlar/06/ papkasida bajaring.

1-mashq (oson): fon rangi skrollga bog'liq

«Bahor» menyusi 300 pikselli ichki skroll konteynerda. Konteyner foni skroll boshida qaymoq rang (#fffaf0), oxirida och yashil (#e8f1ec) bo'lsin — skroll bilan silliq o'zgarsin.

Ishora: animatsiya konteynerning o'zida — u o'z skrolliga qaraydi: scroll(self).

Yechim
html
<style>
  .menyu-oyna {
    height: 200px;
    overflow-y: auto;
    padding-inline: 16px;
    font-family: system-ui, sans-serif;
    animation: fon linear both;
    animation-timeline: scroll(self);
  }
  @keyframes fon {
    from { background-color: #fffaf0; }
    to { background-color: #e8f1ec; }
  }
</style>
<div class="menyu-oyna" tabindex="0" aria-label="Menyu">
  <p>Osh — 35 000 so'm</p><p>Lag'mon — 28 000 so'm</p>
  <p>Manti — 30 000 so'm</p><p>Ko'k choy — 5 000 so'm</p>
  <p>Non — 4 000 so'm</p><p>Somsa — 8 000 so'm</p>
  <p>Shashlik — 18 000 so'm</p><p>Salat — 12 000 so'm</p>
</div>

scroll(self) — "o'zimning skrollim". scroll() yozilsa, brauzer konteynerning otasidagi skrollni qidirardi va bu yerda topmasdi. background-color — arzon xususiyat emas (har kadrda qayta bo'yaladi), lekin kichik konteynerda muammo yo'q.

2-mashq (o'rta): bosqichlar chapdan kirsin

Retsept misolini o'zgartiring:

  1. Bosqichlar pastdan emas, chapdan kirsin (translateX(-40px) dan).
  2. Animatsiya kirishning yarmida tugasin.
  3. Kod faqat qo'llab-quvvatlaydigan brauzerda va harakat kamaytirilmagan holatda ishlasin.

Ishora: «Xavfsiz andoza» bo'limi. entry 0% entry 50%.

Yechim
css
@keyframes chapdan {
  from { opacity: 0; transform: translateX(-40px); }
  to { opacity: 1; transform: none; }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .bosqich {
      animation: chapdan linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 50%;
    }
  }
}

@keyframes shartdan tashqarida qolishi mumkin: uni hech kim ishlatmasa, u hech narsaga ta'sir qilmaydi. entry 50% — element yarmigacha kirganda animatsiya tugaydi, keyin to'liq ko'rinadi. Retsept konteyneri ichida bosqichlar chap chetdan 40 piksel tashqarida boshlaydi — konteynerning chap padding i (16px) yetmasa, gorizontal skroll paydo bo'lishi mumkin. Buni oldini olish uchun konteynerga overflow-x: clip qo'shing.

3-mashq (qiyin): kirish va chiqish

Har bosqich kirib kelganda paydo bo'lsin, tepadan chiqib ketayotganda esa xiralashib kichraysin (scale(0.9)). Bitta elementda ikki animatsiya.

Ishora: animation va animation-range da vergul bilan ikki qiymat — birinchisi birinchi animatsiyaga, ikkinchisi ikkinchisiga (@keyframes darsidagi bir nechta animatsiya).

Yechim
html
<style>
  .retsept {
    height: 240px;
    overflow-y: auto;
    padding-inline: 16px;
    border: 2px solid #2f6b3a;
    font-family: system-ui, sans-serif;
  }
  .bosqich {
    margin-block: 48px;
    padding: 16px;
    border-radius: 12px;
    background-color: #e8f1ec;
  }
  @keyframes kir {
    from { opacity: 0; transform: translateY(24px); }
  }
  @keyframes chiq {
    to { opacity: 0; transform: scale(0.9); }
  }
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .bosqich {
        animation: kir linear both, chiq linear both;
        animation-timeline: view(), view();
        animation-range: entry, exit;
      }
    }
  }
</style>
<div class="retsept" tabindex="0" aria-label="Retsept">
  <p>Pastga skroll qiling.</p>
  <div class="bosqich">1. Qozonni qizdiring</div>
  <div class="bosqich">2. Go'shtni qovuring</div>
  <div class="bosqich">3. Sabzini soling</div>
  <div class="bosqich">4. Guruchni soling</div>
  <div class="bosqich">5. Damlang</div>
</div>
  • @keyframes kir da faqat from, chiq da faqat to — qolgan chet elementning o'z holati (opacity: 1, transform: none). Ikki animatsiya bir-biriga xalaqit bermaydi.
  • animation-range: entry, exit — nomning o'zi entry 0% entry 100% degani.
  • animation-timeline: view(), view() — har animatsiyaga o'z timeline'i. Bitta view() yozilsa ham ikkalasiga qo'llanadi, lekin aniq yozgan tushunarliroq.

4-mashq: Portfolio qadami — loyiha kartalari "sahnaga chiqsin"

Portfolio'ning loyihalar sahifasida kartalar (.loyihalar > article) skroll bilan paydo bo'lsin:

  1. Karta pastdan kira boshlaganda — shaffof va 16px pastda, kirishning 60% ida — joyida.
  2. Faqat qo'llab-quvvatlaydigan brauzerda va harakat kamaytirilmagan bo'lsa.
  3. Chrome'da (DevTools'da ekran enini 360 qilib) va Firefox'da tekshiring: Firefox'da kartalar oddiy ko'rinishi kerak. So'ng npm run check.

Ishora: timeline — sahifa skrolli, kartalar esa uning bolalari, shuning uchun anonim view() yetarli. Kompyuterda kartalar birinchi ekranga sig'ib qolishi mumkin — ular allaqachon "kirgan" bo'ladi va animatsiya ko'rinmaydi. Bu to'g'ri xulq.

Yechim

Avval eski animatsiya. Micro-interaktsiyalar darsida kartalar sahifa ochilganda kirish stagger'i bilan chiqqan edi. Ikkala animatsiya bitta animation xususiyatiga yoziladi — kaskadda faqat bittasi yutadi, qaysi biri — fayldagi tartibga bog'liq. Kartaga ikki xil "kirish" kerak emas: skroll bilan paydo bo'lish uni almashtiradi. O'sha darsdagi karta qoidalarini o'chiring: --i raqamlari, @keyframes kirish va harakat blokidagi .loyihalar > article { animation: ... }. Tugmaning bosilish javobi qoladi.

Keyin asosiy.css ning «Komponentlar» qismidagi kartalar qoidalaridan keyin:

css
/* Kartalar skroll bilan paydo bo'ladi (Scroll-driven animatsiyalar
   darsi). Firefox'da va harakat kamaytirilganda — oddiy */
@keyframes karta-kir {
  from {
    opacity: 0;
    transform: translateY(1rem);
  }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .loyihalar > article {
      animation: karta-kir linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
  }
}
  • @keyframes da faqat from — oxirgi holat kartaning o'z stili. @property darsidagi hover chegarasiga ham xalaqit bermaydi: u --karta-chegara ni o'zgartiradi, bu esa opacity va transform ni.
  • Kartalar subgrid'da (subgrid) — transform va opacity layout'ga tegmaydi, qatorlar tekisligi buzilmaydi.
  • 1rem — portfolio px o'rniga rem ishlatadi (O'lcham birliklari).

Telefonda ikkinchi va uchinchi karta pastdan suzib kiradi. Kompyuterda ikki ustunli to'rda kartalar birinchi ekranda — ular sahifa ochilganda to'liq ko'rinadi.

9. Real ishda

  • Blog va hujjat saytlari — o'qish progress chizig'i. Ilgari har biri scroll hodisasi va JavaScript bilan edi.
  • Landing sahifalar — bo'limlar paydo bo'lishi, raqamlar sanalishi, parallaks. Ko'pincha GSAP ScrollTrigger yoki Motion kabi JS kutubxonalari bilan yoziladi (Motion IV darsida) — CSS varianti yengilroq, lekin hozircha Firefox'da yo'q.
  • Samaradorlik — CSS'dagi scroll-driven animatsiya transform va opacity bilan asosiy oqimdan tashqarida ishlashi mumkin, JavaScript'dagi scroll tinglovchisi esa har kadrda asosiy oqimni band qiladi.
  • Intervyuda: "Skroll progress chizig'ini JavaScript'siz qanday qilasiz va eski brauzer uchun nima qilasiz?" — animation-timeline: scroll() va @supports.

Xulosa

  • Scroll-driven animatsiya — oddiy @keyframes, lekin soatni skroll beradi: animation-timeline.
  • scroll() — konteyner qancha skroll qilingani (progress chizig'i); view() — element oynada qanchalik ko'ringani (paydo bo'lish).
  • animation-range — entry, exit, contain, cover va foizlar bilan animatsiya bo'lagini tanlaydi.
  • animation-timeline doim animation qisqa yozuvidan keyin; davomiylik yozilmaydi (auto).
  • Qo'shni elementga bog'lash — nomli timeline va timeline-scope.
  • Baseline emas (Firefox'da yo'q): @supports (animation-timeline: view()) + prefers-reduced-motion ichida, bezak sifatida.

Keyingi dars: View Transitions — ikki holat yoki hatto ikki sahifa orasida brauzer o'zi yasaydigan silliq o'tishlarni o'rganamiz.

Manbalar

  • MDN: "CSS scroll-driven animations", "animation-timeline", "scroll()", "view()", "animation-range", "timeline-scope" — developer.mozilla.org
  • W3C: "Scroll-driven Animations" — w3.org/TR/scroll-animations-1
  • web-features (Baseline ma'lumotlari): "scroll-driven-animations" — github.com/web-platform-dx/web-features
  • MDN browser-compat-data: css.properties.animation-timeline (Firefox — preview) — github.com/mdn/browser-compat-data
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
CSS scroll-driven animatsiyalar: animation-timeline, scroll() va view() — IlmHamroh