IlmHamroh
JavaScript Full-stack/20-qism. React: UI kutubxonalar va animatsiya23/30-dars21 daqiqa
Mundarija (39)

Motion (Framer Motion) React'da: motion.div, initial, animate va transition — birinchi animatsiyalar

Qisqacha: Motion (avvalgi nomi Framer Motion) — React uchun deklarativ animatsiya kutubxonasi: npm i motion, import — motion/react. Oddiy teg o'rniga motion.div yozasiz va ikki narsani aytasiz: initial — qayerdan boshlash, animate — qayerga borish. Qanday borishini transition belgilaydi: davomiylik va easing (tween) yoki prujina (spring). animate dagi qiymat o'zgarsa, Motion joriy holatdan yangi maqsadga silliq o'tadi — to'xtatilgan animatsiya ham "sinmaydi". Qiymat massiv bo'lsa (x: [0, -8, 8, 0]) — keyframes.

Bu darsda

  • framer-motion va motion paketlari farqini bilasiz va to'g'ri import qilasiz.
  • motion.div ga initial, animate, transition berib, birinchi animatsiyalarni yozasiz.
  • State o'zgarganda Motion qanday ishlashini va to'xtatilgan animatsiyani kuzatasiz.
  • Tween va spring sozlamalarini, har xususiyat uchun alohida transition ni yozasiz.
  • Keyframes massivi bilan "titrash" qilasiz va MotionConfig bilan reduced-motion'ni ulaysiz.

Oldin bilishingiz kerak: UI animatsiya tamoyillari va performance, CSS animatsiyalari React'da, Web Animations API.

1. Nega bu kerak?

Oldingi darsda vazifalar dagi oyna, menyu va tooltip CSS bilan jonlandi. Endi ro'yxat qatorlari navbatda. Jasur aka: "yangi vazifa qo'shilganda ko'z uni darhol topsin".

Sardor CSS bilan urinib ko'rdi va to'rtta muammoga duch keldi:

  • Qaysi qator "yangi"? Sahifa ochilganda 30 ta qator bor — ular animatsiyasiz chiqishi kerak. Faqat keyin qo'shilgani jonlansin. Bu ma'lumot React state'ida, CSS'da emas.
  • Tez almashadigan holat. Almashtirgichni (switch) tez-tez bossangiz, CSS keyframe har safar boshidan boshlaydi.
  • Qiymat JavaScript'da. Sudrash masofasi, ro'yxatdagi tartib — CSS ularni bilmaydi.
  • Chiqish. React elementni o'chirganda animatsiyani kutish kerak — buni qo'lda yozdik va qiyin bo'ldi.

Motion bu ishlarni deklarativ qiladi: "qayerga" deysiz, "qanday" — kutubxonaning ishi. React'ning o'zi kabi: UI = f(state), animatsiya = f(state).

Hayotiy o'xshatish: taksi. Siz manzilni aytasiz — "Chorsu". Qaysi ko'chadan, qaysi tezlikda borishni haydovchi o'zi hal qiladi. Yo'lda manzilni "Amir Temur xiyoboni" ga o'zgartirsangiz, u to'xtab boshidan boshlamaydi — shu joydan burilib ketadi.

2. Paket: Framer Motion endi Motion

2.1 O'rnatish va import

Kutubxona uzoq vaqt Framer Motion nomi bilan tanilgan (Framer — dizayn vositasi kompaniyasi). Keyin u mustaqil loyiha bo'ldi va Motion nomini oldi. Yangi paket — motion:

bash
npm i -E motion@14.0.0
jsx
import { motion } from "motion/react"; // ✅ hozirgi yo'l
import { motion } from "framer-motion"; // eski kodda uchratasiz

framer-motion paketi ham hali chiqadi. motion aynan uning ustiga qurilgan: npm ls qilsangiz, motion@14.0.0 ostida framer-motion@14.0.0 ni ko'rasiz. API bir xil. Eski loyihani ko'chirish — importni motion/react ga almashtirish. Ikkala paketni bir loyihada aralashtirmang: kod ikki marta to'planadi.

motion paketida React'siz qism ham bor: import { animate } from "motion" — oddiy JavaScript'da DOM elementini animatsiya qilish uchun. Bu darsda faqat React qismi.

2.2 Saytdagi natija oynasida

motion va motion/react natija oynasida ishlaydi — har misolni «Ishga tushir» bilan ko'ring. Bir farqni yodda tuting. Kurs tekshiruvchisi brauzersiz muhitda ishlaydi: u yerda Motion animatsiyani vaqt bo'yicha JavaScript bilan hisoblaydi. Biz natijani yakuniy holat (Ekranda:) va Motion hodisalari (onAnimationStart, onAnimationComplete) bilan ko'rsatamiz. Harakatning o'zini natija oynasida ko'rasiz.

3. Birinchi animatsiya: initial va animate

3.1 motion.div

Har HTML va SVG tegining motion. versiyasi bor: motion.div, motion.li, motion.button, motion.svg. Ular oddiy teg kabi ishlaydi — barcha props'lar, ref, hodisalar. Qo'shimchasiga animatsiya props'larini qabul qiladi:

jsx
import { useRef } from "react";
import { motion } from "motion/react";

export default function App() {
  const ref = useRef(null);
  return (
    <motion.div
      ref={ref}
      initial={{ opacity: 0, x: -20 }}
      animate={{ opacity: 1, x: 0 }}
      transition={{ duration: 0.3 }}
      onAnimationStart={() => console.log("boshlandi")}
      onAnimationComplete={(target) => {
        console.log("tugadi:", JSON.stringify(target));
        console.log("style:", ref.current.style.cssText);
      }}
    >
      Yangi buyurtma: osh
    </motion.div>
  );
}

Konsolda:

text
boshlandi
tugadi: {"opacity":1,"x":0}
style: opacity: 1; transform: none;

Ekranda:

text
Yangi buyurtma: osh

Natija oynasida yozuv chapdan siljib, so'nib paydo bo'ladi. Qatorma-qator:

  • initial — birinchi chizilishdagi qiymatlar. Element opacity: 0 va 20 px chapda paydo bo'ladi.
  • animate — maqsad. Motion darhol shu qiymatlarga olib boradi.
  • transition — qanday: 0.3 soniya. Motion'da vaqt soniyada, CSS'dagidek millisekundda emas.
  • onAnimationComplete — tugaganda; argumenti — erishilgan maqsad (animate dagi obyekt).

Oxirgi qatorga qarang: x: 0 DOM'da transform: none bo'ldi. x — Motion'ning alohida transform qiymati: Motion uni transform: translateX(...) ga yig'adi va nolga yetganda tozalaydi. Xuddi shunday y, scale, scaleX, rotate. Ularni alohida-alohida animatsiya qilish mumkin — CSS'dagi bitta transform satri bilan bu qiyin.

3.2 initial={false}

initial yozilmasa, Motion boshlang'ich qiymatni elementning hozirgi uslubidan oladi. initial={false} esa "birinchi chizilishda animatsiya qilma, darhol animate dagi qiymatda bo'l" degani. Sahifa ochilganda allaqachon bor narsalar uchun aynan shu kerak: ular "uchib kirmasin", faqat keyingi o'zgarishlar jonlansin.

Tekshirib ko'ring: transition={{ duration: 300 }} yozilsa, nima bo'ladi?

Javob

Animatsiya 300 soniya — 5 daqiqa davom etadi. Element deyarli qimirlamaydi va "animatsiya ishlamayapti" degan taassurot qoladi. Motion'da duration va delay soniyada: 300 ms → 0.3. CSS'dan kelganlarning eng ko'p uchraydigan xatosi.

4. State o'zgarganda

4.1 animate — state'dan

animate ichidagi qiymat state'ga bog'liq bo'lsa, state o'zgarganda Motion eski qiymatdan yangisiga o'tadi. Almashtirgich (switch) misolida tugmani tez ikki marta bosamiz — ikkinchi bosish birinchi animatsiyani yarim yo'lda to'xtatadi:

jsx
import { useState } from "react";
import { motion } from "motion/react";

export default function App() {
  const [on, setOn] = useState(false);
  return (
    <div>
      <button
        role="switch"
        aria-checked={on}
        onClick={() => setOn(!on)}
      >
        {on ? "O'chirish" : "Yoqish"}
      </button>
      <motion.div
        initial={false}
        animate={{ x: on ? 24 : 0 }}
        onAnimationStart={() => console.log("boshlandi, on =", on)}
        onAnimationComplete={(target) =>
          console.log("tugadi:", JSON.stringify(target))
        }
        style={{ width: 24, height: 24, background: "#1d4ed8" }}
      />
    </div>
  );
}

Konsolda:

text
boshlandi, on = true
boshlandi, on = false
tugadi: {"x":0}

Uchta narsaga qarang:

  1. Ikkita "boshlandi", bitta "tugadi". Birinchi animatsiya (x: 24 ga) yo'lda to'xtatildi. Uning onAnimationComplete i chaqirilmadi. Faqat oxirgi maqsadga yetgani tugadi.
  2. Sakrash yo'q. Ikkinchi animatsiya x = 0 dan emas, quti hozir turgan joydan boshlandi. Joriy tezlik ham saqlandi — chunki x uchun Motion sukut bo'yicha spring ishlatadi (UI animatsiya tamoyillari darsidagi "to'xtatish" afzalligi).
  3. initial={false} — sahifa ochilganda quti joyida edi, "boshlandi" faqat bosishdan keyin keldi.

role="switch" va aria-checked — Malika uchun: "almashtirgich, yoqilgan". Animatsiya faqat ko'rinish, holat esa ARIA'da.

4.2 Har render yangi animatsiya emas

Motion animate dagi qiymatlarni oldingisi bilan solishtiradi. Qiymat o'zgarmagan bo'lsa (masalan, boshqa sabab bilan qayta render), animatsiya boshlanmaydi. Shuning uchun animate={{ opacity: 1 }} ni har renderda yangi obyekt qilib yozish xavfsiz.

5. transition: qanday borish

5.1 Tween

Tween — davomiylik va easing:

jsx
transition={{ duration: 0.2, ease: "easeOut" }}
// cubic-bezier:
transition={{ duration: 0.2, ease: [0.16, 1, 0.3, 1] }}
transition={{ duration: 0.2, delay: 0.1 }}

Easing nomlari CSS'dan biroz farq qiladi: "linear", "easeIn", "easeOut", "easeInOut" (camelCase), yana "circOut", "backOut", "anticipate". Massiv — CSS'dagi cubic-bezier ning to'rt soni.

5.2 Spring

Spring — prujina. Ikki xil sozlanadi:

jsx
// 1) Fizika bilan: qattiqlik, so'ndirish, massa
transition={{ type: "spring", stiffness: 300, damping: 20 }}

// 2) Ko'z bilan: ko'rinadigan davomiylik va "sakrash"
transition={{ type: "spring", visualDuration: 0.3, bounce: 0.25 }}

Ikkinchi shakl tushunarliroq: visualDuration — element maqsadga birinchi marta yetguncha bo'lgan vaqt. bounce — 0 (sakrashsiz) dan 1 gacha. Prujinaning "tebranishi" visualDuration dan keyin ham biroz davom etadi — lekin ko'z uchun animatsiya o'sha vaqtda tugagan bo'ladi.

Sukut bo'yicha nima? type yozilmasa, Motion qiymatga qarab tanlaydi. x, y, scale, rotate kabi "jismoniy" qiymatlar uchun — spring. opacity va ranglar uchun — tween (0.3 s). Shuning uchun almashtirgich misoli transition siz ham "tirik" harakatlandi.

5.3 Har xususiyatga alohida

Ko'pincha x prujina bilan, opacity esa qisqa tween bilan kerak bo'ladi:

jsx
import { useRef } from "react";
import { motion } from "motion/react";

export default function App() {
  const ref = useRef(null);
  return (
    <motion.div
      ref={ref}
      initial={{ opacity: 0, scale: 0.5 }}
      animate={{ opacity: 1, scale: 1 }}
      transition={{
        default: { type: "spring", visualDuration: 0.3, bounce: 0.3 },
        opacity: { duration: 0.15, ease: "easeOut" },
      }}
      onAnimationComplete={() =>
        console.log("style:", ref.current.style.cssText)
      }
    >
      Savatga qo'shildi
    </motion.div>
  );
}

Konsolda:

text
style: opacity: 1; transform: none;

default — hamma boshqa qiymatlar uchun (bu yerda scale). opacity — o'zining 0.15 s lik tween'i. Yozuv tez paydo bo'ladi va bir oz "sakrab" o'z o'lchamiga keladi.

transition ni animate ichiga ham yozish mumkin: animate={{ x: 0, transition: { duration: 0.2 } }} — faqat shu maqsad uchun.

5.4 Takrorlash

jsx
// Yuklanish belgisi: abadiy aylanadi
<motion.div
  animate={{ rotate: 360 }}
  transition={{ repeat: Infinity, duration: 1, ease: "linear" }}
/>

repeat — necha marta (Infinity — abadiy), repeatType: "reverse" — borib-qaytib. Abadiy animatsiyani faqat yuklanish kabi vaqtinchalik holatlarda ishlating — doimiy harakat diqqatni o'g'irlaydi.

Tekshirib ko'ring: Dizayner "tugma bosilganda bir oz kattalashib qaytsin, lekin sakramasin" dedi. Qaysi transition ni tanlaysiz?

Javob

{ type: "spring", visualDuration: 0.2, bounce: 0 } — prujina, lekin sakrashsiz: tez boradi va yumshoq to'xtaydi. Yoki tween: { duration: 0.15, ease: "easeOut" }. Bosish tez-tez takrorlansa, spring yaxshiroq: keyingi bosish oldingisini "to'xtatsa", harakat sinmaydi.

6. Keyframes massivi

6.1 Bir nechta nuqta orqali

animate dagi qiymat massiv bo'lsa — bu keyframes: Motion qiymatni shu nuqtalar orqali olib o'tadi. Noto'g'ri PIN kiritilganda xabar chapga-o'ngga titraydi:

jsx
import { useState } from "react";
import { motion } from "motion/react";

export default function App() {
  const [attempt, setAttempt] = useState(0);
  return (
    <div>
      <button onClick={() => setAttempt(attempt + 1)}>Kirish</button>
      {attempt > 0 && (
        <motion.p
          key={attempt}
          role="alert"
          animate={{ x: [0, -8, 8, -8, 0] }}
          transition={{ duration: 0.4 }}
          onAnimationComplete={(target) =>
            console.log("titradi:", JSON.stringify(target))
          }
        >
          PIN noto'g'ri ({attempt}-urinish)
        </motion.p>
      )}
    </div>
  );
}

Konsolda:

text
titradi: {"x":[0,-8,8,-8,0]}
titradi: {"x":[0,-8,8,-8,0]}

Ekranda:

text
Kirish
PIN noto'g'ri (2-urinish)

Har urinishda key o'zgaradi — xabar yangidan yasaladi va titrash qaytadan boshlanadi (CSS animatsiyalari React'da darsidagi key usuli). Massivdagi 5 nuqta 0.4 s ga teng taqsimlandi.

6.2 times va null

  • times — har nuqta qachon kelishi (0 dan 1 gacha): transition={{ duration: 1, times: [0, 0.2, 1] }} — ikkinchi nuqtaga vaqtning 20% ida.
  • null — "hozirgi qiymat": animate={{ x: [null, 100, 0] }} — qayerda bo'lsa, o'sha joydan boshlaydi.

Keyframes sukut bo'yicha tween (0.8 s — bir nechta nuqta bo'lsa). Prujina faqat ikki nuqta orasida ishlaydi.

7. Qanday tez ishlaydi

7.1 Kompozitor va JavaScript kadrlari

Motion ichida ikki yo'l bor (manba: motion-dom 14.0.0 kodi, supportsBrowserAnimation):

  • opacity, transform (butun satr), filter, clipPath, backgroundColor — imkon bo'lsa, brauzerning Web Animations API'siga (WAAPI) topshiriladi. Animatsiya kompozitorda ishlaydi: asosiy oqim band bo'lsa ham silliq.
  • x, y, scale, rotate (alohida transform'lar) va boshqalar — Motion o'zi har kadrda hisoblaydi va style.transform ga yozadi. Bu asosiy oqimda ishlaydi, lekin baribir faqat transform — layout yo'q.

WAAPI'ga topshirish o'chadigan holatlar ham bor: masalan onUpdate callback'i berilsa (har kadrda qiymat kerak — WAAPI uni bera olmaydi).

Amaliy xulosa: animatsiya UI animatsiya tamoyillari darsidagi qoidaga mos bo'lsa (opacity va transform), Motion uni tez qiladi. width, height, top ni Motion ham layout'siz animatsiya qila olmaydi. Bunday o'zgarish uchun layout props'i bor — Motion III darsida.

7.2 React render'siz

Animatsiya paytida Motion komponentingizni qayta render qilmaydi. Qiymatlar React state'ida emas, Motion ichidagi "motion value" larda. Biz o'tgan darslarda ref bilan qo'lda qilgan ish shu: React — "qayerga", Motion — har kadr.

8. prefers-reduced-motion: MotionConfig

Motion o'zi reduced-motion'ni bilmaydi — siz aytishingiz kerak. Eng oson yo'l — ilovaning tepasida MotionConfig:

jsx
import { MotionConfig } from "motion/react";

<MotionConfig reducedMotion="user">
  <App />
</MotionConfig>

reducedMotion="user" — "foydalanuvchining tizim sozlamasiga qara". Harakatni kamaytirish yoqilgan bo'lsa, transform animatsiyalari (x, y, scale, rotate) o'chadi — element darhol maqsadda bo'ladi. opacity va rang esa qoladi — UI animatsiya tamoyillari darsidagi "harakatni olib tashla, ma'noni qoldir" qoidasi o'zi bajariladi. Boshqa qiymatlar: "always" (doim kamaytir — sinash uchun qulay), "never".

MotionConfig ga umumiy transition ham berish mumkin: <MotionConfig transition={{ duration: 0.2 }}> — ichidagi hamma motion komponentlar uchun sukut.

Komponentning o'zida tekshirish kerak bo'lsa — useReducedMotion() hook'i bor. Uni Motion IV darsida, kanon kodida ko'ramiz.

9. O'z komponentingiz: motion.create

motion. faqat HTML/SVG teglari uchun. O'z Tugma komponentingizni jonlantirmoqchi bo'lsangiz:

jsx
import { motion } from "motion/react";
import { Tugma } from "@/ui/Tugma.tsx";

const MotionTugma = motion.create(Tugma);

<MotionTugma whileTap={{ scale: 0.97 }}>Saqlash</MotionTugma>

Shart — Radix'dagi asChild kabi: Tugma ref va qolgan props'ni <button> ga yoyishi kerak. Motion animatsiyani ref orqali haqiqiy DOM elementiga qo'llaydi. Eski kodda motion(Tugma) yozuvini ko'rasiz — bu motion.create ning eski shakli.

10. Narx: Motion qancha turadi

Kanon kutubxonalar tajribasida (natijalar/tajriba/kutubxonalar.txt, bir xil kichik ro'yxat):

Motion narxi: JS gzip (faqat React ustiga)
  • to'liq motion.* (layout bilan)40,4 KB
  • LazyMotion + m (sinxron)27,1 KB
  • vazifalar: birinchi yuklash+14.5 KB keyin14,2 KB

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

Grafikda nimaga qarang: to'liq motion.* +40.4 KB — React Flow'dan sal kam. Kanon uni ikki bo'lakka bo'ldi: sahifa ochilganda faqat 14.2 KB, animatsiya "yadrosi" (14.5 KB) esa birinchi chizishdan keyin alohida fayl bo'lib keladi. Bu LazyMotion va m komponentlari bilan qilinadi — Motion IV darsida batafsil. CSP: 0 buzilish — Motion uslubni style xususiyatlari orqali beradi.

Va INP: UI animatsiya tamoyillari darsidagi o'lchovda qatorlardagi Motion katak bosishga taxminan +8 ms qo'shgan edi. Har qatorga motion.li qo'yishdan oldin o'ylang.

11. Vazifalar qadami: yangi qator jonlanadi (holat 25 boshlanadi)

11.1 Reja

Jasur akaning talabi: yangi vazifa qo'shilganda u ko'zga tashlansin. Kanon qarorlari:

  • Faqat kirish — yangi qator yuqoridan 8 px tushib, so'nib paydo bo'ladi. 150 ms, easeOut, faqat opacity va y.
  • Faqat ochilgandan keyin qo'shilgan qator. Sahifa ochilganda bor qatorlar jim turadi.
  • Chiqish — Motion emas. O'chirilayotgan qator brauzerning WAAPI'si bilan chiqadi (Motion III darsida — nega AnimatePresence emas, o'lchov bilan).
  • Reduced-motion — umuman animatsiyasiz, opacity ham.
bash
git switch -c feat/motion
npm i -E motion@14.0.0

11.2 qator-harakati.ts: kirish sozlamasi

Yangi fayl src/ui/qator-harakati.ts (kanon, holat 25; kirish qismi):

ts
// qator-harakati.ts — ro'yxat qatorlari animatsiyasi (20/#23–#26).
// Faqat opacity va transform — kompozitor, layout qayta hisoblanmaydi
// (20/#21 tamoyili). Davomiylik qisqa: 150 ms.
import { useReducedMotion } from "motion/react";
import type { HTMLMotionProps } from "motion/react";

type QatorHarakati = Pick<
  HTMLMotionProps<"li">,
  "initial" | "animate" | "transition"
>;

/** @param yangi — ro'yxat ochilgandan KEYIN qo'shilgan qator */
export function useQatorHarakati(yangi: boolean): QatorHarakati {
  // prefers-reduced-motion — animatsiyasiz (opacity ham)
  const kamaytir = useReducedMotion() ?? false;
  return {
    initial: yangi && !kamaytir ? { opacity: 0, y: -8 } : false,
    animate: { opacity: 1, y: 0 },
    transition: { duration: 0.15, ease: "easeOut" },
  };
}

(Kanon fayli boshidagi izohning chiqish haqidagi qismi va chiqishAnimatsiyasi funksiyasi — Motion III darsida.)

Bugungi darsning hammasi shu 10 qatorda:

  • initial — yangi qator uchun { opacity: 0, y: -8 }, qolganlar uchun false (animatsiyasiz, «initial={false}» bo'limidagidek).
  • animate — doim bir xil maqsad. Qiymat o'zgarmagani uchun keyingi render'lar animatsiya boshlamaydi.
  • transition — tween, 0.15 s, easeOut. Spring emas: qator to'xtatilmaydi, sakrash kerak emas.
  • HTMLMotionProps<"li"> — Motion'ning motion.li props turlari. Pick bilan faqat uchtasini oldik (Utility types darsidagi Pick).
  • useReducedMotion() — MotionConfig dan farqli: u opacity ni ham o'chiradi (initial: false). Kanon bu yerda "hech qanday animatsiya" ni tanladi.

11.3 Qator.tsx va HarakatSozlamasi

Qator.tsx da to'liq ro'yxatdagi <li> endi m.li (kanon, holat 25):

diff
+import * as m from "motion/react-m";
+import { useQatorHarakati } from "../ui/qator-harakati.ts";
...
+  const harakat = useQatorHarakati(yangi);
+  // 20/#25: to'liq ro'yxatda — m.li (yangi qator paydo bo'lishi);
+  // virtual ro'yxatda — oddiy li (aylantirishda chizilganlar
+  // animatsiyasiz)
+  if (joy === undefined) {
+    return (
+      <m.li
+        data-id={id}
+        className={bajarildi ? "bajarildi" : undefined}
+        {...harakat}
+      >
+        {children}
+      </m.li>
+    );
+  }

m.li — motion.li ning "yengil" shakli: uning ichida animatsiya mexanizmi yo'q, mexanizm alohida yuklanadi. Hozircha m ni motion deb o'qing — farqi Motion IV darsida. Virtual ro'yxatda (virtualizatsiya) esa oddiy <li>: aylantirganda ekranga kirgan qatorlar "yangi" emas, ular jonlanmasligi kerak.

Butun ilova uchun sozlama — yangi src/ui/HarakatSozlamasi.tsx (kanon):

tsx
// HarakatSozlamasi.tsx — Motion sozlamasi (20/#23–#26): LazyMotion
// (yadro kerak bo'lganda) + MotionConfig. reducedMotion="user" —
// tizimda "harakatni kamaytirish" yoqilgan bo'lsa transform
// animatsiyalari o'chadi; opacity esa qoladi, shuning uchun qatorlar
// o'zi ham useReducedMotion'ga qaraydi
import { LazyMotion, MotionConfig } from "motion/react";
import type { ReactNode } from "react";

const yadro = () =>
  import("./harakat-imkoniyatlari.ts").then((m) => m.default);

export function HarakatSozlamasi({
  children,
}: {
  children: ReactNode;
}) {
  return (
    // strict — to'liq motion.* ni LazyMotion ichida taqiqlaydi (u
    // butun yadroni birinchi yuklashga qaytaradi: +26 KB gzip). Lekin
    // tekshiruv faqat Node'da ishlaydi (Motion `typeof process` ga
    // qaraydi): brauzerda — dev'da ham, build'da ham — JIM. Vitest'da
    // ham ushlamaydi: testlar App'ni bu provider'siz chizadi. Haqiqiy
    // himoya — lint (eslint.config.js: motion/react dan `motion`
    // importi taqiqlangan)
    <LazyMotion features={yadro} strict>
      <MotionConfig reducedMotion="user">{children}</MotionConfig>
    </LazyMotion>
  );
}

(Izoh qatorlari telefonga sig'ishi uchun qayta bo'lingan.) Bugun bizga MotionConfig reducedMotion="user" muhim — «MotionConfig» bo'limidagi sozlama. LazyMotion va yadro — Motion IV mavzusi. Qobiq.tsx butun sahifani shu komponentga o'raydi.

strict haqidagi izohga e'tibor bering. Nomi "qattiq tekshiruv" ga o'xshaydi, lekin brauzerda u hech narsa demaydi — kanon buni o'lchab ko'rdi. Shuning uchun haqiqiy himoya — lint qoidasi. Uni Motion IV da, holat 25 commit'idan oldin qo'shamiz.

Qaysi qator "yangi" ekanini kim aytadi (yangi props'i qayerdan keladi)? Bu keyingi darsda — u yerda ro'yxat darajasidagi qaror va nega "birin-ketin" (stagger) animatsiya qilmaganimiz.

11.4 Kanon tekshiruvlari

Holat 25 ning e2e ssenariysidagi (Chrome 155) shu darsga tegishli qatorlar:

text
✅ yangi qator: kirish animatsiyasi — faqat opacity va transform: true
✅ Motion: CSP buzilishi yo'q: []
✅ reduced-motion: qatorda animatsiya yo'q, o'chirilgan qator darhol yo'q: [0,false]

Ssenariy Chrome'da Element.prototype.animate ni (WAAPI) kuzatadi. Birinchi qator: yangi <li> ga brauzerga topshirilgan animatsiyalarda faqat opacity va transform bor. Uchinchisi: reduced-motion'da qatorlarga bitta ham shunday animatsiya topshirilmadi. Commit hali yo'q: holat 25 Motion darslarining oxirida bitta commit bo'ladi.

12. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
animatsiya "ishlamaydi", element qimirlamaydi duration: 300 — 5 daqiqa soniyada: 0.3
sahifa ochilganda hamma narsa uchib kiradi initial hamma elementda mavjudlariga initial={false}
style={{ transform }} yo'qoldi Motion x/scale bilan transform ni o'zi yozadi style da ham x, scale ishlating
motion(Tugma) animatsiya qilmaydi ref yoki props <button> ga yetmaydi {...rest} va ref; motion.create
bundle'da framer-motion ikki marta framer-motion va motion importlari aralash hammasi motion/react dan
reduced-motion foydalanuvchida ham uchadi MotionConfig yo'q reducedMotion="user"
sekin, qaltiraydi width, height, top animatsiyasi scale, x, y yoki layout

13. Mashqlar

Mashqlar kurs/mashqlar/20/23-motion-i/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): props nomlari

Bo'sh joylarni to'ldiring:

  • Birinchi chizilishdagi qiymat —
  • Maqsad qiymatlar —
  • 200 ms ni Motion'da yozish: duration:
  • Sukut bo'yicha x qanday animatsiya qilinadi:
Yechim
  • initial — boshlanish, animate — maqsad.
  • duration: 0.2 — Motion vaqti soniyada.
  • x, scale, rotate — sukut bo'yicha spring; opacity va rang — tween.

2-mashq (o'rta): progress chizig'i — scaleX bilan

Buyurtma holati: «Qabul» → «Oshxona» → «Yo'lda» → «Yetkazildi». «Keyingi bosqich» tugmasi chiziqni 25% ga to'ldirsin.

  1. Chiziq width bilan emas, scaleX bilan to'lsin; originX: 0 — chapdan.
  2. transition — tween, 0.3 s, easeOut.
  3. Animatsiya tugaganda konsolga yetib kelgan qiymat chiqsin.
  4. Chiziq ostida bosqich nomi yozilsin. Chiziq role="progressbar" va aria-valuenow bilan.

Ishora: animate={{ scaleX: step / 4 }}. originX — Motion'da transform-origin ning x qismi, style ga yoziladi.

Yechim
jsx
import { useState } from "react";
import { motion } from "motion/react";

const steps = ["Qabul", "Oshxona", "Yo'lda", "Yetkazildi"];

export default function App() {
  const [step, setStep] = useState(1);
  return (
    <div>
      <div
        role="progressbar"
        aria-valuemin={0}
        aria-valuemax={4}
        aria-valuenow={step}
        aria-label="Buyurtma holati"
        style={{ height: 8, background: "#e8edfb" }}
      >
        <motion.div
          initial={false}
          animate={{ scaleX: step / 4 }}
          transition={{ duration: 0.3, ease: "easeOut" }}
          onAnimationComplete={(target) =>
            console.log("yetdi:", target.scaleX)
          }
          style={{ height: 8, background: "#1d4ed8", originX: 0 }}
        />
      </div>
      <p>{steps[step - 1]}</p>
      <button
        onClick={() => setStep((s) => Math.min(4, s + 1))}
        disabled={step === 4}
      >
        Keyingi bosqich
      </button>
    </div>
  );
}

Konsolda:

text
yetdi: 0.5
yetdi: 0.75

Ekranda:

text
Yo'lda
Keyingi bosqich

initial={false} — sahifa ochilganda chiziq allaqachon 25% da, animatsiyasiz. Har bosishda scaleX 0.25 ga oshadi va animatsiya o'sha nuqtadan boshlanadi. originX: 0 bo'lmasa, chiziq o'rtadan ikki tomonga o'sardi. disabled={step === 4} — oxirida tugma o'chadi.

3-mashq (qiyin): "Savatga qo'shildi" belgisi

Taom kartochkasidagi «Savatga» tugmasi bosilganda:

  1. Tugma yonida «+1» belgisi paydo bo'ladi: pastdan 10 px ko'tarilib, so'nib chiqadi (opacity va y).
  2. Keyin keyframes bilan bir marta "lip" etadi: scale: [1, 1.2, 1].
  3. Har bosishda belgi qaytadan boshlanadi; ichida jami soni (+1, +2…).
  4. Belgi aria-live="polite" hudud ichida: Malika "Savatda 2 ta" ni eshitsin.
  5. Hamma narsa MotionConfig reducedMotion="user" ichida.

Ishora: key={count}; initial={{ opacity: 0, y: 10, scale: 1 }}, animate={{ opacity: 1, y: 0, scale: [1, 1.2, 1] }}. Har qiymatga alohida transition: «Har xususiyatga alohida» bo'limi.

Yechim
jsx
import { useState } from "react";
import { MotionConfig, motion } from "motion/react";

export default function App() {
  const [count, setCount] = useState(0);
  return (
    <MotionConfig reducedMotion="user">
      <p>Osh — 35 000 so'm</p>
      <button onClick={() => setCount(count + 1)}>
        Savatga
      </button>{" "}
      <span aria-live="polite">
        {count > 0 && (
          <motion.span
            key={count}
            style={{ display: "inline-block" }}
            initial={{ opacity: 0, y: 10, scale: 1 }}
            animate={{ opacity: 1, y: 0, scale: [1, 1.2, 1] }}
            transition={{
              opacity: { duration: 0.15 },
              y: { type: "spring", visualDuration: 0.2, bounce: 0 },
              scale: { duration: 0.3, delay: 0.15 },
            }}
            onAnimationComplete={() => console.log("lip:", count)}
          >
            +{count} <span className="sr">(Savatda {count} ta)</span>
          </motion.span>
        )}
      </span>
    </MotionConfig>
  );
}

Konsolda:

text
lip: 1
lip: 2

Ekranda:

text
Osh — 35 000 so'm
Savatga +2 (Savatda 2 ta)

Uch qiymat — uch xil transition: opacity tez paydo bo'ladi, y sakrashsiz prujina bilan ko'tariladi, scale esa 0.15 s kutib (delay), keyin bir marta "lip" etadi. display: inline-block — span ga transform ta'sir qilishi uchun. Haqiqiy loyihada "(Savatda 2 ta)" qismi ko'zdan yashiriladi (sr-only sinfi, React'da accessibility), lekin ekran o'quvchi uni o'qiydi. Reduced-motion yoqilgan bo'lsa: y va scale o'chadi, belgi faqat so'nib paydo bo'ladi.

4-mashq: Vazifalar qadami

feat/motion branch'ida: motion@14.0.0 ni aniq versiya bilan o'rnating; src/ui/qator-harakati.ts — useQatorHarakati; src/ui/HarakatSozlamasi.tsx va Qobiq.tsx ni unga o'rash; Qator.tsx — to'liq ro'yxatda m.li. Commit qilmang.

Yechim

Tekshiruv ro'yxati:

  • package.json da "motion": "14.0.0" (^ siz).
  • Vazifa qo'shing — yangi qator tepadan tushib so'nib chiqadi; sahifani yangilang — mavjud qatorlar jim.
  • DevTools → Rendering → prefers-reduced-motion: reduce — yangi qator darhol paydo bo'ladi.
  • Hamma joyda m.*, motion.* emas — buni ko'z bilan tekshiring. Konsolga ishonmang: strict brauzerda jim. Lint qoidasi Motion IV da qo'shiladi.
  • npm run tip — 0 xato (HTMLMotionProps turlari mos).

14. Real ishda

  • Motion — React ekotizimidagi eng ko'p ishlatiladigan animatsiya kutubxonalaridan biri. Landing sahifalar, dashboard'lar, mobil-veb ilovalar. Intervyuda "Framer Motion bilan ishlaganmisiz?" ko'p so'raladi — endi to'g'ri javob: "ha, hozir u Motion, motion/react".
  • Dizayn tizimlari — transition sozlamalari tokenlanadi (spring.snappy, duration.fast) va MotionConfig orqali butun ilovaga beriladi.
  • Performans ko'rigi — motion.* qayerlarda ishlatilgani va bundle hajmi. Har qatorga motion.li — o'ylab qo'yiladigan qaror.
  • Intervyu savollari: "initial, animate, transition nima?", "spring va tween qachon?", "Motion qaysi qiymatlarni GPU'da animatsiya qiladi?", "reduced-motion'ni Motion'da qanday hurmat qilasiz?".

Xulosa

  • Paket — motion, import — motion/react (framer-motion — eski nom, API bir xil).
  • motion.div: initial → animate, transition bilan. initial={false} — birinchi chizilishda animatsiyasiz.
  • animate state'dan: qiymat o'zgarsa — joriy holatdan yangi maqsadga; to'xtatilgan animatsiyaning onAnimationComplete i chaqirilmaydi.
  • transition: vaqt soniyada; tween (duration, ease) yoki spring (visualDuration, bounce / stiffness, damping); har qiymatga alohida — default.
  • Keyframes — massiv; times, null. Qayta boshlash — key.
  • MotionConfig reducedMotion="user" — transform o'chadi, opacity qoladi. Narx: to'liq +40.4 KB; kanonda 14.2 + 14.5 (keyin).

Keyingi dars: Motion II: variants, stagger va gestures — nomlangan holatlar, ota-bola orkestratsiyasi, whileHover, whileTap va sudrash; vazifalar da qaysi qator "yangi" ekanini hal qilamiz.

Manbalar

  • Motion for React: Get started, Animation, Transitions, MotionConfig — motion.dev/docs/react (motion 14.0.0)
  • Motion: Upgrade guide (framer-motion → motion) — motion.dev/docs/react-upgrade-guide
  • motion-dom 14.0.0 manba kodi: supportsBrowserAnimation (WAAPI'ga topshiriladigan qiymatlar)
  • MDN: Web Animations API — developer.mozilla.org/docs/Web/API/Web_Animations_API
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Motion (Framer Motion) React'da: motion.div, initial, animate va transition — birinchi animatsiyalar — IlmHamroh