IlmHamroh
JavaScript Full-stack/20-qism. React: UI kutubxonalar va animatsiya29/30-dars18 daqiqa
Mundarija (27)

Lottie, Rive va AutoAnimate React'da: dizayner animatsiyalari, WASM va CSP tuzog'i

Qisqacha: Lottie va Rive — dizayner chizgan animatsiyani kod yozmasdan sahifaga qo'yish usuli. Lottie — After Effects'dan eksport qilingan JSON (yoki siqilgan .lottie), Rive — o'z muharririda chizilgan, holat mashinali interaktiv .riv fayl. AutoAnimate esa boshqa narsa: bitta ref bilan ro'yxatdagi qo'shish, o'chirish va siljishni o'zi animatsiya qiladi. Tuzoq — CSP: dotLottie va Rive pleeri WebAssembly'ni CDN'dan yuklaydi. Qattiq connect-src va script-src buni to'sadi va animatsiya jim chiqmaydi. Davosi — WASM'ni o'z serveringizdan berish va 'wasm-unsafe-eval'.

Bu darsda

  • Lottie, dotLottie va Rive farqini va qaysi biri qachon kerakligini tushuntirasiz.
  • lottie-react 3 ning yangi API'si bilan animatsiya qo'yasiz va eski (v2) kodni taniysiz.
  • CSP ostida WASM pleer nega ishlamasligini o'lchov bilan ko'rasiz va uni tuzatasiz.
  • Og'ir pleerni "poster + kech yuklash" naqshi bilan qo'shasiz va harakatni kamaytirgan foydalanuvchini hurmat qilasiz.
  • AutoAnimate ichida nima borligini bilasiz va ro'yxatda u nega AnimatePresence tuzog'ini takrorlashini ko'rasiz.

Oldin bilishingiz kerak: Motion III: chiqish va layout animatsiyalari, GSAP, Frontend xavfsizligi React'da, lazy va code splitting, Suspense.

1. Nega bu kerak?

«Bahor» ilovasiga dizayner Nilufar uchta animatsiya tayyorladi. Birinchisi — bo'sh savat: qozon ichidan bug' chiqib turadi. Ikkinchisi — "Buyurtma qabul qilindi" belgisi. Uchinchisi — bosilganda ko'z qisadigan oshpaz maskoti. Ularni Motion yoki GSAP'da qo'lda yozish — har biriga bir necha kun. Nilufar esa ularni After Effects va Rive muharririda bir soatda chizdi.

Bu dars — dizayner va dasturchi o'rtasidagi ko'prik. Dizayner fayl beradi, dasturchi uni sahifaga qo'yadi. Hayotiy o'xshatish: musiqa. Kompozitor notani yozadi (After Effects, Rive), musiqachi uni o'qib chaladi (pleer). Nota fayli kichik, lekin chaladigan asbob — pleer — og'ir bo'lishi mumkin.

Asosiy savol "qanday qo'yish" emas — u bir qator. Savol: pleer qancha turadi va xavfsizlik siyosatimizga sig'adimi? Kanon tajribasida (tajriba/kutubxonalar.txt) dotLottie va Rive vazifalar CSP'si ostida umuman ochilmadi. Biz bu tajribani Chrome 155 da qaytadan, to'liq xato matnlari bilan takrorladik va tuzatish yo'lini sinadik.

2. Lottie: After Effects'dan JSON

2.1 Format

Lottie — Airbnb chiqargan ochiq format. Dizayner After Effects'da animatsiya qiladi, Bodymovin plagini uni JSON'ga eksport qiladi. JSON ichida qatlamlar, shakllar va kalit kadrlar — rasm emas, vektor ko'rsatmalar. Shuning uchun fayl kichik va har o'lchamda tiniq.

Mana eng kichik Lottie — chapdan o'ngga suriladigan ko'k kvadrat (biz o'lchov uchun qo'lda yozganmiz, qisqartirilgan):

json
{
  "v": "5.7.4", "fr": 30, "ip": 0, "op": 60, "w": 200, "h": 100,
  "layers": [{
    "ty": 4, "nm": "quti",
    "ks": { "p": { "a": 1, "k": [
      { "t": 0, "s": [30, 50, 0] },
      { "t": 59, "s": [170, 50, 0] }
    ] } },
    "shapes": [{ "ty": "rc", "s": { "a": 0, "k": [40, 40] } }]
  }]
}

fr — sekundiga 30 kadr, op: 60 — 60 kadr, ya'ni 2 soniya. p — o'rin (position), a: 1 — animatsiyali, k — kalit kadrlar: 0-kadrda x=30, 59-kadrda x=170. rc — to'rtburchak. Odatda bu faylni qo'lda yozmaysiz — uni dizayner beradi.

2.2 lottie-react 3: yangi API

lottie-react — eng ko'p ishlatiladigan React o'rami. Uning ichida Airbnb'ning lottie-web pleeri (5.13.0). 3-versiyada API o'zgardi:

jsx
import { Lottie } from "lottie-react";

export function EmptyCart() {
  return (
    <div style={{ width: 200, height: 100 }}>
      <Lottie src="/animatsiyalar/bosh-savat.json" autoplay loop />
    </div>
  );
}
  • import { Lottie } — nomli eksport. src — URL yoki tayyor JSON obyekti. Animatsiya o'rab turgan elementni to'ldiradi — o'lchamni o'sha elementga bering.
  • Eski kodda (v2) boshqacha ko'rasiz: import Lottie from "lottie-react" va animationData={json}. 3.1.2 da bu ishlamaydi. Standart eksport yo'q — Vite build "default" is not exported by "node_modules/lottie-react/build/index.js" xatosini beradi. Biz buni o'z tajribamizda ko'rdik.
  • useLottie({ src, autoplay, loop }) — hook varianti: play(), seek() kabi buyruqlar. lottieInView, lottieScrollScrub — ko'ringanda o'ynash va scroll bilan boshqarish.

2.3 O'lchov: kanon tajribasidagi kichik xato

Kanon tajribasi (tajriba/kitlar/src/k/lottie.tsx) avval lottie-react 3.1.2 ni v2 uslubida yozgan edi: <Lottie animationData={anim} loop />. 3-versiyada animationData prop emas. U DOM'ga oddiy atribut bo'lib tushdi va animatsiya chizilmadi. Chrome'dagi sahifa DOM'i — div bo'sh, faqat begona atribut:

text
<main><div animationdata="[object Object]" class="lottie-display" style="width: 200px;"></div></main>

Biz buni topdik va kanon tajribasi 3.x API (src) bilan qayta o'lchandi (lottie-qayta.txt, Chrome 155.0.8059.82): +80.2 KB, animatsiya chiziladi (SVG transform harakatda). Eski variant +80.0 KB edi — lottie-web to'plamga kirgan, lekin hech narsa chizmagan. CSP natijasini ham v3 API bilan, vazifalar CSP'si ostida tekshirdik:

text
## lottie.html {"svg":1,"canvas":0,"style":1}
{"csp":["style-src-elem inline"]}
## lottie-n.html {"svg":1,"canvas":0,"style":1}   ← CSP'siz
{"csp":[]}

Animatsiya chizildi (svg: 1) — CSP bilan ham, CSP'siz ham. Bitta buzilish: kutubxona o'z o'ramining stilini <style> teg bilan qo'shadi (@layer lottie-react{:where(.lottie-display){width:100%;height:100%;…}}). CSP uni to'sadi — natijada o'ram stilsiz qoladi. O'lchamni o'zingiz bersangiz, ko'zga farq yo'q.

Bu saboq Radix va Motion III dagi popLayout ga o'xshaydi: kutubxona "jim" <style> qo'shadi. Lekin bu yerda u faqat bezak — ishlash buzilmaydi.

Diqqat: lottie-web After Effects ifodalarini (expressions — kadrlar orasida hisoblanadigan kichik JS kod) eval bilan bajaradi. Vite build buni ogohlantiradi: Use of direct eval function is strongly discouraged. CSP'da 'unsafe-eval' yo'q bo'lsa, ifodali animatsiya ishlamaydi. Yechim — LottieLight (ifodasiz yengil pleer) va dizaynerdan ifodasiz eksport so'rash.

3. dotLottie: siqilgan format va WASM pleer

dotLottie (.lottie) — LottieFiles kompaniyasining formati: bir yoki bir nechta Lottie JSON va rasmlar zip ichida, ustiga tema va holat mashinasi. Fayl odatda JSON'dan ancha kichik. Pleer (@lottiefiles/dotlottie-react 0.20.0, ichida dotlottie-web 0.81.0) boshqa yo'l tanlagan. Animatsiyani JS emas, WebAssembly'ga kompilyatsiya qilingan dvigatel <canvas> ga chizadi:

jsx
import { DotLottieReact } from "@lottiefiles/dotlottie-react";

<DotLottieReact src="/animatsiyalar/qabul.lottie" autoplay />;

JS qismi kichik (+14.0 KB). Lekin dvigatel — dotlottie-player.wasm — alohida fayl: 1.24 MB, gzip bilan ~487 KB. U birinchi pleer yaratilganda CDN'dan yuklanadi.

4. Rive: interaktiv animatsiya va holat mashinasi

Rive — o'z muharriri (rive.app) va runtime'i bor platforma. Lottie "videoga o'xshash" — boshidan oxirigacha o'ynaydi. Rive esa holat mashinasi bilan ishlaydi (Holat mashinalari): animatsiya kirish qiymatlariga (hover, bosildi, daraja) javob beradi. Oshpaz maskoti sichqoncha yaqinlashsa qaraydi, bosilsa ko'z qisadi — bularni dizayner muharrirda bog'laydi, dasturchi faqat qiymat beradi:

jsx
import {
  useRive,
  useStateMachineInput,
} from "@rive-app/react-canvas";

export function Chef() {
  const { rive, RiveComponent } = useRive({
    src: "/animatsiyalar/oshpaz.riv",
    stateMachines: "Asosiy",
    autoplay: true,
  });
  const wink = useStateMachineInput(rive, "Asosiy", "kozQisish");
  return (
    <RiveComponent
      style={{ width: 200, height: 200 }}
      onClick={() => wink?.fire()}
    />
  );
}

Rive ham WebAssembly: rive.wasm — 1.99 MB, gzip ~810 KB, JS qismi +62.7 KB.

5. CSP tuzog'i: WASM qayerdan va qanday yuklanadi

5.1 O'lchov: uch bosqich

vazifalar ning CSP'si (Frontend xavfsizligi) — default-src 'self'; script-src 'self'; connect-src 'self'; …. dotLottie va Rive bunda nima qiladi? Uch variantni sinadik (Chrome 155.0.8059.82, Vite 8.3.3 build, har biri alohida sahifa):

Variant dotLottie Rive
1. Hech narsa qilmaymiz connect-src ×4 connect-src, script-src
2. WASM o'z serverimizda script-src (wasm-eval) ×2 script-src, keyin yana CDN
3. + 'wasm-unsafe-eval' load, CSP 0 "wasm tayyor", CSP 0

1-variant. dotLottie WASM'ni jsdelivr'dan so'radi, keyin zaxira sifatida unpkg'dan. Ikkalasi ham to'sildi:

text
Connecting to 'https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web@0.81.0/dist/dotlottie-player.wasm' violates the following Content Security Policy directive: "connect-src 'self'". The action has been blocked.

Tarjimasi: "jsdelivr'dagi fayl bilan ulanish connect-src 'self' direktivasini buzadi — amal to'sildi". connect-src — fetch qayerga murojaat qila olishini belgilaydi. Pleer konsolga yozdi: dotlottie: loadError Failed to load wasm module: Error: WASM loading failed from all sources. — "WASM modulini hech bir manbadan yuklab bo'lmadi". Ekranda esa bo'sh <canvas>. Foydalanuvchi uchun bu shunchaki "animatsiya yo'q".

2-variant. WASM faylini node_modules dan o'z public/ papkamizga ko'chirdik va pleerga ko'rsatdik. Fayl yuklandi, lekin kompilyatsiya to'sildi:

text
WebAssembly.instantiateStreaming(): Compiling or instantiating WebAssembly module violates the following Content Security policy directive because 'unsafe-eval' is not an allowed source of script…

Tarjimasi: "WebAssembly modulini kompilyatsiya qilish CSP'ni buzadi, chunki script-src da 'unsafe-eval' yo'q". Brauzer uchun WASM kompilyatsiyasi — yangi kod yaratish, xuddi eval kabi. Rive bu yerda yana bir qadam qildi: o'z WASM'i ishlamagach, jsdelivr'dagi zaxira rive_fallback.wasm ga murojaat qildi (u ham to'sildi).

3-variant. script-src 'self' 'wasm-unsafe-eval' qo'shildi. Ikkala pleer ham ishladi, CSP buzilishi — 0.

flowchart TD
  A["Pleer yaratildi"] --> B{"WASM qayerdan?"}
  B -- "CDN (standart)" --> C["connect-src 'self' — to'sildi"]
  B -- "o'z serverimiz" --> D{"script-src da wasm-unsafe-eval?"}
  D -- "yo'q" --> E["kompilyatsiya to'sildi"]
  D -- "bor" --> F["animatsiya ishlaydi"]

5.2 'wasm-unsafe-eval' — 'unsafe-eval' emas

Nomi qo'rqitadi, lekin farq katta. 'unsafe-eval' — JavaScript'dagi eval() va new Function() ga ruxsat. Bu XSS hujumi uchun ochiq eshik: hujumchi satrni kodga aylantira oladi. 'wasm-unsafe-eval' esa faqat WebAssembly kompilyatsiyasiga ruxsat beradi. JS eval baribir to'sig'ich ortida.

Tuzatilgan pleerlar uchun kod:

jsx
// dotLottie — birinchi pleerdan OLDIN
import { setWasmUrl } from "@lottiefiles/dotlottie-react";
setWasmUrl("/wasm/dotlottie-player.wasm");

// Rive
import { RuntimeLoader } from "@rive-app/react-canvas";
RuntimeLoader.setWasmUrl("/wasm/rive.wasm");

Va CSP: script-src 'self' 'wasm-unsafe-eval'. connect-src o'zgarmaydi — fayl endi 'self' dan.

Ikki ehtiyot chorasi:

  • Versiya. WASM fayli JS qismi bilan bir versiyada bo'lsin. dotLottie README'sida: har yangilanishda fayllarni o'rnatilgan paketdan qayta ko'chiring. Buni build skriptiga qo'shing, qo'lda emas.
  • Zaxira CDN. setWasmUrl aniq URL bersa, dotLottie zaxira CDN'larni o'chiradi (README). Xato yo'l jim CDN'ga emas, loadError ga olib keladi — bu yaxshi, xato ko'rinadi.

Tekshirib ko'ring: Sardor CSP xatosini ko'rib, connect-src ga https://cdn.jsdelivr.net va https://unpkg.com ni qo'shdi. Animatsiya ishlaydimi? Bu yaxshi yechimmi?

Javob

Ishlamaydi. WASM yuklanadi, lekin kompilyatsiya script-src da to'siladi — 2-variantdagi xato. 'wasm-unsafe-eval' baribir kerak. Yechim sifatida ham yomon: ilova endi ikki begona serverga bog'liq. CDN ishlamay qolsa yoki bloklansa (O'zbekistonda ba'zi CDN'lar sekin), animatsiya yo'q. Begona domen'dan kelgan kodga ishonish — yana bir hujum yo'li. O'z serveringizdan bering.

6. Og'ir pleer: poster va kech yuklash

dotLottie va Rive pleerlari — yuzlab KB. Uni bo'sh savat rasmi uchun sahifaning birinchi yuklashiga qo'shish — LCP va INP'ga zarba. Naqsh:

  1. Avval poster — oddiy rasm yoki SVG, 0 KB JS.
  2. Pleer lazy — kerak bo'lganda (ko'rinishga kirdi, bosildi).
  3. Harakat kamaytirilgan bo'lsa — faqat poster.

Natija oynasida haqiqiy pleer yo'q. Uning o'rniga 200 ms da "yuklanadigan" soxta pleer — naqsh o'sha:

jsx
import { Suspense, lazy, useState } from "react";

// Soxta pleer. Haqiqiy loyihada:
// lazy(() => import("./SavatLottie"))
function FakePlayer() {
  return <p>🎞️ Animatsiya o'ynayapti</p>;
}
const Player = lazy(
  () =>
    new Promise((resolve) =>
      setTimeout(() => resolve({ default: FakePlayer }), 200),
    ),
);

function EmptyCart({ reduceMotion }) {
  const [play, setPlay] = useState(false);
  if (reduceMotion || !play) {
    return (
      <div>
        <p role="img" aria-label="Bo'sh savat">
          🧺
        </p>
        {!reduceMotion && (
          <button onClick={() => setPlay(true)}>
            Animatsiyani ko'rish
          </button>
        )}
      </div>
    );
  }
  return (
    <Suspense fallback={<p>Yuklanmoqda…</p>}>
      <Player />
    </Suspense>
  );
}

export default function App() {
  return <EmptyCart reduceMotion={false} />;
}

Ekranda:

text
🎞️ Animatsiya o'ynayapti
  • Bosishgacha — faqat poster. Pleer to'plamga ham, tarmoqqa ham kirmagan.
  • Bosilgach — Suspense fallback'i ("Yuklanmoqda…", ekran() tekshiradi), 200 ms dan keyin pleer.
  • reduceMotion ni true qilib ko'ring: tugma yo'qoladi, faqat poster qoladi. Loyihada bu qiymat matchMedia("(prefers-reduced-motion: reduce)") yoki Motion'ning useReducedMotion() idan keladi (Motion IV).

Bosish o'rniga "ko'rinishga kirganda" ham bo'ladi — useInView (Motion IV). Lekin har sahifada avtomatik yuklash — yana o'sha yuzlab KB.

7. AutoAnimate: bitta ref bilan ro'yxat animatsiyasi

7.1 Qanday ishlaydi

@formkit/auto-animate (0.10.0) — butunlay boshqa toifa. Dizayner fayli yo'q. Ota elementga ref berasiz — u bolalarning qo'shilishi, o'chirilishi va siljishini o'zi animatsiya qiladi:

jsx
import { useAutoAnimate } from "@formkit/auto-animate/react";

export function DishList({ dishes }) {
  const [parent] = useAutoAnimate();
  return (
    <ul ref={parent}>
      {dishes.map((d) => (
        <li key={d.id}>{d.name}</li>
      ))}
    </ul>
  );
}

Ichida nima bor? Manba kodida (index.mjs): MutationObserver — bolalar o'zgarishini kuzatadi; ResizeObserver — o'lcham; element.animate — Web Animations API bilan FLIP (Motion III dagi g'oya). Standart: 250 ms, ease-in-out. prefers-reduced-motion ni o'zi hurmat qiladi (disrespectUserMotionPreference sozlamasi bilan o'chirish mumkin). Narxi — +2.9 KB, CSP 0 (kanon tajribasi). Bir qatorlik "sehr" uchun juda arzon.

7.2 O'chirish: tanish tuzoq

React elementni o'chirdi — AutoAnimate chiqish animatsiyasini qanday qiladi? Manba kodidagi remove funksiyasi javob beradi: o'chirilgan elementni DOM'ga qaytadan qo'yadi, position: absolute va pointer-events: none bilan, animatsiya qiladi, keyin olib tashlaydi. Chrome'da o'lchadik (uch taom, birinchisi o'chiriladi):

text
oddiy: o'chirishdan 50 ms keyin [Osh:absolute,Lag'mon:static,Manti:static] | 450 ms [Lag'mon:static,Manti:static] | qo'shishda animatsiyalar: 4 | csp []
reduce: o'chirishdan 50 ms keyin [Lag'mon:static,Manti:static] | 450 ms [Lag'mon:static,Manti:static] | qo'shishda animatsiyalar: 0 | csp []

O'chirishdan 50 ms keyin «Osh» DOM'da (absolute), state'da esa yo'q. Bu Motion III dagi AnimatePresence holatining aynan o'zi: filtr, qidiruv va testlar 250 ms davomida "o'lik" qatorni ko'radi. Bundan tashqari element React bilmagan holda DOM'ga qaytarildi. Harakat kamaytirilganda esa hammasi darhol va toza (reduce qatori).

Xulosa: AutoAnimate — kichik ro'yxatlar, sozlamalar paneli, "qo'shish-o'chirish" demolari uchun ajoyib. Testlari va filtrlari ko'p bo'lgan asosiy ro'yxat uchun — vazifalar dagi kanon yo'li: kirish animatsiyasi + o'chirishdan oldin chiqish.

8. react-spring — qisqacha

react-spring — fizikaga asoslangan (prujina) animatsiya kutubxonasi: useSpring, useTransition, useTrail hooklari. U Motion bilan bir toifada va yillar davomida mashhur bo'lgan. Eski loyihalarda, ayniqsa @react-three/fiber bilan 3D sahnalarda (keyingi dars) uchratasiz. Biz uni o'lchamadik. Yangi loyiha uchun vazifalar da Motion tanlangani — ekotizim, hujjat va LazyMotion bilan hajmni boshqarish imkoniyati sababli.

9. Qaysi birini tanlash

Pleer narxi: JS + WASM (gzip)
  • AutoAnimateCSP 02,9 KB
  • lottie-react 3<style> ×180,2 KB
  • dotLottie (JS + WASM)14.0 + 487.4501,4 KB
  • Rive (JS + WASM)62.7 + 810.2872,9 KB

Manba: Kanon o'lchovi: tajriba/kutubxonalar.txt, lottie-qayta.txt (JS) va IlmHamroh tajribasi 2026-10-17, Chrome 155.0.8059.82 (WASM gzip -9: 499 096 va 829 637 bayt)

Vazifa Tanlov
ro'yxat qo'shish/o'chirish, kichik UI AutoAnimate (yoki Motion, CSS)
dizaynerdan oddiy illyustratsiya animatsiyasi lottie-react — WASM yo'q, faqat <style> buzilishi
ko'p animatsiya, kichik fayllar, tema dotLottie — WASM o'z serveringizdan
interaktiv maskot, holat mashinasi, o'yinsimon UI Rive — eng og'ir, eng kuchli

Muhim: WASM bir marta yuklanadi va keshlanadi. Sahifada 20 ta dotLottie bo'lsa, ~487 KB bitta marta. Bitta kichik belgi uchun esa — juda qimmat.

10. Kanon nega olmadi — tajriba

vazifalar da dizayner animatsiyasi yo'q — u ish vositasi. Bo'sh holat ikonkasi lucide SVG (Ikonkalar), yuklanish — CSS skeleton (Yuklanish holatlari). Kanon tajribasi Lottie, dotLottie va Rive'ni o'lchadi (kutubxonalar.txt) — eng kami +14.0 KB JS va qattiq CSP'da ochilmadi. AutoAnimate (+2.9 KB, CSP 0) texnik jihatdan mos edi. Lekin o'chirishda qatorni DOM'ga qaytaradi — #25 dagi o'lchov bilan rad etilgan xulq.

Bizning qo'shimcha o'lchovimiz kanon tajribasidagi bitta noaniqlikni ham ochdi: lottie.tsx v2 API'da yozilgan edi va animatsiya umuman chizilmagan. Tajriba 3.x API bilan tuzatildi va qayta o'lchandi: +80.2 KB, animatsiya chiziladi. Saboq: "o'lchandi" degan raqam ham kod haqiqatan ishlaganini isbotlamaydi — natijaga ko'z bilan qarang.

11. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
"default" is not exported by "…lottie-react…" v2 importi v3 bilan import { Lottie }, src
Lottie div bo'sh, animationdata atributi v2 animationData prop src={json}
dotLottie/Rive — bo'sh canvas, connect-src xatosi WASM CDN'dan setWasmUrl / RuntimeLoader.setWasmUrl
Compiling … WebAssembly module violates … 'unsafe-eval' script-src da WASM ruxsati yo'q 'wasm-unsafe-eval' ('unsafe-eval' emas)
yangilanishdan keyin pleer buzildi eski WASM fayli, yangi JS build'da WASM'ni paketdan ko'chirish
ifodali Lottie ishlamaydi eval CSP'da to'silgan LottieLight, ifodasiz eksport
filtrda "o'lik" qatorlar AutoAnimate o'chirilganni qaytaradi asosiy ro'yxatda o'chirishdan oldin chiqish
sahifa sekin, LCP yomon pleer birinchi yuklashda poster + lazy

12. Mashqlar

Mashqlar kurs/mashqlar/20/29-lottie-rive/ da.

1-mashq (oson): qaysi direktiva?

  • Pleer .wasm faylini fetch bilan CDN'dan so'radi →
  • Brauzer WebAssembly modulini kompilyatsiya qilmoqchi →
  • Kutubxona <style> tegi qo'shdi →
Yechim
  • connect-src — fetch, XHR, WebSocket qayerga ulanadi.
  • script-src — 'wasm-unsafe-eval' kalit so'zi shu direktivaga yoziladi.
  • style-src — konsoldagi aniq nomi style-src-elem (stil teglari uchun) — o'lchovdagi "style-src-elem inline".

2-mashq (o'rta): harakatni kamaytirgan foydalanuvchi

«Poster va kech yuklash» bo'limidagi komponentga harakat sozlamasini qo'shing. App da «Harakatni kamaytirish» belgilash katagi (checkbox) bo'lsin, holat EmptyCart ga reduceMotion bo'lib tushsin. Katakcha belgilansa — faqat poster va Animatsiya o'chirilgan matni. Natija: katakchani belgilash.

Ishora: controlled inputlar — checked va onChange.

Yechim
jsx
import { Suspense, lazy, useState } from "react";

function FakePlayer() {
  return <p>🎞️ Animatsiya o'ynayapti</p>;
}
const Player = lazy(
  () =>
    new Promise((resolve) =>
      setTimeout(() => resolve({ default: FakePlayer }), 200),
    ),
);

function EmptyCart({ reduceMotion }) {
  const [play, setPlay] = useState(false);
  if (reduceMotion) {
    return (
      <p role="img" aria-label="Bo'sh savat">
        🧺 Animatsiya o'chirilgan
      </p>
    );
  }
  if (!play) {
    return (
      <button onClick={() => setPlay(true)}>
        Animatsiyani ko'rish
      </button>
    );
  }
  return (
    <Suspense fallback={<p>Yuklanmoqda…</p>}>
      <Player />
    </Suspense>
  );
}

export default function App() {
  const [reduce, setReduce] = useState(false);
  return (
    <div>
      <label>
        <input
          type="checkbox"
          checked={reduce}
          onChange={(e) => setReduce(e.target.checked)}
        />{" "}
        Harakatni kamaytirish
      </label>
      <EmptyCart reduceMotion={reduce} />
    </div>
  );
}

Ekranda:

text
Harakatni kamaytirish
🧺 Animatsiya o'chirilgan

Haqiqiy loyihada katakcha o'rnida tizim sozlamasi: matchMedia("(prefers-reduced-motion: reduce)").matches yoki useReducedMotion(). Ilova ichida alohida "animatsiyalarni o'chirish" sozlamasi ham foydali — ba'zi foydalanuvchilar tizim sozlamasini bilmaydi.

3-mashq (qiyin): CSP qatorini yasang

Funksiya yozing: buildCsp({ wasm, cdn }) — vazifalar CSP'sidan boshlab, kerakli ruxsatlarni qo'shadi. wasm: true — script-src ga 'wasm-unsafe-eval'. cdn — massiv: domenlar connect-src ga qo'shiladi. 'unsafe-eval' hech qachon qo'shilmasin. Direktivalar ; bilan.

Ishora: direktivalarni obyektda saqlang ({ "script-src": ["'self'"], … }), keyin Object.entries va join bilan satr yasang.

js
// ... funksiyangiz

console.log(buildCsp({ wasm: false, cdn: [] }));
console.log(buildCsp({ wasm: true, cdn: [] }));
const cdn = ["https://cdn.example.com"];
console.log(buildCsp({ wasm: true, cdn }));

Konsolda:

text
default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'
default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; connect-src 'self'
default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; connect-src 'self' https://cdn.example.com
Yechim
js
function buildCsp({ wasm, cdn }) {
  const rules = {
    "default-src": ["'self'"],
    "script-src": ["'self'"],
    "style-src": ["'self'"],
    "connect-src": ["'self'", ...cdn],
  };
  if (wasm) rules["script-src"].push("'wasm-unsafe-eval'");
  return Object.entries(rules)
    .map(([name, values]) => `${name} ${values.join(" ")}`)
    .join("; ");
}

console.log(buildCsp({ wasm: false, cdn: [] }));
console.log(buildCsp({ wasm: true, cdn: [] }));
const cdn = ["https://cdn.example.com"];
console.log(buildCsp({ wasm: true, cdn }));

Konsolda:

text
default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'
default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; connect-src 'self'
default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; connect-src 'self' https://cdn.example.com

rules har chaqiruvda yangidan yasaladi — oldingi chaqiruvning push i keyingisiga "oqib" o'tmaydi. Obyekt funksiyadan tashqarida bo'lsa, ikkinchi chaqiruvdagi 'wasm-unsafe-eval' uchinchisida ikki marta chiqardi. Haqiqiy CSP'da object-src 'none', base-uri 'none', Trusted Types qatorlari ham bor — vazifalar ning vercel.json iga qarang. cdn ni qo'shish texnik jihatdan mumkin, lekin «CSP tuzog'i» bo'limidagi savolga ko'ra — o'z serveringiz yaxshiroq.

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

Kanon Lottie/Rive'ni olmadi. O'z tajribangiz: vazifalar nusxasida bo'sh holatga lottie-react 3 bilan kichik animatsiya qo'ying (JSON — «Format» bo'limidagi kvadrat). npm run build va brauzer: birinchi yuklash qancha o'sdi, konsolda qanday CSP xatosi bor? So'ng uni poster + lazy naqshiga o'tkazing va qayta o'lchang.

Yechim
  • To'g'ridan-to'g'ri import: birinchi yuklashga ~80 KB (kanon: +80.2). Konsolda bitta style-src-elem buzilishi — o'ram stili. Animatsiya chiziladi.
  • Poster + lazy: birinchi yuklash deyarli o'zgarmaydi. Lottie alohida chunk'ga chiqadi va faqat bosilganda yuklanadi. CSP xatosi baribir bor (o'sha <style>), lekin faqat pleer yuklanganda.
  • Xulosa — kanondagidek: ish vositasi uchun pleer qimmat. Nusxani o'chiring.

13. Real ishda

  • Onboarding va bo'sh holatlar — "Hali buyurtma yo'q" ekranidagi jonli illyustratsiya, muvaffaqiyat belgilari. Bank va yetkazib berish ilovalarida ko'p uchraydi.
  • Interaktiv maskotlar va o'yinsimon UI — Rive: tugmalar, progress, "jonli" belgilar.
  • Dizayner bilan ishlash — formatni oldindan kelishing: ifodasiz Lottie, fayl hajmi chegarasi, .lottie yoki .json. Bu sizning CSP va LCP'ingizni himoya qiladi.
  • Intervyu savollari: "Lottie va Rive farqi?", "'wasm-unsafe-eval' va 'unsafe-eval' farqi?", "og'ir animatsiya pleerini sahifaga qanday qo'shasiz?", "AutoAnimate o'chirishni qanday animatsiya qiladi?".

Xulosa

  • Lottie — After Effects'dan JSON, lottie-react 3: import { Lottie } + src; +80 KB, CSP'da faqat o'ram <style> i to'siladi; ifodalar eval talab qiladi.
  • dotLottie va Rive — WASM pleer (487 va 810 KB gzip), standartda CDN'dan. Qattiq CSP'da: connect-src, keyin script-src to'sadi.
  • Davosi: WASM o'z serveringizdan (setWasmUrl, RuntimeLoader.setWasmUrl) + script-src 'wasm-unsafe-eval'. Chrome o'lchovi: CSP 0, pleer ishladi.
  • Og'ir pleer — poster + lazy + prefers-reduced-motion.
  • AutoAnimate — +2.9 KB, bitta ref. Lekin o'chirilgan elementni DOM'ga qaytaradi — asosiy ro'yxat uchun AnimatePresence dagi tuzoq.

Keyingi dars: 3D va WebGL: Three.js va React Three Fiber — 20-qismning yakuni: 3D qachon arziydi, u +233 KB turadi va vazifalar v10 qayerga keldi.

Manbalar

  • lottie-react 3.1.2 README (import { Lottie }, src, migration guide — lottiereact.com/docs/migration); lottie-web 5.13.0
  • @lottiefiles/dotlottie-react 0.20.0 / dotlottie-web 0.81.0 README: setWasmUrl, self-hosting va CSP
  • Rive React runtime (@rive-app/react-canvas 4.36.0): useRive, useStateMachineInput, RuntimeLoader.setWasmUrl — rive.app/docs
  • @formkit/auto-animate 0.10.0 manba kodi (index.mjs: remove, MutationObserver, prefers-reduced-motion)
  • MDN: CSP script-src — 'wasm-unsafe-eval'; connect-src — developer.mozilla.org/docs/Web/HTTP/Headers/Content-Security-Policy
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Lottie, Rive va AutoAnimate React'da: dizayner animatsiyalari, WASM va CSP tuzog'i — IlmHamroh