מדריכים · 05
כניסה, יציאה ופריסה שמרו ילדים יוצאים עד שהתנועה שלהם מסתיימת, והנפישו שינויי מיקום וגודל, גם בין רכיבים.
Animated.Keep Animated.Keep שומר ילדים ישירים עם key לאורך ה־pose depart שלהם. seed={false} גורם לילדים שקיימים ברינדור הראשון להתחיל ב־pose שלהם, 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 ;
}נוכחות עם hook 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 נותן את אותו תיאום של לכידה ו־commit לכל renderer או שינוי ידני ב־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 ();