คู่มือ · 02
Timing สปริง และการประกอบ เลือก driver ผสมหลายตัว และรู้แน่ชัดว่าเกิดอะไรขึ้นเมื่อตัวหนึ่งหยุดหรือถูกแทนที่
timing, spring และ decay timing เดินตามเส้นโค้งตลอด duration ที่กำหนด spring ทำตามฟิสิกส์และไม่มีระยะเวลาตายตัว decay เริ่มด้วยความเร็วแล้วช้าลง
driver เดียวกันรับตัวเลข ValueXY หรือเรคคอร์ด { x, y, opacity } จึงเคลื่อนไปพร้อมกันได้
JavaScript ถูกปิดอยู่ โค้ดด้านล่างคือตัวอย่างฉบับสมบูรณ์
App.jsx import { Easing , parallel, spring, timing, useAnimatedValue } from '@kkwaenggwari/animated' ;
import './styles.css' ;
export default function App ({ stiffness = 180 , damping = 12 , mass = 1 , duration = 500 } ) {
const eased = useAnimatedValue (0 );
const standard = useAnimatedValue (0 );
const custom = useAnimatedValue (0 );
const go = ( ) => {
const toValue = custom.getValue () < 0.5 ? 1 : 0 ;
parallel ([
timing (eased, { toValue, duration, easing : Easing .inOut (Easing .cubic ) }),
spring (standard, { toValue }),
spring (custom, { toValue, stiffness, damping, mass }),
]).start ();
};
const lanes = [
['timing' , eased],
['spring()' , standard],
['spring(props)' , custom],
];
return (
<div className ="stage" >
<button type ="button" onClick ={go} >
Go
</button >
{lanes.map(([label, value]) => (
<div key ={label} className ="lane" >
<code > {label}</code >
<div className ="rail" >
<span
style ={{
left: `calc (${value } * (100 % - 40px ))`,
}}
/>
</div >
</div >
))}
</div >
);
}styles.css .stage {
display : grid;
gap : 14px ;
padding : 28px ;
}
button {
justify-self : start;
min-height : 44px ;
padding : 0 24px ;
border : 0 ;
border-radius : 999px ;
background : #2854ff ;
color : #fff ;
font : inherit;
font-weight : 650 ;
cursor : pointer;
}
.lane {
display : grid;
grid-template-columns : 120px 1 fr;
align-items : center;
gap : 12px ;
}
.lane code {
color : #1a33c7 ;
font-size : 13px ;
}
.rail {
position : relative;
height : 40px ;
border-radius : 999px ;
background : #f5f6fb ;
box-shadow : inset -2px 0 0 #e4e6ef ;
}
.rail span {
position : absolute;
top : 0 ;
width : 40px ;
height : 40px ;
border-radius : 50% ;
background : #8fb0ff ;
}
.lane :first-of-type .rail span {
background : #5c7cff ;
}
.lane :last-of-type .rail span {
background : #2854ff ;
}พารามิเตอร์ของสปริง ใช้ชุดพารามิเตอร์เดียว ถ้าไม่ระบุจะได้สปริงเริ่มต้นของ React Native (tension 40, friction 7) velocity ของสปริงโดยค่าเริ่มต้นคือความเร็วของการเคลื่อนไหวที่มันขัดจังหวะ
springs.js คัดลอกโค้ด spring (value, { toValue : 1 });
spring (value, { toValue : 1 , stiffness : 260 , damping : 20 });
spring (value, { toValue : 1 , bounciness : 8 , speed : 12 });
spring (value, { toValue : 1 , tension : 40 , friction : 7 }); การประกอบ sequence รันทีละตัว parallel รันพร้อมกัน stagger เริ่มแต่ละตัวหลังความหน่วง delay รอ และ loop วนซ้ำ ผลลัพธ์ก็เป็นตัวควบคุมอีกตัว จึงซ้อนการประกอบกันได้
JavaScript ถูกปิดอยู่ โค้ดด้านล่างคือตัวอย่างฉบับสมบูรณ์
App.jsx import {
delay,
loop,
parallel,
sequence,
spring,
stagger,
timing,
useAnimated,
} from '@kkwaenggwari/animated' ;
import './styles.css' ;
const keys = ['a' , 'b' , 'c' , 'd' , 'e' ];
export default function App ({ mode = 'stagger' , gap = 90 } ) {
const bars = useAnimated (
{ a : 0 , b : 0 , c : 0 , d : 0 , e : 0 },
(value ) => {
const group = (list ) =>
mode === 'sequence'
? sequence (list)
: mode === 'parallel'
? parallel (list)
: stagger (gap, list);
const rise = keys.map ((key ) =>
spring (value[key], { toValue : 1 , stiffness : 220 , damping : 16 }),
);
const fall = keys.map ((key ) => timing (value[key], { toValue : 0 , duration : 260 }));
loop (sequence ([group (rise), delay (500 ), group (fall), delay (300 )]));
},
[mode, gap],
);
return (
<div className ="stage" >
{keys.map((key) => (
<span key ={key} style ={{ transform: `scaleY (calc (0.15 + ${bars [key ]} * 0.85 ))` }} />
))}
</div >
);
}styles.css .stage {
display : flex;
align-items : flex-end;
justify-content : center;
gap : 14px ;
height : 260px ;
padding : 32px ;
}
.stage span {
width : 44px ;
height : 100% ;
border-radius : 12px ;
background : #2854ff ;
transform-origin : bottom;
}
.stage span :nth-child (2 n) {
background : #8fb0ff ;
}
.stage span :nth-child (3 ) {
background : #5c7cff ;
}start, stop และ reset ตัวควบคุมทุกตัวมี start(callback?), stop() และ reset() callback ได้รับ { finished } ซึ่งเป็น false เมื่อการเคลื่อนไหวถูกหยุดหรือถูกแทนที่
การเริ่ม driver ใหม่บนค่าเดิมจะแทนที่ตัวที่กำลังรันจากตำแหน่งปัจจุบัน ไม่มีการกระโดดไปปลายด้านใด
controller.js คัดลอกโค้ด const move = timing (value, { toValue : 1 , duration : 600 });
move.start (({ finished } ) => {
});
move.stop ();
move.reset (); การตามค่าอื่น toValue ที่มีชีวิตจะเริ่ม driver ใหม่ทุกครั้งที่ค่านั้นเปลี่ยน เหมือน tracking ใน React Native follow(target, source) คือการเชื่อมด้วยสปริงแบบเดียวกันในรูปฟังก์ชันที่มีชื่อ
tracking.js คัดลอกโค้ด
spring (badge, { toValue : pointer, stiffness : 300 , damping : 26 }).start ();
follow (badge, pointer, { stiffness : 300 , damping : 26 });Easing และคีย์เฟรม Easing ตรงกับ React Native: linear, quad, cubic, sin, circle, exp, elastic, back, bounce, bezier และตัวช่วย in, out, inOut keyframes(value, frames, options) พาค่าเดียวผ่านตัวเลขหลายตัว โดยมี times และ repeat เป็นตัวเลือก