Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. Variants: holatlarga nom berish
- 2.1 Obyekt o'rniga nom
- 2.2 Nom bolalarga o'tadi
- 2.3 staggerChildren — eski kodda
- 3. Orkestratsiya
- 3.1 when: ota va bolalar navbati
- 3.2 Dinamik variants: custom
- 3.3 Stagger qachon o'rinli
- 4. Gestures: foydalanuvchi harakatiga javob
- 4.1 whileHover, whileTap, whileFocus
- 4.2 onTap va onClick: amal qayerda?
- 4.3 Bosish javobi — qancha?
- 5. Drag: sudrash
- 5.1 drag va dragConstraints
- 5.2 A11y: sudrash — yagona yo'l bo'lmasin
- 5.3 Motion drag yoki dnd-kit?
- 6. Harakatni kamaytirish: variants va gestures
- 7. Narx va LazyMotion
- 8. Vazifalar qadami: qaysi qator "yangi"? (holat 25 davomi)
- 8.1 Muammo
- 8.2 Yechim: birinchi chizishdagi id'lar
- 8.3 Sudrash va Motion bitta qatorda
- 8.4 Kanon tekshiruvi
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): qaysi props?
- 2-mashq (o'rta): menyu kartochkalari
- 3-mashq (qiyin): "Sevimli" tugmasi
- 4-mashq: Vazifalar qadami
- 11. Real ishda
- Xulosa
- Manbalar
Motion variants, stagger va gestures: whileHover, whileTap va drag — React'da jonli interfeys
Qisqacha: Variants — animatsiya holatlariga nom berish:
{ yopiq: {...}, ochiq: {...} }. Otaanimate="ochiq"desa, ichidagi hammamotionbolalar ham shu nomdagi holatga o'tadi — props uzatmasdan. Ota esa bolalarni orkestrlaydi:delayChildren: stagger(0.08)— birin-ketin,when: "beforeChildren"— avval ota. Gestures —whileHover,whileTap,whileFocus(holat faqat harakat davomida) vadrag(sudrash,dragConstraintsbilan chegara). Amal har doimonClickda, gesture — faqat ko'rinish; sudrashning tugmali muqobili bo'lsin.
Bu darsda
- Variants bilan holatlarga nom berasiz va ularni ota-boladan bolaga uzatasiz.
stagger,delayChildren,whenbilan ketma-ketlikni boshqarasiz va dinamik variants (custom) yozasiz.- Stagger qachon o'rinli, qachon xalaqit berishini o'lchov bilan ajratasiz.
whileHover,whileTap,whileFocusni a11y bilan to'g'ri ishlatasiz: sensor ekran, klaviatura,onTapvaonClickfarqi.drag,dragConstraints,onDragEndbilan "surib o'chirish" ni yozasiz vavazifalarda qaysi qator "yangi" ekanini hal qilasiz.
Oldin bilishingiz kerak: Motion I: motion komponentlar va asosiy animatsiya, Drag and drop: dnd-kit, Fokus boshqaruvi va klaviatura navigatsiyasi.
1. Nega bu kerak?
Oldingi darsda bitta elementni jonlantirdik: initial → animate. Real interfeysda elementlar ko'p va ular bir-biriga bog'liq.
«Bahor» menyusi sahifasini oching. Kategoriya ochilganda 6 ta taom kartochkasi paydo bo'ladi. Har biriga initial va animate yozish — 6 marta bir xil kod. Dizayner esa "birin-ketin chiqsin" dedi — har biriga delay: 0.05, 0.1, 0.15… qo'lda hisoblash kerak bo'ladi. Kartochka ustiga sichqoncha kelsa ko'tarilsin, bosilganda bir oz siqilsin. Telefonda esa savatdagi taomni chapga surib o'chirish mumkin bo'lsin.
Sardorning birinchi kodi uch ekranga cho'zildi. Bu darsdagi uch vosita uni bir ekranga sig'diradi:
- variants — holatlarga nom: bir joyda yoziladi, hamma joyda ishlatiladi;
- orkestratsiya — ota bolalarning vaqtini boshqaradi;
- gestures — foydalanuvchi harakatiga javob: hover, bosish, fokus, sudrash.
Hayotiy o'xshatish: orkestr. Har musiqachiga "uchinchi daqiqada, 5 soniyadan keyin chal" deb alohida aytmaysiz. Dirijyor bitta ishora qiladi — hamma o'z partiyasini o'z vaqtida boshlaydi. Variants — nota, ota — dirijyor.
2. Variants: holatlarga nom berish
2.1 Obyekt o'rniga nom
Motion I da animate={{ opacity: 1, y: 0 }} deb yozdik. Variants bilan bu qiymatlar nomlangan obyektga ko'chadi:
const card = {
yopiq: { opacity: 0, y: 8 },
ochiq: { opacity: 1, y: 0 },
};
<motion.li variants={card} initial="yopiq" animate="ochiq" />initial va animate endi obyekt emas — nom. Bitta komponent uchun farq kichik. Kuch ota-bola munosabatida ochiladi.
2.2 Nom bolalarga o'tadi
Ota animate="ochiq" desa, uning ichidagi har motion bola (o'zining animate i bo'lmasa) xuddi shu nomni o'z variants idan qidiradi. Props uzatish kerak emas — xuddi Context kabi, daraxt bo'ylab:
import { motion, stagger } from "motion/react";
const list = {
yopiq: {},
ochiq: { transition: { delayChildren: stagger(0.08) } },
};
const item = {
yopiq: { opacity: 0, y: 8 },
ochiq: { opacity: 1, y: 0 },
};
export default function App() {
return (
<motion.ul
variants={list}
initial="yopiq"
animate="ochiq"
onAnimationComplete={(name) => console.log("ro'yxat:", name)}
>
{["Osh", "Manti", "Lag'mon"].map((name) => (
<motion.li
key={name}
variants={item}
onAnimationComplete={() => console.log("chiqdi:", name)}
>
{name}
</motion.li>
))}
</motion.ul>
);
}Konsolda:
chiqdi: Osh
chiqdi: Manti
chiqdi: Lag'mon
ro'yxat: ochiqEkranda:
Osh
Manti
Lag'monNatija oynasida taomlar birin-ketin, 80 ms oraliq bilan paydo bo'ladi. Qatorma-qator:
<motion.li>lardainitialham,animateham yo'q. Ular faqatvariantsni biladi. "Qachon qaysi holat" ni ota aytdi.- Ota
variantsidaochiq: {}— otaning o'zi hech narsani animatsiya qilmaydi, faqattransitionbilan bolalarni boshqaradi. delayChildren: stagger(0.08)— har keyingi bola 0.08 s kechroq boshlaydi. Konsoldagi tartib — Osh, Manti, Lag'mon — shuning natijasi.onAnimationCompleteargumenti — variant nomi ("ochiq"). Ota bolalar tugagach tugadi.
2.3 staggerChildren — eski kodda
Ko'p maqola va eski loyihalarda boshqa yozuvni ko'rasiz:
// eski yozuv — hali ishlaydi, lekin eskirgan (deprecated)
ochiq: { transition: { staggerChildren: 0.08 } }Motion 14 ning tur fayllarida staggerChildren "delayChildren: stagger(interval) ni ishlating" izohi bilan eskirgan deb belgilangan. stagger() funksiyasi kuchliroq: stagger(0.08, { from: "last" }) — oxiridan boshlab, from: "center" — o'rtadan ikki tomonga.
Tekshirib ko'ring: Ichki
motion.ligaanimate={{ opacity: 1 }}qo'shsak, otaanimate="ochiq"desa ham u stagger'ga qo'shiladimi?
Javob
Yo'q. O'z animate props'i bor bola otadan nom kutmaydi — u mustaqil. Stagger va ota holati unga ta'sir qilmaydi. Variants'ning "nom bolaga o'tadi" qoidasi faqat animate siz (yoki animate o'rniga variant nomi bergan) bolalarda ishlaydi. Eng ko'p uchraydigan sabab: "stagger ishlamayapti".
3. Orkestratsiya
3.1 when: ota va bolalar navbati
Otaning o'zi ham animatsiya qilsa (masalan, fon so'nib chiqsa), kim birinchi?
const panel = {
yopiq: {
opacity: 0,
transition: { when: "afterChildren" },
},
ochiq: {
opacity: 1,
transition: {
when: "beforeChildren",
delayChildren: stagger(0.05),
},
},
};"beforeChildren"— avval ota (panel paydo bo'ladi), keyin bolalar (kartochkalar)."afterChildren"— avval bolalar (kartochkalar yo'qoladi), keyin ota (panel). Yopishda shu mantiqiy.whenyozilmasa — hammasi bir vaqtda boshlanadi.
3.2 Dinamik variants: custom
Variant obyekt emas, funksiya bo'lishi ham mumkin. Uning argumenti — elementning custom props'i:
import { motion } from "motion/react";
const item = {
yopiq: { opacity: 0, x: -10 },
ochiq: (i) => ({
opacity: 1,
x: 0,
transition: { delay: i * 0.1 },
}),
};
export default function App() {
return (
<ul>
{["Osh", "Manti"].map((name, i) => (
<motion.li
key={name}
custom={i}
variants={item}
initial="yopiq"
animate="ochiq"
onAnimationComplete={() => console.log("chiqdi:", name)}
>
{name}
</motion.li>
))}
</ul>
);
}Konsolda:
chiqdi: Osh
chiqdi: MantiBu yerda ota oddiy <ul> — orkestrlovchi yo'q. Har bola o'z kechikishini custom dan hisobladi. Qachon kerak? Kechikish yoki yo'nalish ma'lumotga bog'liq bo'lsa: "o'chirilgan qator chapga, qo'shilgani o'ngga", "ro'yxatdagi joyiga qarab".
3.3 Stagger qachon o'rinli
UI animatsiya tamoyillari darsidagi savolni eslang: 30 qator × 100 ms — oxirgi qator 3 soniyadan keyin. Stagger uchun amaliy qoidalar:
- Umumiy vaqt qisqa. Oraliq 30–80 ms. Element ko'p bo'lsa — faqat birinchi 6–10 tasini kechiktiring, qolganlari birga.
- Faqat birinchi tanishuvda. Kategoriya ochilganda — ha. Har filtr o'zgarganda butun ro'yxat qaytadan "oqsa" — yo'q, foydalanuvchi ma'lumotni kutadi.
- Ko'z yo'nalishida. Ro'yxat tepadan pastga o'qiladi — stagger ham tepadan.
flowchart TD
A{"Element birinchi marta chiqyaptimi?"} -->|yo'q| N["Stagger yo'q"]
A -->|ha| B{"Elementlar 10 tadan ko'pmi?"}
B -->|ha| C["Faqat birinchi 6–10 tasi"]
B -->|yo'q| D{"Ko'p takrorlanadigan ekranmi?"}
D -->|ha| N
D -->|yo'q| S["Stagger, 30–80 ms"]Sxemada nimaga qarang: stagger'ga olib boradigan yo'l bitta va u tor. Ko'pincha javob "yo'q" — vazifalar ham shunday qaror qildi («Vazifalar qadami» bo'limida).
4. Gestures: foydalanuvchi harakatiga javob
4.1 whileHover, whileTap, whileFocus
while… props'lari — "harakat davom etayotganda shu holatda bo'l". Harakat tugashi bilan element animate holatiga qaytadi:
<motion.button
whileHover={{ y: -2 }}
whileTap={{ scale: 0.97 }}
whileFocus={{ outline: "2px solid #1d4ed8" }}
>
Savatga
</motion.button>Ular ham variant nomini qabul qiladi va bolalarga o'tadi. Kartochka whileHover="faol" desa, ichidagi ikonka ham o'z faol variantiga o'tadi.
Uchtasining o'z qoidalari bor (manba: Motion 14 manba kodi):
whileHover— faqat sichqoncha va qalam. Barmoq bilan (touch) teginish hover hisoblanmaydi — telefonda "yopishib qolgan hover" bo'lmaydi. Demak muhim ma'lumotni hover'ga yashirmang: telefonda u hech qachon ko'rinmaydi.whileTap— bosish davomida. Klaviaturada Enter ham bosish hisoblanadi: element fokusda bo'lsa, Enter bosilgandawhileTapholati yoqiladi.whileFocus— faqat:focus-visiblebo'lganda, ya'ni klaviatura bilan kelganda. Sichqoncha bilan bosilganda fokus ramkasi chiqmaydi — brauzerning o'z qoidasi kabi.
4.2 onTap va onClick: amal qayerda?
Motion'da bosish hodisalari ham bor: onTap, onTapStart, onTapCancel, onHoverStart, onHoverEnd. Ular onClick ning o'rnini bosadimi? Sinab ko'ramiz: sichqoncha bilan bosamiz, keyin klaviaturada Space va Enter:
import { useState } from "react";
import { motion } from "motion/react";
export default function App() {
const [taps, setTaps] = useState(0);
const [clicks, setClicks] = useState(0);
return (
<div>
<motion.button
whileTap={{ scale: 0.95 }}
onTap={() => setTaps((t) => t + 1)}
onClick={() => setClicks((c) => c + 1)}
>
Sevimli
</motion.button>
<p>
onTap: {taps}, onClick: {clicks}
</p>
</div>
);
}Ekranda:
Sevimli
onTap: 2, onClick: 3Qadamma-qadam:
| Qadam | Fokus | Ekranda |
|---|---|---|
| sichqoncha bilan bosish | «Sevimli» da (bosilgan tugma fokus oladi) | onTap: 1, onClick: 1 |
| Space | «Sevimli» da | onTap: 1, onClick: 2 |
| Enter | «Sevimli» da | onTap: 2, onClick: 3 |
Uchta bosish — onClick uchalasini ham oldi. onTap esa Space'ni o'tkazib yubordi: Motion'ning klaviatura qo'llab-quvvatlashi faqat Enter'ni tekshiradi. Brauzerning o'zi esa tugma uchun Enter'ni ham, Space'ni ham click ga aylantiradi (React'da accessibility darsida).
Qoida: amal — onClick da (saqlash, o'chirish, savatga qo'shish). whileTap — faqat ko'rinish ("bosildi" hissi). onTap — faqat tugma bo'lmagan elementlar uchun va klaviatura muqobilini o'zingiz berganda.
4.3 Bosish javobi — qancha?
Bosilganda scale: 0.97 — sezilarli, lekin bezovta qilmaydi. 0.8 — tugma "qochib ketadi". Hover'da y: -2 yoki soya — yetadi. Bular ko'p takrorlanadigan harakatlar: UI animatsiya tamoyillari darsidagi "~100 ms" qatori. Motion scale uchun sukut bo'yicha spring ishlatadi — tez-tez bosilsa ham harakat sinmaydi.
whileHover ning CSS muqobili bor: :hover. Agar faqat rang yoki soya o'zgarsa — CSS yetadi va JavaScript'siz. Motion'ning whileHover i spring, bolalarga uzatish yoki sudrash bilan birga ishlash kerak bo'lganda foydali.
Tekshirib ko'ring: Taom kartochkasida narx faqat
whileHoverda paydo bo'ladi. Telefon foydalanuvchisi nimani ko'radi?
Javob
Narxni hech qachon ko'rmaydi. Motion barmoq teginishini hover deb hisoblamaydi, telefonda esa sichqoncha yo'q. Klaviatura foydalanuvchisi (Bobur) ham ko'rmaydi — hover fokus emas. Narx doim ko'rinsin, hover esa faqat bezak (ko'tarilish, soya) bo'lsin.
5. Drag: sudrash
5.1 drag va dragConstraints
drag props'i elementni sudraladigan qiladi. drag="x" — faqat gorizontal, drag="y" — faqat vertikal. Telefondagi savat: taomni chapga sursangiz, o'chiriladi.
Sudrashni kurs tekshiruvchisi bajara olmaydi (haqiqiy sichqoncha yo'q). Natija oynasida o'zingiz sinang — sichqoncha yoki barmoq bilan:
import { useState } from "react";
import { motion } from "motion/react";
export default function App() {
const [items, setItems] = useState(["Osh", "Manti", "Ko'k choy"]);
function remove(name) {
setItems((all) => all.filter((x) => x !== name));
}
return (
<ul style={{ padding: 0, maxWidth: 280 }}>
{items.map((name) => (
<motion.li
key={name}
drag="x"
dragConstraints={{ left: -120, right: 0 }}
dragElastic={0.1}
dragSnapToOrigin
onDragEnd={(e, info) => {
if (info.offset.x < -80) remove(name);
}}
style={{
listStyle: "none",
margin: 4,
padding: 12,
background: "#e8edfb",
borderRadius: 8,
touchAction: "pan-y",
}}
>
{name}{" "}
<button onClick={() => remove(name)}>O'chirish</button>
</motion.li>
))}
</ul>
);
}Har props:
dragConstraints={{ left: -120, right: 0 }}— chegara: 120 px chapga, o'ngga umuman yo'q. Chegarani ref bilan ham berish mumkin:dragConstraints={konteynerRef}— element konteynerdan chiqmaydi.dragElastic={0.1}— chegaradan tashqariga qanchalik "cho'ziladi" (0 — umuman yo'q, 1 — erkin).dragSnapToOrigin— qo'yib yuborilganda joyiga qaytadi (prujina bilan).onDragEnd(e, info)—info.offset.x— boshlanishdan beri qancha surildi,info.velocity.x— qo'yib yuborilgandagi tezlik. 80 px dan ko'p chapga — o'chirish.touchAction: "pan-y"— telefonda tik aylantirish brauzerda qoladi, gorizontal harakatni esa Motion oladi. Busiz qatorni surayotganda sahifa ham siljiydi.
Yana: dragMomentum (qo'yib yuborilgandan keyin inersiya bilan davom etish, sukut — bor), whileDrag (sudralayotgan paytdagi holat, masalan { scale: 1.03 }), onDragStart, onDrag.
5.2 A11y: sudrash — yagona yo'l bo'lmasin
Bobur sichqoncha ishlatmaydi. Malika qatorni "ko'rmaydi". Sudrash ular uchun yo'q. Shuning uchun misolda har qatorda «O'chirish» tugmasi bor — sudrash tezlashtiruvchi yo'l, asosiy yo'l emas. WCAG 2.2 da bu alohida mezon: 2.5.7 "Dragging Movements" — sudrash bilan qilinadigan ishni bitta bosish bilan ham qilish mumkin bo'lsin.
5.3 Motion drag yoki dnd-kit?
Drag and drop: dnd-kit darsida ro'yxatni tartiblashni qurdik. Ikkalasi ham "sudrash", lekin vazifasi boshqa:
Motion drag |
dnd-kit | |
|---|---|---|
| Nima uchun | erkin surish: swipe, slayder, "tashla" | ro'yxat tartibi, ustunlar orasida ko'chirish |
| Klaviatura | yo'q — o'zingiz qo'shasiz | KeyboardSensor tayyor |
| Ekran o'quvchi | yo'q | e'lonlar tayyor |
| Fizika | prujina, inersiya | oddiy |
Qoida: elementlarni tartiblash — dnd-kit. Elementni surib qo'yish yoki "uloqtirish" — Motion drag.
Tekshirib ko'ring: Swipe misolidan
touchAction: "pan-y"olib tashlansa, telefonda nima bo'ladi?
Javob
Barmoq qatorga tekkanda brauzer va Motion "kim boshqaradi?" deb tortishadi. Qatorni chapga sursangiz, brauzer buni sahifani aylantirish deb ham qabul qilishi mumkin. Natija: qator siljimaydi yoki sahifa bilan birga titraydi. touch-action: pan-y brauzerga aytadi: "tik harakat — senga, gorizontal — sahifadagi kodga".
6. Harakatni kamaytirish: variants va gestures
Motion I dagi <MotionConfig reducedMotion="user"> bu darsdagi hamma narsaga ham tegishli. Tizimda "harakatni kamaytirish" yoqilgan bo'lsa, variantlardagi y, x, scale darhol maqsadda bo'ladi, opacity esa qoladi. Stagger'li ro'yxat endi uchmaydi — faqat birin-ketin so'nib chiqadi. Ma'no (yangi narsa paydo bo'ldi) saqlanadi.
Ikki narsani o'zingiz hal qilasiz:
- Uzun stagger. Kamaytirilgan rejimda ham kutish qoladi.
useReducedMotion()(Motion IV da batafsil)truebo'lsa,delayChildrenni olib tashlang — hammasi birga. - Sudrash.
drag— foydalanuvchining o'z harakati, u o'chmaydi. Lekin qo'yib yuborilgandan keyingi inersiya va "sakrab qaytish" bezak: kamaytirilgan rejimdadragMomentum={false}qo'yish mumkin.
7. Narx va LazyMotion
Gestures va variants ham Motion'ning "yadro"sida. Motion IV darsida yadroni LazyMotion bilan bo'lakka bo'lamiz. Hozir bitta faktni biling: yadroning ikki varianti bor. Birinchisi — domAnimation: animatsiyalar, variants, hover, tap va focus. Ikkinchisi — domMax: shularning hammasi, ustiga drag va layout. Manba — framer-motion 14.0.0 kodi. vazifalar domAnimation bilan cheklangan: unda Motion drag ham, layout animatsiya ham yo'q, shuning uchun yadro kichik (14.5 KB).
8. Vazifalar qadami: qaysi qator "yangi"? (holat 25 davomi)
8.1 Muammo
Oldingi darsda useQatorHarakati(yangi) yozdik: yangi bo'lsa — kirish animatsiyasi, bo'lmasa initial: false. Lekin yangi ni kim hisoblaydi?
Birinchi xayol — stagger: sahifa ochilganda hamma qatorlar birin-ketin chiqsin. Kanon bunga yo'q dedi:
vazifalar— ish quroli, kuniga ko'p marta ochiladi. Ochilishda kutish — har safar.- 200 tagacha qator to'liq ro'yxatda — stagger bilan oxirgisi soniyalab kechikadi.
- Mavjud qatorlar "yangi" emas — ularni jonlantirish hech narsani tushuntirmaydi (to'rt vazifadan hech biri).
Qoida: faqat ro'yxat ochilgandan keyin qo'shilgan qator jonlanadi.
8.2 Yechim: birinchi chizishdagi id'lar
VazifaRoyxati.tsx (kanon, holat 25):
-import { memo, useMemo } from "react";
+import { memo, useMemo, useState } from "react";
...
+ // 20/#25: ochilishdagi qatorlar animatsiyasiz — faqat keyin
+ // qo'shilganlari "yangi" (kirish animatsiyasi). useState
+ // initializer — bir marta, birinchi chizishda (effekt va
+ // ref'siz)
+ const [boshlangich] = useState(
+ () => new Set(vazifalar.map((v) => v.id)),
+ );
return vazifalar.map((vazifa) => (
<VazifaQatori
key={vazifa.id}
vazifa={vazifa}
sorov={sorov}
tahrirda={vazifa.id === tahrirId}
+ yangi={!boshlangich.has(vazifa.id)}
/>
));Bu bir necha qatorda React'ning ikki g'oyasi ishlaydi:
useStateboshlang'ich funksiyasi (useState) —() => new Set(...)faqat birinchi chizishda chaqiriladi. Keyingi render'larda React saqlanganSetni qaytaradi. Setter (setBoshlangich) yo'q — qiymat hech qachon o'zgarmaydi.- Hisoblanadigan qiymat —
yangialohida state emas, har renderda id'ni solishtirib topiladi.
Nega effekt yoki ref emas? Effekt birinchi chizishdan keyin ishlaydi — bitta render davomida hamma qator "yangi" bo'lib qolardi va jonlanardi. Ref'ga render paytida yozish esa React qoidasiga zid (useRef I darsida).
yangi props'i zanjir bo'ylab Qator gacha tushadi. VazifaQatori.tsx (kanon):
+ // 20/#25: ochilgandan keyin qo'shilgan — kirish animatsiyasi
+ yangi?: boolean;
...
- return <OddiyQator vazifa={vazifa} sorov={sorov} joy={qatorJoyi} />;
+ return (
+ <OddiyQator
+ vazifa={vazifa}
+ sorov={sorov}
+ joy={qatorJoyi}
+ yangi={yangi}
+ />
+ );OddiyQator.tsx uni Qator ga beradi (yangi={yangi}). Tutqichli qatorga ham (tutqichliQator({ vazifa, yangi, children })).
8.3 Sudrash va Motion bitta qatorda
«Tartiblash» rejimidagi qator (Drag and drop: dnd-kit) ham m.li bo'ldi. Bu yerda ikki kutubxona bitta elementning transform ini boshqaradi. SudraladiganQator.tsx (kanon):
+ const harakat = useQatorHarakati(yangi);
+ // 20/#25: paydo bo'lish — Qator.tsx dagidek. Motion faqat
+ // opacity va x/y ni animatsiya qiladi; dnd-kit'ning transform'i
+ // (style) sudrash paytida — ikkalasi bir vaqtda ishlamaydi
return (
- <li
+ <m.li
+ {...harakat}
ref={setNodeRef}Izohdagi argumentga qarang: ikkalasi transform ga yozadi, lekin bir vaqtda emas. Motion — qator paydo bo'lgan 150 ms da. dnd-kit — faqat sudrash paytida. Agar Motion'ning drag i yoki whileHover={{ y: -2 }} qo'shilsa, ular dnd-kit bilan to'qnashardi. Kanonda qatorlarda gesture yo'qligining ikkinchi sababi shu (birinchisi — hover'ni CSS :hover yetarli qiladi).
8.4 Kanon tekshiruvi
Holat 25 ning 20-qism ssenariysidan (Chrome 155):
✅ ochilishda mavjud qatorlar animatsiyasiz: 0
✅ yangi qator: kirish animatsiyasi — faqat opacity va transform: true
✅ qo'shish/o'chirish: CLS 0 (hadRecentInput hisobga olinmaydi): 0Birinchi qator: sahifa ochilganda <li> larga topshirilgan animatsiyalar soni — 0. Uchinchisi: yangi qator paydo bo'lganda sahifa "sakramadi" (CLS — Cumulative Layout Shift, kutilmagan siljish, Core Web Vitals). Animatsiya faqat opacity va transform da bo'lgani uchun boshqa qatorlar joyidan qimirlamadi. Commit — Motion darslarining oxirida.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| stagger ishlamaydi | bolada o'z animate i bor |
bolada faqat variants |
| bola animatsiya qilmaydi | variant nomlari mos emas (ochiq / open) |
bir xil nomlar |
staggerChildren haqida eskirganlik izohi |
eski API | delayChildren: stagger(…) |
| Space bilan "Sevimli" ishlamaydi | amal onTap da |
amal onClick da |
| telefonda hover ma'lumoti ko'rinmaydi | whileHover touch'da yo'q |
muhim narsa doim ko'rinsin |
| sudralgan element ekrandan uchib ketadi | dragConstraints yo'q |
chegara yoki ref |
| qatorni surganda sahifa ham siljiydi | touch-action yo'q |
touchAction: "pan-y" |
| sudrash bilan o'chiriladi, boshqa yo'l yo'q | a11y | tugma muqobili (WCAG 2.5.7) |
| ro'yxat ochilganda hamma qator "uchadi" | hamma qatorda initial |
faqat yangilariga |
10. Mashqlar
Mashqlar kurs/mashqlar/20/24-motion-ii/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): qaysi props?
- Ota bolalarni 0.05 s oraliq bilan chiqarsin →
delayChildren: - Yopishda avval bolalar, keyin ota →
when: - Faqat gorizontal sudrash →
drag= - Klaviatura bilan kelganda fokus holati →
Yechim
delayChildren: stagger(0.05)—staggerChildrenemas (eskirgan).when: "afterChildren"— yopilishda bolalar oldin ketadi.drag="x".whileFocus— faqat:focus-visibleda ishlaydi.
2-mashq (o'rta): menyu kartochkalari
«Bahor» kategoriya sahifasi: 4 ta taom (Osh, Lag'mon, Manti, Ko'k choy).
- Ota
motion.ulva bolalarmotion.li— variants bilan (yopiq/ochiq). - Bolalar 0.06 s oraliq bilan, oxiridan boshlab chiqsin (
from: "last"). - Otaning o'zi avval so'nib chiqsin (
opacity0 → 1), keyin bolalar. - Har bola chiqqanda konsolga nomi yozilsin.
Ishora: «when: ota va bolalar navbati» bo'limi; stagger(0.06, { from: "last" }).
Yechim
import { motion, stagger } from "motion/react";
const menu = ["Osh", "Lag'mon", "Manti", "Ko'k choy"];
const list = {
yopiq: { opacity: 0 },
ochiq: {
opacity: 1,
transition: {
when: "beforeChildren",
delayChildren: stagger(0.06, { from: "last" }),
},
},
};
const card = {
yopiq: { opacity: 0, y: 8 },
ochiq: { opacity: 1, y: 0 },
};
export default function App() {
return (
<motion.ul variants={list} initial="yopiq" animate="ochiq">
{menu.map((name) => (
<motion.li
key={name}
variants={card}
onAnimationComplete={() => console.log("chiqdi:", name)}
>
{name}
</motion.li>
))}
</motion.ul>
);
}Konsolda:
chiqdi: Ko'k choy
chiqdi: Manti
chiqdi: Lag'mon
chiqdi: OshEkranda:
Osh
Lag'mon
Manti
Ko'k choyEkranda tartib o'zgarmadi — faqat chiqish tartibi teskari. when: "beforeChildren" — avval ul ning o'zi so'nib chiqdi, keyin bolalar. from: "last" qachon kerak? Masalan pastdan chiqadigan menyuda: ko'z pastdan yuqoriga qaraydi.
3-mashq (qiyin): "Sevimli" tugmasi
Taom kartochkasidagi yurak tugmasi:
motion.button,aria-pressedbilan; amal —onClick(Enter va Space ikkalasi ishlasin).whileTap={{ scale: 0.9 }}— bosish hissi.- Holat o'zgarganda ichidagi belgi (
♥/♡) variants bilan "lip" etsin:faol: { scale: [1, 1.3, 1] },nofaol: { scale: 1 }. Variant nomi otadan bolaga o'tsin. - Ostida: "Sevimlilarda: ha/yo'q".
Tekshirish: sichqoncha bilan bir marta, keyin Space bilan bir marta — natija "yo'q" (ikki marta almashdi).
Ishora: otada animate={fav ? "faol" : "nofaol"}, bolada faqat variants. «Nom bolalarga o'tadi» bo'limi.
Yechim
import { useState } from "react";
import { motion } from "motion/react";
const heart = {
faol: { scale: [1, 1.3, 1], transition: { duration: 0.3 } },
nofaol: { scale: 1 },
};
export default function App() {
const [fav, setFav] = useState(false);
return (
<div>
<motion.button
aria-pressed={fav}
aria-label="Sevimli"
initial={false}
animate={fav ? "faol" : "nofaol"}
whileTap={{ scale: 0.9 }}
onClick={() => setFav(!fav)}
onAnimationComplete={(name) => console.log("variant:", name)}
>
<motion.span
variants={heart}
style={{ display: "inline-block" }}
aria-hidden="true"
>
{fav ? "♥" : "♡"}
</motion.span>
</motion.button>
<p>Sevimlilarda: {fav ? "ha" : "yo'q"}</p>
</div>
);
}Ekranda:
♡
Sevimlilarda: yo'qSichqoncha — onClick, va "lip". Space — yana onClick, . Amal onClick da bo'lgani uchun Space ham ishladi — onTap da bo'lsa, Space'da hech narsa bo'lmasdi («onTap va onClick» bo'limi). aria-label="Sevimli" va aria-pressed — Malika "Sevimli, almashtirgich tugma, bosilgan" ni eshitadi. Belgining o'zi aria-hidden: yurak belgisi ekran o'quvchida g'alati o'qilishi mumkin. Belgi otaning variant nomini oldi — unga hech qanday props uzatmadik.
4-mashq: Vazifalar qadami
feat/motion branch'ida davom eting: VazifaRoyxati.tsx — boshlangich (useState boshlang'ich funksiyasi bilan) va yangi props'i; VazifaQatori.tsx, OddiyQator.tsx, Qator.tsx, SudraladiganQator.tsx — yangi zanjiri va useQatorHarakati(yangi). Commit qilmang.
Yechim
Tekshiruv ro'yxati:
- Sahifani oching — mavjud qatorlar jim. Yangi vazifa qo'shing — faqat u jonlanadi.
- Filtrni almashtiring: filtrdan qaytgan eski qatorlar "yangi" emas (ularning id'lari
boshlangichda) — jonlanmaydi. - «Tartiblash» rejimiga kiring va qator qo'shing — sudrash paytida Motion aralashmaydi.
- DevTools Performance → yangi qator qo'shishda "Layout Shift" yozuvi yo'q.
npm run lint— React Compiler lint qoidalari (render paytida ref'ga yozish yo'q) toza.
11. Real ishda
- Landing va marketing sahifalari — variants + stagger: sarlavha, matn, tugmalar birin-ketin. Bu yerda stagger o'rinli: sahifa bir marta ko'riladi.
- Mobil-veb ilovalar — swipe (o'chirish, arxivlash), pastdan chiqadigan panel (
drag="y"), karusel. Sensor ekran fizikasi — Motion'ning kuchli tomoni. - Dizayn tizimlari — tugmalarning
whileTapjavobi umumiy komponentda bir marta yoziladi (motion.create(Tugma)), sahifalarda takrorlanmaydi. - Intervyu savollari: "variants qanday ishlaydi va nima uchun kerak?", "stagger'ni qanday qilasiz va qachon qilmaysiz?", "
whileHovertelefonda nima qiladi?", "sudrash bilan qilinadigan ishni a11y uchun qanday ochiq qilasiz?".
Xulosa
- Variants — holatlarga nom; ota
animate="nom"desa,animatesiz bolalar o'zvariantsidan shu nomni oladi. - Orkestratsiya:
delayChildren: stagger(t)(staggerChildren— eskirgan),when: "beforeChildren"/"afterChildren", dinamik variant —custom. - Stagger — faqat birinchi tanishuvda, qisqa oraliq bilan, elementlar ko'p bo'lsa cheklab.
whileHover— touch'da yo'q;whileTap— Enter'da ham, Space'da emas;whileFocus— faqat:focus-visible. Amal —onClickda.drag,dragConstraints,dragElastic,onDragEnd(info.offset),touch-action; sudrashning tugmali muqobili majburiy. Tartiblash — dnd-kit.- Kanon: stagger yo'q — faqat ochilgandan keyin qo'shilgan qator jonlanadi (
useStateboshlang'ich funksiyasi).
Keyingi dars: Motion III: chiqish va layout animatsiyalari — AnimatePresence, exit, layout va layoutId; nega kanon ro'yxatda AnimatePresence ni ishlatmadi (o'lchov bilan).
Manbalar
- Motion for React: Animation (variants, orchestration), Gestures, Drag — motion.dev/docs/react (motion 14.0.0)
- Motion:
stagger— motion.dev/docs/stagger - framer-motion / motion-dom 14.0.0 manba kodi: hover (touch filtri), klaviatura bilan bosish (Enter),
:focus-visible,domAnimation/domMax - WCAG 2.2: 2.5.7 Dragging Movements — w3.org/WAI/WCAG22/Understanding/dragging-movements
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!