الأدلة · 03
مكوّنات Animated اعرض عناصر عادية تتحرك نحو وجهة، وتستجيب للإيماءات، وتغادر بأناقة.
Animated.<tag> تعرض Animated.div وAnimated.button وAnimated.svg وكل وسم HTML أو SVG آخر العنصر العادي من دون غلاف. يقبل style الخاص بها القيم، ويمكن أن تكون التحويلات مصفوفات كما في React Native.
الحركة المحدودة لـ transform وopacity على هذه العناصر، بما فيها النوابض، مؤهلة للتشغيل في المتصفح.
JavaScript معطّل. الشيفرة أدناه هي المثال الكامل.
App.jsx import { useState } from 'react' ;
import { Animated } from '@kkwaenggwari/animated' ;
import './styles.css' ;
const plans = ['Research' , 'Build' , 'Launch' ];
export default function App ({ stiffness = 260 , damping = 22 } ) {
const [selected, setSelected] = useState ('Build' );
return (
<div className ="stage" >
{plans.map((plan, index) => (
<Animated.button
key ={plan}
type ="button"
className ={plan === selected ? 'plan active ' : 'plan '}
onClick ={() => setSelected(plan)}
seed={{ opacity: 0, transform: [{ translateY: 24 }] }}
pose={{
opacity: 1,
transform: [
{ translateY: plan === selected ? -12 : 0 },
{ scale: plan === selected ? 1.06 : 1 },
],
}}
tempo={{ spring: { stiffness, damping } }}
>
<strong > {plan}</strong >
<span > Step {index + 1}</span >
</Animated.button >
))}
</div >
);
}styles.css .stage {
display : flex;
flex-wrap : wrap;
align-items : flex-end;
gap : 16px ;
min-height : 220px ;
padding : 40px 28px 28px ;
}
.plan {
display : grid;
gap : 6px ;
width : 150px ;
padding : 20px ;
border : 1px solid #e4e6ef ;
border-radius : 18px ;
background : #fff ;
color : #16181d ;
font : inherit;
text-align : start;
cursor : pointer;
}
.plan span {
color : #5b6070 ;
font-size : 14px ;
}
.plan .active {
border-color : #2854ff ;
background : #2854ff ;
color : #fff ;
}
.plan .active span {
color : #d6e0ff ;
}seed وpose وposes وtempo seed هو الإطار الأول، وpose الوجهة الحالية (كائن أو اسم مدخل في poses)، وtempo توقيت أو نابض. تغيير tempo وحده لا يعيد تشغيل وجهة لم تتغيّر.
يُطلَق onRest حين يصل العنصر.
Card.jsx نسخ الشيفرة <Animated .article
seed={{ opacity : 0 , transform : [{ translateY : 16 }] }}
pose={selected ? 'raised' : 'rest' }
poses={{
rest : { opacity : 1 , transform : [{ translateY : 0 }, { scale : 1 }] },
raised : { opacity : 1 , transform : [{ translateY : -8 }, { scale : 1.04 }] },
}}
tempo={{ spring : { stiffness : 300 , damping : 26 } }}
onRest={() => console .log ('arrived' )}
/>;الاستجابة للإيماءات يربط respond الإيماءات بوجهات مؤقتة: hover وfocus وfocusWithin وpress وlongPress وdrag وinView وغيرها. حين تنتهي الاستجابة، يعود العنصر إلى وضعيته من حيث هو.
يُبلغ onResponse عن بداية كل استجابة ونهايتها، ويراقب respond={{ press: {} }} إيماءة من دون تغيير أي شيء بصريًا.
JavaScript معطّل. الشيفرة أدناه هي المثال الكامل.
App.jsx import { Animated } from '@kkwaenggwari/animated' ;
import './styles.css' ;
const actions = ['Share' , 'Save' , 'Export' ];
export default function App ({ lift = 8 , scale = 1.06 } ) {
return (
<div className ="stage" >
{actions.map((action) => (
<Animated.button
key ={action}
type ="button"
className ="action"
pose ={{ transform: [{ translateY: 0 }, { scale: 1 }] }}
respond ={{
hover: { transform: [{ translateY: -lift }, { scale }] },
focus: { transform: [{ translateY: -lift }, { scale }] },
press: { transform: [{ translateY: 0 }, { scale: 0.94 }] },
}}
tempo ={{ spring: { stiffness: 420 , damping: 24 } }}
>
{action}
</Animated.button >
))}
</div >
);
}styles.css .stage {
display : flex;
flex-wrap : wrap;
align-items : center;
justify-content : center;
gap : 16px ;
min-height : 200px ;
padding : 32px ;
}
.action {
min-width : 120px ;
min-height : 56px ;
padding : 0 24px ;
border : 0 ;
border-radius : 16px ;
background : #16181d ;
color : #fff ;
font : inherit;
font-weight : 650 ;
cursor : pointer;
}
.action :focus-visible {
outline : 3px solid #5c7cff ;
outline-offset : 3px ;
}مشاركة الإعدادات الافتراضية عبر Animated.Scope يمرّر Animated.Scope اسم وضعية وإيقاعًا وإعدادات حركة افتراضية إلى عناصر Animated داخله.
Menu.jsx نسخ الشيفرة <Animated .Scope tempo={{ spring : { stiffness : 380 , damping : 30 } }}>
<Animated.li pose ={{ opacity: 1 }} />
<Animated.li pose ={{ opacity: 1 }} />
</Animated .Scope >;مكوّناتك الخاصة يجعل Animated.wrap(Component) — وهو createAnimatedComponent في React Native — مكوّنًا يمرّر ref ويُحَلّ إلى عنصر DOM واحد يقبل props نفسها.
Link.jsx نسخ الشيفرة import { forwardRef } from 'react' ;
import { Animated } from '@kkwaenggwari/animated' ;
const Link = forwardRef ((props, ref ) => <a ref ={ref} {...props } /> );
const AnimatedLink = Animated .wrap (Link );
<AnimatedLink href ="/pricing" respond ={{ hover: { transform: [{ translateX: 4 }] } }} /> ;