الأدلة · 05
الدخول والمغادرة والتخطيط احتفظ بالأبناء المغادرين حتى تنتهي حركتهم، وحرّك تغيّرات الموضع والحجم، حتى بين المكوّنات.
Animated.Keep يحتفظ Animated.Keep بالأبناء المباشرين ذوي key خلال وضعية depart الخاصة بهم. ويجعل seed={false} الأبناء الموجودين في العرض الأول يبدؤون من وضعيتهم، ويؤخّر wait الأبناء الداخلين حتى تنتهي المغادرات، ويُطلَق onDeparted بعد آخرها.
JavaScript معطّل. الشيفرة أدناه هي المثال الكامل.
App.jsx import { useState } from 'react' ;
import { Animated } from '@kkwaenggwari/animated' ;
import './styles.css' ;
const departures = {
up : { opacity : 0 , transform : [{ translateY : -24 }] },
side : { opacity : 0 , transform : [{ translateX : 120 }] },
shrink : { opacity : 0 , transform : [{ scale : 0.6 }] },
};
let next = 4 ;
export default function App ({ depart = 'side' , wait = false } ) {
const [items, setItems] = useState ([1 , 2 , 3 ]);
const add = ( ) => setItems ((list ) => [...list, next++]);
const remove = (id ) => setItems ((list ) => list.filter ((item ) => item !== id));
return (
<Animated.Scene tempo ={{ spring: { stiffness: 320 , damping: 30 } }}>
<div className ="stage" >
<button type ="button" className ="add" onClick ={add} >
Add message
</button >
<Animated.Keep wait ={wait} >
{items.map((id) => (
<Animated.div
key ={id}
move
className ="message"
seed ={{ opacity: 0 , transform: [{ translateY: 16 }] }}
pose ={{ opacity: 1 , transform: [{ translateY: 0 }] }}
depart ={departures[depart]}
tempo ={{ spring: { stiffness: 320 , damping: 26 } }}
>
<span > Message #{id}</span >
<button type ="button" onClick ={() => remove(id)}>
Dismiss
</button >
</Animated.div >
))}
</Animated.Keep >
</div >
</Animated.Scene >
);
}styles.css .stage {
display : grid;
align-content : start;
gap : 10px ;
min-height : 320px ;
max-width : 420px ;
padding : 28px ;
}
button {
font : inherit;
cursor : pointer;
}
.add {
justify-self : start;
min-height : 44px ;
margin-bottom : 8px ;
padding : 0 20px ;
border : 0 ;
border-radius : 999px ;
background : #2854ff ;
color : #fff ;
font-weight : 650 ;
}
.message {
display : flex;
align-items : center;
justify-content : space-between;
gap : 12px ;
padding : 10px 10px 10px 18px ;
border : 1px solid #e4e6ef ;
border-radius : 14px ;
background : #fff ;
}
.message button {
min-height : 36px ;
padding : 0 14px ;
border : 1px solid #e4e6ef ;
border-radius : 999px ;
background : #f5f6fb ;
color : #16181d ;
}Animated.Scene وmove يقيس Animated.Scene التخطيط المعتمد. العنصر الذي يحمل move يحرّك تغيّرات موضعه وحجمه؛ ونص مثل move="pill" يصل العناصر ذات الهوية نفسها داخل Scene، حتى حين يُزال أحدها ويُركَّب آخر.
يمتلك Scene إزاحة التخطيط وتحجيمه لتلك العناصر، فضع تحويلاتك الخاصة على عنصر متداخل.
JavaScript معطّل. الشيفرة أدناه هي المثال الكامل.
App.jsx import { useState } from 'react' ;
import { Animated } from '@kkwaenggwari/animated' ;
import './styles.css' ;
const tabs = ['All' , 'Motion' , 'Layout' , 'Input' ];
const colors = ['#2854ff' , '#8fb0ff' , '#5c7cff' , '#16181d' , '#4a6bff' , '#7d9bff' ];
export default function App ({ columns = 3 , stiffness = 300 , damping = 28 } ) {
const [tab, setTab] = useState ('All' );
const [tiles, setTiles] = useState (colors);
const shuffle = ( ) => setTiles ((list ) => [...list.slice (1 ), list[0 ]]);
return (
<Animated.Scene tempo ={{ spring: { stiffness , damping } }}>
<div className ="stage" >
<nav className ="tabs" >
{tabs.map((name) => (
<button key ={name} type ="button" onClick ={() => setTab(name)}>
{name === tab && <Animated.span className ="pill" move ="pill" /> }
<span className ="label" > {name}</span >
</button >
))}
</nav >
<div className ="tiles" style ={{ gridTemplateColumns: `repeat (${columns }, 1fr )` }}>
{tiles.map((color) => (
<Animated.div key ={color} move className ="tile" style ={{ background: color }} />
))}
</div >
<button type ="button" className ="shuffle" onClick ={shuffle} >
Rotate tiles
</button >
</div >
</Animated.Scene >
);
}styles.css .stage {
display : grid;
gap : 18px ;
padding : 28px ;
}
button {
font : inherit;
cursor : pointer;
}
.tabs {
display : flex;
flex-wrap : wrap;
gap : 4px ;
justify-self : start;
padding : 4px ;
border-radius : 999px ;
background : #f5f6fb ;
}
.tabs button {
position : relative;
min-height : 40px ;
padding : 0 18px ;
border : 0 ;
background : none;
color : #16181d ;
}
.pill {
position : absolute;
inset : 0 ;
border-radius : 999px ;
background : #2854ff ;
}
.label {
position : relative;
}
.tabs button :has (.pill ) .label {
color : #fff ;
}
.tiles {
display : grid;
gap : 10px ;
max-width : 420px ;
}
.tile {
height : 64px ;
border-radius : 14px ;
}
.shuffle {
justify-self : start;
min-height : 44px ;
padding : 0 20px ;
border : 1px solid #e4e6ef ;
border-radius : 999px ;
background : #fff ;
}الحضور عبر خطّاف يعيد useAnimatedPresence(visible, { enter, exit }) القيمة { mounted, animatedValue } حين تفضّل التحكم في التركيب بنفسك.
Toast.jsx نسخ الشيفرة const { mounted, animatedValue } = useAnimatedPresence (open, {
enter : (value ) => spring (value, { toValue : 1 }),
exit : (value ) => timing (value, { toValue : 0 , duration : 180 }),
});
return mounted ? <div style ={{ opacity: animatedValue }}> Saved</div > : null ;خارج React يمنح createLayoutScene تنسيق الالتقاط والاعتماد نفسه لأي مُصيِّر أو تغيير يدوي في DOM.
outside-react.js نسخ الشيفرة const scene = createLayoutScene ({ tempo : { spring : { stiffness : 320 , damping : 30 } } });
const card = scene.attach (element, { key : 'card' });
scene.capture ();
list.prepend (element);
scene.commit ();