IlmHamroh
JavaScript Full-stack/20-qism. React: UI kutubxonalar va animatsiya28/30-dars19 daqiqa
Mundarija (30)

GSAP React'da: timeline, ScrollTrigger, useGSAP va SplitText

Qisqacha: GSAP — murakkab animatsiya "ssenariylari" uchun kutubxona. gsap.to(nishon, { x: 100 }) — bitta harakat (tween). gsap.timeline() — ko'p harakatni vaqt o'qida aniq joylash: "sarlavhadan 0.2 s oldin taomlar boshlansin". ScrollTrigger animatsiyani scroll'ga bog'laydi: scrub — scroll bilan birga, pin — elementni "mixlab" qo'yadi. React'da useGSAP hooki animatsiyalarni komponent bilan birga tozalaydi. Narxi — +43.4 KB gzip. Shuning uchun GSAP marketing sahifalari va "scroll hikoyalari" uchun, ish vositasi ilovalari uchun emas.

Bu darsda

  • gsap.to, from, fromTo, set va stagger bilan harakat yozasiz.
  • Timeline'da joylashuv parametrini ("<", "-=0.2", yorliqlar) o'qiysiz va hisoblaysiz.
  • useGSAP bilan React'da animatsiyani to'g'ri tozalaysiz, contextSafe ni bilasiz.
  • ScrollTrigger bilan scrub, pin, toggleActions va SplitText bilan harf animatsiyasini yasaysiz.
  • GSAP'ning narxi, CSP bilan mosligi va Motion'dan qachon afzalligini o'lchov bilan aytasiz.

Oldin bilishingiz kerak: Motion IV: scroll animatsiya va motion values, Motion II: variants va stagger, useRef II: DOM elementlari, Effekt hayot sikli va StrictMode, Scroll-driven animatsiyalar.

1. Nega bu kerak?

«Bahor» 10 yoshga to'ldi. Jasur aka bayram sahifasini xohlaydi. Sarlavha harfma-harf "yozilsin", taomlar ketma-ket chiqsin, narxlar "sakrab" paydo bo'lsin. Pastroqda "Osh qanday tayyorlanadi" bo'limi: foydalanuvchi aylantirgan sari qozon joyida turib qolsin, uning yonidan bosqichlar (zirvak, guruch, dam) birin-ketin o'tsin.

Buni Motion bilan ham yozsa bo'ladi. Lekin 15 ta harakat bir-biriga bog'langan: biri tugashidan 0.2 s oldin keyingisi, biri boshqasi bilan bir vaqtda. Har birining delay ini qo'lda hisoblash — bitta harakat o'zgarsa, qolganlari ham qayta hisoblanadi.

Hayotiy o'xshatish: to'y dasturi. Boshlovchining qog'ozida: "19:00 — kelin-kuyov kiradi, kirishidan 10 soniya oldin musiqa boshlanadi, ular o'tirgach — birinchi tilak". Vaqtlar bir-biriga nisbatan yozilgan. Bitta tadbir cho'zilsa, qolganlari o'zi suriladi. GSAP timeline — aynan shu dastur.

GSAP (GreenSock Animation Platform) — ko'p yillik JavaScript animatsiya kutubxonasi. U framework'ga bog'liq emas: React, Vue yoki oddiy JS bilan ishlaydi. Bugun (3.15.0) ScrollTrigger, SplitText, MorphSVG kabi barcha plaginlar ochiq npm paketining ichida. README'sida: Webflow tufayli GSAP to'liq bepul, tijoriy loyihada ham. Ilgari bonus plaginlar faqat pullik Club GSAP a'zolariga edi. Litsenziyasi esa MIT emas: package.json dagi license maydonida — "Standard 'no charge' license: https://gsap.com/standard-license.". Bepul, lekin o'z shartlari bor.

Natija oynasida GSAP yo'q. Shuning uchun bu darsdagi GSAP kodi o'zingizning loyihangiz uchun (npm i -E gsap@3.15.0 @gsap/react@2.1.2). Natijalar — kanon tajribasi va biz Chrome 155 da o'tkazgan qo'shimcha o'lchovlardan.

2. Tween: bitta harakat

2.1 to, from, fromTo, set

GSAP'da bitta harakat — tween (inglizcha "in-between" — "orasidagi"). Boshlang'ich va oxirgi holat orasidagi barcha oraliq kadrlarni kutubxona hisoblaydi.

js
import gsap from "gsap";

// hozirgi holatdan → shu qiymatlarga
gsap.to(".narx", { x: 100, duration: 0.5, ease: "power2.out" });

// shu qiymatlardan → hozirgi holatga (paydo bo'lish)
gsap.from(".taom", { opacity: 0, y: 20, duration: 0.4 });

// ikkalasini aniq berish
gsap.fromTo(".chiziq", { scaleX: 0 }, { scaleX: 1, duration: 1 });

// darhol, animatsiyasiz
gsap.set(".modal", { autoAlpha: 0 });
  • Nishon — CSS selektor, element, elementlar massivi yoki istalgan JS obyekti. gsap.to(hisob, { qiymat: 1250 }) — oddiy obyektning sonini ham animatsiya qiladi (hisoblagich uchun).
  • x, y, rotation, scale — transform qisqartmalari (Motion'dagi x, y kabi). autoAlpha — opacity + visibility: hidden (0 da element bosilmaydi ham).
  • ease — tezlanish egri chizig'i: "power2.out" (oxirida sekinlashadi), "back.out(1.7)" (biroz o'tib qaytadi), "elastic.out", "none" (tekis).
  • duration — soniyada: 0.5 — yarim soniya. CSS va WAAPI'dagi millisekund emas.

2.2 stagger

Bir nechta nishon bo'lsa, stagger: 0.1 — har biri oldingisidan 0.1 s keyin boshlanadi. Motion II dagi delayChildren: stagger(…) ning o'zi:

js
gsap.from(".taom", {
  opacity: 0,
  x: -30,
  duration: 0.4,
  stagger: 0.1,
});

Uchta taom: 0.0, 0.1, 0.2 s da boshlanadi, oxirgisi 0.6 s da tugaydi.

3. Timeline: harakatlarni vaqt o'qida joylash

3.1 Zanjir va joylashuv parametri

gsap.timeline() — tweenlar "konteyneri". Standartda har yangi tween oldingisi tugaganda boshlanadi. Uchinchi argument — joylashuv parametri (position parameter) — buni o'zgartiradi:

js
const tl = gsap.timeline({
  defaults: { duration: 0.4, ease: "power2.out" },
});
tl.from(".sarlavha", { y: -20, opacity: 0 })
  .from(".taom", { x: -30, opacity: 0, stagger: 0.1 }, "-=0.2")
  .addLabel("narx")
  .from(".narx", { scale: 0.8, opacity: 0 }, "narx");
Qiymat Ma'nosi
(yo'q) timeline oxiriga
1 aniq 1-soniyada
"-=0.2" / "+=0.5" oxiridan 0.2 s oldin / 0.5 s keyin
"<" / ">" oldingi tween boshida / oxirida
"<0.1" oldingi tween boshidan 0.1 s keyin
"narx", "narx+=0.2" addLabel bilan qo'yilgan yorliqqa nisbatan

defaults — barcha tweenlar uchun umumiy sozlama: har birida duration yozish shart emas.

3.2 Hisoblab ko'ramiz

Yuqoridagi timeline qachon nima qiladi? Uni o'zimiz hisoblaymiz — GSAP'ning joylashuv qoidasining soddalashtirilgan modeli:

js
// GSAP joylashuv parametri — soddalashtirilgan model
const fmt = (n) => n.toFixed(2);

function place(steps) {
  let end = 0; // timeline oxiri
  let prevStart = 0; // oldingi tween boshi
  const labels = {};
  const lines = [];
  for (const s of steps) {
    const at = s.at;
    if (s.label) {
      labels[s.label] = end;
      continue;
    }
    let start = end;
    if (typeof at === "number") start = at;
    else if (at === "<") start = prevStart;
    else if (at?.startsWith("-=")) start = end - Number(at.slice(2));
    else if (at?.startsWith("+=")) start = end + Number(at.slice(2));
    else if (at in labels) start = labels[at];
    const finish = start + s.duration;
    lines.push(`${s.name}: ${fmt(start)}–${fmt(finish)}`);
    prevStart = start;
    end = Math.max(end, finish);
  }
  lines.push(`jami: ${end.toFixed(2)} s`);
  return lines.join("\n");
}

console.log(
  place([
    { name: "sarlavha", duration: 0.4 },
    // 3 ta taom, har biri 0.4 s, stagger 0.1 → 0.4 + 2 × 0.1
    { name: "taomlar", duration: 0.6, at: "-=0.2" },
    { label: "narx" },
    { name: "narx", duration: 0.4, at: "narx" },
  ]),
);

Konsolda:

text
sarlavha: 0.00–0.40
taomlar: 0.20–0.80
narx: 0.80–1.20
jami: 1.20 s

Haqiqiy GSAP nima deydi? Xuddi shu timeline'ni Chrome 155 da ishga tushirdik (gsap 3.15.0, tl.duration(), tl.labels.narx, tl.getChildren().length):

text
timeline: 1.20 s, narx yorlig'i 0.80 s, bolalar 3

Bir xil. stagger li tween — bitta "bola", shuning uchun 3 ta. Vaqt chizig'ida:

gantt
  title Timeline: 1.2 s
  dateFormat x
  axisFormat %S.%L
  section Sarlavha
  sarlavha 0–0.4 :a, 0, 400ms
  section Taomlar
  Osh 0.2–0.6 :b, 200, 400ms
  Lag'mon 0.3–0.7 :c, 300, 400ms
  Manti 0.4–0.8 :d, 400, 400ms
  section Narx
  narx 0.8–1.2 :e, 800, 400ms

Taomlar sarlavha tugashidan 0.2 s oldin boshlandi ("-=0.2") — ustma-ust tushish harakatni "oqimli" qiladi. Sarlavha 0.4 s cho'zilsa, hech narsani qayta hisoblamaysiz: taomlar va narx o'zi suriladi.

3.3 Timeline'ni boshqarish

Timeline — pleer kabi obyekt: tl.play(), tl.pause(), tl.reverse(), tl.seek(0.8), tl.progress(0.5), tl.timeScale(2) (ikki baravar tez). paused: true bilan yaratib, tugma bosilganda o'ynatish mumkin. Mobil menyu uchun klassik naqsh: bitta timeline — ochish, reverse() — yopish.

Tekshirib ko'ring: tl.to(".a", { x: 100, duration: 1 }).to(".b", { x: 100, duration: 0.5 }, "<") — .b qachon boshlanadi va timeline necha soniya?

Javob

.b 0 s da — "<" oldingi tweenning boshi. Timeline 1 s: .a 0–1, .b 0–0.5. Eng uzoq tugagani timeline oxiri bo'ladi. Agar ">" bo'lsa, .b 1 s da boshlanib, timeline 1.5 s bo'lardi.

4. React'da: useGSAP

4.1 Nega useEffect yetmaydi

GSAP DOM'ni to'g'ridan-to'g'ri o'zgartiradi. Demak u effektda ishlashi kerak. Lekin uch muammo bor:

  1. StrictMode dev rejimda effektni ikki marta ishga tushiradi (Effekt hayot sikli). Tozalash bo'lmasa — from ikki marta qo'llanadi, element "yarim yo'lda" qotib qoladi.
  2. Tozalash — komponent o'chsa, tween va ScrollTrigger'lar xotirada qoladi va scroll hodisasini tinglashda davom etadi.
  3. Selektor — ".taom" butun sahifadan qidiriladi, boshqa komponentdagi .taom larni ham ushlaydi.

GSAP'ning javobi — gsap.context(): ichida yaratilgan hamma narsani yozib boradi, revert() esa hammasini bekor qilib, elementlarni asl holatiga qaytaradi. @gsap/react paketidagi useGSAP hooki buni React'ga ulaydi:

jsx
import { useRef } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";

gsap.registerPlugin(useGSAP);

export function Menu() {
  const scope = useRef(null);
  useGSAP(
    () => {
      gsap.from(".taom", { opacity: 0, x: -30, stagger: 0.1 });
    },
    { scope }, // ".taom" — faqat shu komponent ichidan
  );
  return (
    <ul ref={scope}>
      <li className="taom">Osh</li>
      <li className="taom">Lag'mon</li>
    </ul>
  );
}

useGSAP nima beradi:

  • Avtomatik tozalash — komponent o'chsa yoki StrictMode qayta ishga tushirsa, context.revert() chaqiriladi. Kanon tajribasida (tajriba/kitlar/src/k/gsap.tsx) aynan shu naqsh.
  • scope — selektorlar faqat shu ref ichidan qidiriladi. Motion'ning useAnimate idagi scope bilan bir xil g'oya (Motion IV).
  • dependencies — useEffect dagidek: { scope, dependencies: [tanlangan] }. O'zgarsa, funksiya qayta ishlaydi. revertOnUpdate: true — avval eskisini bekor qiladi.
  • Ichkarida useLayoutEffect — animatsiya brauzer chizishidan oldin o'rnatiladi, from holatidagi element bir lahza "miltillab" ko'rinmaydi.

4.2 contextSafe: hodisadagi animatsiya

Bosish hodisasida yaratilgan tween useGSAP funksiyasi tashqarisida — kontekst uni bilmaydi va tozalamaydi. Uni contextSafe bilan o'rang:

jsx
const { contextSafe } = useGSAP({ scope });

const shake = contextSafe(() => {
  gsap.to(".savat", { x: 6, repeat: 5, yoyo: true, duration: 0.05 });
});

return <button onClick={shake}>Savatga</button>;

5. ScrollTrigger

5.1 Asosiy sozlamalar

ScrollTrigger — animatsiyani scroll'ga bog'laydigan plagin:

jsx
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(useGSAP, ScrollTrigger);

useGSAP(
  () => {
    gsap.from(".taom", {
      opacity: 0,
      y: 40,
      stagger: 0.1,
      scrollTrigger: {
        trigger: ".menyu",
        start: "top 80%",
        toggleActions: "play none none reverse",
      },
    });
  },
  { scope },
);
  • trigger — qaysi element kuzatiladi.
  • start: "top 80%" — "trigger'ning tepasi ekranning 80% chizig'iga yetganda". Birinchi so'z — element nuqtasi, ikkinchisi — ekran nuqtasi. end ham xuddi shunday ("bottom top", "+=500" — boshidan 500 px keyin).
  • toggleActions — to'rt holat: kirdi, oldinga chiqib ketdi, orqaga qaytib kirdi, orqaga chiqib ketdi. "play none none reverse" — kirganda o'ynasin, tepaga qaytib chiqsa — teskari.
  • markers: true — dev rejimda start/end chiziqlarini ekranda chizadi. Production'ga qoldirmang.

5.2 scrub va pin: scroll hikoyasi

scrub: true — animatsiya vaqt bilan emas, scroll bilan yuradi. Aylantirsangiz — oldinga, qaytsangiz — orqaga. scrub: 0.5 — 0.5 s "ergashish" (Motion'dagi useSpring ga o'xshash yumshatish). pin: true — trigger start dan end gacha ekranda mixlangan turadi, sahifa esa uning atrofida aylanadi.

jsx
useGSAP(
  () => {
    const tl = gsap.timeline({
      scrollTrigger: {
        trigger: ".qozon",
        start: "top top",
        end: "+=1200",
        scrub: 0.5,
        pin: true,
      },
    });
    tl.from(".zirvak", { opacity: 0 })
      .from(".guruch", { opacity: 0, y: 40 })
      .from(".dam", { opacity: 0, y: 40 });
  },
  { scope },
);

1200 px aylantirish davomida qozon joyida turadi, uning yonida uch bosqich birin-ketin chiqadi. Bu "scrollytelling" — scroll bilan hikoya.

Biz pin va scrub ni vazifalar CSP'si ostida sinab ko'rdik (Chrome 155, end: "+=300", sahifa 550 px aylantirildi):

text
{"csp":[],"xato":[],"log":["pin-spacer: 1"],"rot":"translate3d(0px, 0px, 0px) rotate(37.1deg)","pinPos":"fixed"}

pin elementni pin-spacer nomli div ga o'radi va position: fixed qildi. Sahifa sakramasligi uchun pin-spacer bo'sh joyni ushlab turadi. scrub aylanishni scroll'ga bog'ladi (rotate(37.1deg) — 90° ning bir qismi). CSP buzilishi — 0: GSAP stillarni element.style orqali qo'yadi, <style> tegi yaratmaydi.

Diqqat: ScrollTrigger sahifa balandligini bir marta o'lchaydi. Keyin rasm yuklanib balandlik o'zgarsa, start/end nuqtalari noto'g'ri bo'ladi. Rasmlarga aniq width/height bering (Rasm unumdorligi darsidagidek), kontent o'zgarganda esa ScrollTrigger.refresh() ni chaqiring.

5.3 CSS, Motion yoki GSAP?

Vazifa Eng yengil yo'l
progress chizig'i, "ko'ringanda paydo bo'lish" CSS animation-timeline (C-75) — 0 KB
scroll qiymatiga React mantig'i Motion useScroll (Motion IV)
pin, bir nechta bosqichli scroll hikoyasi GSAP ScrollTrigger

6. SplitText: matnni harflarga bo'lish

Sarlavha harfma-harf "yozilishi" uchun har harf alohida element bo'lishi kerak. Buni qo'lda qilish — 30 ta <span>. SplitText buni avtomatik qiladi:

jsx
import { SplitText } from "gsap/SplitText";
gsap.registerPlugin(useGSAP, SplitText);

useGSAP(
  () => {
    const split = SplitText.create(".shior", {
      type: "words,chars",
      aria: "auto",
    });
    gsap.from(split.chars, { opacity: 0, y: 10, stagger: 0.02 });
  },
  { scope },
);

Ekran o'quvchi uchun muammo: 14 ta alohida <span> — Malika "B, a, h, o, r…" ni harfma-harf eshitishi mumkin. aria: "auto" — SplitText asl matnni aria-label ga yozadi, bo'laklarni esa ekran o'quvchidan yashiradi. Chrome'dagi o'lchov (o'sha CSP + Trusted Types ostida):

text
SplitText: 14 harf, aria-label=Bahor oshxonasi

"Bahor oshxonasi" — 15 belgi, bo'shliq harf emas: 14. aria-label qo'yildi. CSP va Trusted Types buzilishi — 0. Shrift kech yuklansa yoki ekran eni o'zgarsa, qatorlar qayta bo'linishi kerak — buning uchun autoSplit: true va onSplit sozlamalari bor. useGSAP tozalaganda split.revert() ham chaqiriladi — matn asl holiga qaytadi.

7. Harakatni kamaytirish: gsap.matchMedia()

GSAP'da prefers-reduced-motion uchun gsap.matchMedia() bor. U media so'rov mos kelsa animatsiyani yaratadi, mos kelmay qolsa — avtomatik revert qiladi:

jsx
useGSAP(
  () => {
    const mm = gsap.matchMedia();
    mm.add("(prefers-reduced-motion: no-preference)", () => {
      gsap.from(".taom", { opacity: 0, y: 40, stagger: 0.1 });
    });
  },
  { scope },
);

Harakat kamaytirilgan bo'lsa, ichidagi kod umuman ishlamaydi — taomlar darhol joyida. pin li scroll hikoyasini ham shu ichiga qo'ying: kamaytirilgan rejimda bosqichlar oddiy ro'yxat bo'lib qoladi. Bitta matchMedia bilan ekran eni bo'yicha ham ajratish mumkin ("(min-width: 800px)") — mobil va kompyuter uchun turli animatsiya.

8. Narx va CSP — o'lchov

Kanon tajribasi (tajriba/kutubxonalar.txt, gsap 3.15.0 + ScrollTrigger + @gsap/react 2.1.2, Vite 8.3.3, gzip) va CSP natijasi:

Animatsiya kutubxonalari: birinchi yuklashga qo'shimcha
  • Motion m.* (vazifalar)14,2 KB
  • Motion to'liq40,4 KB
  • GSAP + ScrollTriggerCSP 043,4 KB

Manba: Kanon o'lchovi: tajriba/kutubxonalar.txt va QADAMLAR #23–#26 (Chrome 155, 2026-10-14)

  • Hajm — +43.4 KB, to'liq Motion bilan deyarli teng. Faqat GSAP yadrosi (ScrollTrigger'siz) alohida o'lchanmadi.
  • CSP — kanon tajribasida 0 buzilish. Biz qo'shimcha sinadik: pin, scrub va SplitText — style-src 'self' va Trusted Types ostida ham 0. Bu GSAP'ning kuchli tomoni. U <style> yaratmaydi va innerHTML ga satr yozmaydi. Mantine va Konva bunday CSP'da umuman ochilmagan edi, sonner esa ishladi, lekin uslubi to'sildi (UI kutubxonalar xaritasi, Node-diagramma va canvas).
  • Lazy yuklash — GSAP faqat landing sahifasida kerak bo'lsa, o'sha marshrutni lazy qiling: ilovaning qolgan qismi uni yuklamaydi.

9. GSAP yoki Motion?

Ikkalasi ham yaxshi kutubxona. Savol — vazifaga qaysi biri mos:

Mezon Motion GSAP
Yondashuv deklarativ: props (animate, exit) imperativ: gsap.to, timeline
React bilan tabiiy: state → animatsiya, AnimatePresence, layout useGSAP orqali, DOM'ga to'g'ridan-to'g'ri
Murakkab ketma-ketlik sequence (at, yorliqlar) — bor timeline — eng kuchli, boshqarish (seek, reverse)
Scroll hikoyasi, pin useScroll — pin yo'q ScrollTrigger
Matn bo'lish, SVG morph yo'q SplitText, MorphSVG
Hajm (o'lchov) 14.2 KB (LazyMotion) … 40.4 KB 43.4 KB

Qisqasi: ilova interfeysi (ro'yxat, modal, tab, holat o'zgarishi) — Motion yoki CSS. Marketing sahifasi, scroll hikoyasi, reklama banneri, SVG illyustratsiya — GSAP.

Motion ham timeline'ga o'xshash ketma-ketlikni biladi. 3-mashqda yuqoridagi GSAP timeline'ini Motion bilan qayta yozamiz va bir xil 1.20 s ni olamiz.

10. Kanon nega GSAP'ni olmadi — tajriba

vazifalar — ish vositasi: ro'yxat, forma, statistika. Unda scroll hikoyasi, pin, harfma-harf sarlavha yo'q. Kanon tajribasi GSAP'ni o'lchadi (tajriba/kitlar/src/k/gsap.tsx: useGSAP + scope, timeline().from(".b", { stagger }), ScrollTrigger scrub): +43.4 KB, CSP 0, xato yo'q. Texnik to'siq yo'q. Lekin ilovada Motion allaqachon bor (14.2 KB) va qator animatsiyasi uchun yetadi. Ikkinchi animatsiya kutubxonasi birinchi yuklashni yana ~43 KB ga oshirardi — hech qanday yangi imkoniyat foydalanuvchiga bermasdan.

Qoida (QADAMLAR, #27–#30): kutubxona vazifa bo'lganda qo'shiladi, "chiroyli" bo'lgani uchun emas.

11. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
dev'da from animatsiyasi yarim yo'lda qotadi useEffect + StrictMode, tozalash yo'q useGSAP
boshqa komponentdagi .taom ham animatsiya bo'ldi scope berilmagan useGSAP(fn, { scope })
bosishdagi animatsiya sahifadan chiqqach ham ishlaydi hodisadagi tween kontekstdan tashqarida contextSafe
duration: 300 — animatsiya 5 daqiqa GSAP soniyada o'lchaydi duration: 0.3
ScrollTrigger noto'g'ri joyda boshlanadi rasm yuklanib balandlik o'zgardi o'lchamlar, ScrollTrigger.refresh()
ScrollTrigger ishlamaydi, xato yo'q plagin ro'yxatdan o'tmagan gsap.registerPlugin(ScrollTrigger)
ekran o'quvchi sarlavhani harflab o'qiydi SplitText aria siz aria: "auto"
production'da qizil chiziqlar markers: true qolib ketgan faqat dev'da

12. Mashqlar

Mashqlar kurs/mashqlar/20/28-gsap/ da. GSAP natija oynasida yo'q — 2- va 3-mashqlar sof JavaScript va Motion bilan, GSAP g'oyasini tekshiradi.

1-mashq (oson): qachon boshlanadi?

Timeline: tl.to(".a", { duration: 1 }).to(".b", { duration: 1 }, POZITSIYA). .b necha soniyada boshlanadi?

  • POZITSIYA yo'q →
  • "<" →
  • "-=0.5" →
  • "<0.25" →
Yechim
  • Yo'q — oxiriga: .a 1 s da tugaydi, .b 1 s da.
  • "<" — .a ning boshi: 0.
  • "-=0.5" — timeline oxiridan (1) 0.5 s oldin: 0.5.
  • "<0.25" — .a boshidan 0.25 s keyin: 0.25.

2-mashq (o'rta): place ga ">" va "<0.1" ni qo'shing

«Hisoblab ko'ramiz» bo'limidagi place funksiyasi ">" (oldingi tween oxiri) va "<0.1" (oldingi tween boshidan siljish) ni bilmaydi. Qo'shing. Kutilgan natija pastda.

Ishora: oldingi tweenning oxirini ham saqlang (prevEnd). "<0.1" — "<" bilan boshlanadi va qolgan qismi son: at.slice(1) (satr metodlari). "<" ning o'zi ham shu qoidaga tushsin (bo'sh qism — 0).

js
const fmt = (n) => n.toFixed(2);

function place(steps) {
  let end = 0;
  let prevStart = 0;
  const lines = [];
  for (const s of steps) {
    const at = s.at;
    let start = end;
    if (typeof at === "number") start = at;
    else if (at === "<") start = prevStart;
    else if (at?.startsWith("-=")) start = end - Number(at.slice(2));
    const finish = start + s.duration;
    lines.push(`${s.name}: ${fmt(start)}–${fmt(finish)}`);
    prevStart = start;
    end = Math.max(end, finish);
  }
  return lines.join("\n");
}

console.log(
  place([
    { name: "qozon", duration: 1 },
    { name: "bug'", duration: 0.5, at: "<0.1" },
    { name: "osh", duration: 0.4, at: ">" },
  ]),
);

Konsolda:

text
qozon: 0.00–1.00
bug': 1.00–1.50
osh: 1.50–1.90
Yechim
js
const fmt = (n) => n.toFixed(2);

function place(steps) {
  let end = 0;
  let prevStart = 0;
  let prevEnd = 0;
  const lines = [];
  for (const s of steps) {
    const at = s.at;
    let start = end;
    if (typeof at === "number") start = at;
    else if (at === ">") start = prevEnd;
    else if (at?.startsWith("<")) {
      start = prevStart + Number(at.slice(1) || 0);
    } else if (at?.startsWith("-=")) {
      start = end - Number(at.slice(2));
    }
    const finish = start + s.duration;
    lines.push(`${s.name}: ${fmt(start)}–${fmt(finish)}`);
    prevStart = start;
    prevEnd = finish;
    end = Math.max(end, finish);
  }
  return lines.join("\n");
}

console.log(
  place([
    { name: "qozon", duration: 1 },
    { name: "bug'", duration: 0.5, at: "<0.1" },
    { name: "osh", duration: 0.4, at: ">" },
  ]),
);

Konsolda:

text
qozon: 0.00–1.00
bug': 0.10–0.60
osh: 0.60–1.00

">" — timeline oxiri emas, oldingi tweenning oxiri. Farq shu yerda: «bug'» 0.6 da tugadi, timeline esa 1.0 gacha (qozon). "osh" 0.6 da boshlandi — "-=0" yoki standart bo'lsa 1.0 da boshlanardi. Number("" || 0) — "<" ning o'zi uchun 0 qo'shadi.

3-mashq (qiyin): GSAP timeline'ni Motion bilan

«Zanjir va joylashuv parametri» bo'limidagi timeline'ni natija oynasida Motion bilan qayta yozing. Talablar:

  1. useAnimate va sequence (massivlar massivi) — bitta animate([...]) chaqiruvi.
  2. Sarlavha 0.4 s; taomlar sarlavha tugashidan 0.2 s oldin, har biri 0.4 s, stagger 0.1; "narx" yorlig'i; narx yorliqda.
  3. Konsolga umumiy davomiylik (controls.duration, 2 xona) va tugaganda tugadi.

Ishora: Motion sequence'da joylashuv — uchinchi elementdagi at ("-0.2" — oldingisi tugashidan 0.2 s oldin, "narx" — yorliq). Yorliq — massivdagi oddiy satr. stagger — motion/react dan, delay: stagger(0.1) (Motion II).

Yechim
jsx
import { stagger, useAnimate } from "motion/react";

export default function App() {
  const [scope, animate] = useAnimate();
  async function run() {
    const controls = animate([
      ["h2", { opacity: [0, 1], y: [-20, 0] }, { duration: 0.4 }],
      [
        ".taom",
        { opacity: [0, 1], x: [-30, 0] },
        { duration: 0.4, delay: stagger(0.1), at: "-0.2" },
      ],
      "narx",
      [
        ".narx",
        { opacity: [0, 1], scale: [0.8, 1] },
        { duration: 0.4, at: "narx" },
      ],
    ]);
    console.log("davomiylik:", controls.duration.toFixed(2), "s");
    await controls;
    console.log("tugadi");
  }
  return (
    <div ref={scope}>
      <button onClick={run}>Boshla</button>
      <h2>Menyu</h2>
      <p className="taom">Osh</p>
      <p className="taom">Lag'mon</p>
      <p className="taom">Manti</p>
      <p className="narx">35 000</p>
    </div>
  );
}

Konsolda:

text
davomiylik: 1.20 s
tugadi

GSAP bilan bir xil — 1.20 s. Sintaksis boshqa ("-=0.2" → at: "-0.2", addLabel → massivdagi satr), g'oya o'sha. Agar ilovada Motion allaqachon bo'lsa, bunday ketma-ketlik uchun GSAP qo'shish shart emas. Mezon — pin, SplitText, MorphSVG kabi imkoniyatlar kerakmi.

4-mashq: Vazifalar qadami — tajriba (commit yo'q)

Kanon GSAP'ni olmadi. O'z tajribangiz: vazifalar dan nusxa olingan alohida papkada npm i -E gsap@3.15.0 @gsap/react@2.1.2. Bo'sh holat (#bosh-xabar) ikonkasiga useGSAP bilan kichik "silkinish" qo'shing: scope, gsap.matchMedia bilan prefers-reduced-motion. npm run build — birinchi yuklash JS qancha o'sdi? Brauzerda CSP konsolida xato bormi?

Yechim

Kutiladigan natija:

  • CSP xatosi yo'q — kanon tajribasi va bizning pin/SplitText o'lchovimizdagidek.
  • Birinchi yuklash JS o'sadi. GSAP yadrosi (ScrollTrigger'siz) alohida o'lchanmagan; to'liq to'plam (+ ScrollTrigger) — +43.4 KB. Bitta silkinish uchun bu juda qimmat. Xuddi shuni CSS @keyframes (CSS animatsiyalari React'da) 0 KB bilan qiladi.
  • Xulosa — kanon qarori bilan bir xil: ilova uchun GSAP ortiqcha. Nusxa papkani o'chiring.

13. Real ishda

  • Landing sahifalar va reklama — mahsulot taqdimoti, bayram aksiyalari, scroll bilan hikoya qiluvchi sahifalar. Ko'p raqamli agentliklarning portfoliosida GSAP deyarli majburiy ko'nikma.
  • Reklama bannerlari — GSAP README'sida: har bir yirik reklama tarmog'i GSAP'ni banner fayl hajmiga qo'shmaydi, ko'pchiligi uni o'z CDN'ida saqlaydi.
  • SVG illyustratsiya va infografika — MorphSVG, DrawSVG, MotionPath plaginlari.
  • Intervyu savollari: "GSAP timeline'dagi joylashuv parametri nima?", "React'da GSAP animatsiyasini qanday tozalaysiz?", "scrub va toggleActions farqi?", "Motion yoki GSAP — qachon qaysi?".

Xulosa

  • Tween — bitta harakat (to, from, fromTo, set), duration soniyada, stagger — ketma-ket.
  • Timeline — harakatlar vaqt o'qida: "<", ">", "-=0.2", yorliqlar. Bitta harakat o'zgarsa, qolganlari o'zi suriladi.
  • React'da — useGSAP: avtomatik revert, scope, dependencies; hodisadagi animatsiya — contextSafe.
  • ScrollTrigger: start/end, toggleActions, scrub, pin (pin-spacer); SplitText — aria: "auto" bilan; harakatni kamaytirish — gsap.matchMedia().
  • +43.4 KB, CSP va Trusted Types bilan to'liq mos (o'lchov). Marketing va scroll hikoyasi uchun — GSAP, ilova interfeysi uchun — Motion yoki CSS.

Keyingi dars: Lottie, Rive va AutoAnimate — dizayner tayyorlagan animatsiyalarni sahifaga qo'yish va bitta qator bilan ro'yxat animatsiyasi. U yerda CSP bilan haqiqiy to'qnashuvni ko'ramiz.

Manbalar

  • GSAP 3.15.0: gsap.to/from, Timeline, position parameter, ScrollTrigger, SplitText, gsap.matchMedia — gsap.com/docs/v3 (2026-10)
  • @gsap/react 2.1.2: useGSAP — gsap.com/resources/React
  • GSAP litsenziyasi: gsap/package.json 3.15.0 (npm view gsap@3.15.0 license), gsap.com/standard-license; README — "GSAP is FREE!" bo'limi
  • Kanon tajribasi: tajriba/kitlar/src/k/gsap.tsx, natijalar/tajriba/kutubxonalar.txt
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
GSAP React'da: timeline, ScrollTrigger, useGSAP va SplitText — IlmHamroh