アニメーション
Yamada UIは、アニメーションに特化したコンポーネントのMotionやCSSのkeyframesのように記述できるuseAnimationなど、多くのユーティリティを提供しています。
CSSアニメーション
Yamada UIは、CSSのkeyframesのように記述できるuseAnimationとuseDynamicAnimationを提供しています。また、テーマにトークンを設定することで、プロジェクトにアニメーションの一貫性を担保することができます。
useAnimationを使う
useAnimationは、引数にkeyframesなどを設定し、生成されたanimationをpropsに渡します。
keyframes: アニメーションの流れに沿ったキーフレーム(または中間地点)のスタイルを設定します。各スタイルの値は、Yamada UIのスタイルシステムやテーマのトークンが使用できます。duration: 1回のアニメーションサイクルに要する時間の長さを設定します。timingFunction: アニメーションの進め方を設定します。これは加速曲線を定義することで、キーフレーム間のアニメーションをどのように進めていくかを設定します。delay: 要素が読み込まれてからアニメーションを始めるまでの遅延時間を設定します。iterationCount: アニメーションを繰り返す回数を設定します。アニメーションを無限に繰り返すにはinfiniteを指定してください。direction: アニメーションのシーケンス完了時に、逆方向にアニメーションして繰り返すか、始めの状態にリセットしてアニメーションを繰り返すかを設定します。fillMode: アニメーションの実行前後に、指定したスタイルを適用するかを設定します。playState: アニメーションを一時停止したり、再開したりするかを設定します。
アニメーションの設定は、@keyframesとCSSアニメーションの使用を基本としています。
編集可能な例
const animation = useAnimation({ keyframes: { "0%": { bg: "red.500", }, "20%": { bg: "green.500", }, "40%": { bg: "purple.500", }, "60%": { bg: "yellow.500", }, "80%": { bg: "blue.500", }, "100%": { bg: "red.500", }, }, duration: "10s", iterationCount: "infinite", timingFunction: "linear", }) return <Box w="full" h="xs" animation={animation} />
複数のアニメーションを使う
useAnimationにアニメーションの配列を渡すことで、複数のアニメーションを使うことができます。
編集可能な例
const animation = useAnimation([ { keyframes: { "0%": { bg: "red.500", }, "20%": { bg: "green.500", }, "40%": { bg: "purple.500", }, "60%": { bg: "yellow.500", }, "80%": { bg: "blue.500", }, "100%": { bg: "red.500", }, }, duration: "10s", iterationCount: "infinite", timingFunction: "linear", }, { keyframes: { "0%": { h: "xs", }, "50%": { h: "md", }, "100%": { h: "xs", }, }, duration: "10s", iterationCount: "infinite", timingFunction: "linear", }, { keyframes: { "0%": { w: "full", }, "50%": { w: "50%", }, "100%": { w: "full", }, }, duration: "10s", iterationCount: "infinite", timingFunction: "linear", }, ]) return ( <Box h="md"> <Box w="full" h="xs" animation={animation} /> </Box> )
レスポンシブスタイル
propsのanimationにオブジェクトを渡すだけでPCファーストのレスポンシブスタイルに対応します。
編集可能な例
const desktopAnimation = useAnimation({ keyframes: { "0%": { bg: "red.500", }, "20%": { bg: "green.500", }, "40%": { bg: "purple.500", }, "60%": { bg: "yellow.500", }, "80%": { bg: "blue.500", }, "100%": { bg: "red.500", }, }, duration: "10s", iterationCount: "infinite", timingFunction: "linear", }) const tabletAnimation = useAnimation({ keyframes: { "0%": { bg: "cyan.500", }, "20%": { bg: "emerald.500", }, "40%": { bg: "pink.500", }, "60%": { bg: "amber.500", }, "80%": { bg: "sky.500", }, "100%": { bg: "cyan.500", }, }, duration: "10s", iterationCount: "infinite", timingFunction: "linear", }) return ( <Box w="full" h="xs" animation={{ base: desktopAnimation, md: tabletAnimation }} /> )
レスポンシブスタイルをもっと学びたい場合は、こちらをご覧ください。
カラーモード
propsのanimationに配列を渡すことでカラーモードに対応します。
編集可能な例
const lightAnimation = useAnimation({ keyframes: { "0%": { bg: "red.500", }, "20%": { bg: "green.500", }, "40%": { bg: "purple.500", }, "60%": { bg: "yellow.500", }, "80%": { bg: "blue.500", }, "100%": { bg: "red.500", }, }, duration: "10s", iterationCount: "infinite", timingFunction: "linear", }) const darkAnimation = useAnimation({ keyframes: { "0%": { bg: "red.800", }, "20%": { bg: "green.800", }, "40%": { bg: "purple.800", }, "60%": { bg: "yellow.800", }, "80%": { bg: "blue.800", }, "100%": { bg: "red.800", }, }, duration: "10s", iterationCount: "infinite", timingFunction: "linear", }) return <Box w="full" h="xs" animation={[lightAnimation, darkAnimation]} />
カラーモードをもっと学びたい場合は、こちらをご覧ください。
useDynamicAnimationを使う
useDynamicAnimationは、引数にオブジェクトを渡します。オブジェクトのキーはアニメーションのキーになり、setStateの引数にキーを渡すことでアニメーションが変更されます。
編集可能な例
const [animation, setAnimation] = useDynamicAnimation({ moveLeft: { keyframes: { "0%": { transform: "translateX(400%)", }, "100%": { transform: "translateX(0%)", }, }, duration: "slower", fillMode: "forwards", timingFunction: "ease-in-out", }, moveRight: { keyframes: { "0%": { transform: "translateX(0%)", }, "100%": { transform: "translateX(400%)", }, }, duration: "slower", fillMode: "forwards", timingFunction: "ease-in-out", }, }) return ( <VStack alignItems="flex-start"> <Button onClick={() => setAnimation((prev) => prev === "moveRight" ? "moveLeft" : "moveRight", ) } > Click me! </Button> <Box bg="primary" p="md" rounded="md" color="white" animation={animation}> Box </Box> </VStack> )
複数のアニメーションと組み合わせる
動的なアニメーションと複数のアニメーションを組み合わせることも可能です。
編集可能な例
const [animation, setAnimation] = useDynamicAnimation< Record<"moveLeft" | "moveRight", AnimationStyle[]> >({ moveLeft: [ { keyframes: { "0%": { transform: "translateX(400%)", }, "100%": { transform: "translateX(0%)", }, }, duration: "slower", fillMode: "forwards", timingFunction: "ease-in-out", }, { keyframes: { "0%": { bg: "secondary", }, "100%": { bg: "primary", }, }, duration: "slower", fillMode: "forwards", timingFunction: "ease-in-out", }, ], moveRight: [ { keyframes: { "0%": { transform: "translateX(0%)", }, "100%": { transform: "translateX(400%)", }, }, duration: "slower", fillMode: "forwards", timingFunction: "ease-in-out", }, { keyframes: { "0%": { bg: "primary", }, "100%": { bg: "secondary", }, }, duration: "slower", fillMode: "forwards", timingFunction: "ease-in-out", }, ], }) return ( <VStack alignItems="flex-start"> <Button onClick={() => setAnimation((prev) => prev === "moveRight" ? "moveLeft" : "moveRight", ) } > Click me! </Button> <Box bg="primary" p="md" rounded="md" color="white" animation={animation}> Box </Box> </VStack> )
テーマのトークンを使う
テーマのアニメーションのトークンを使用する場合は、まずテーマの運用をご覧ください。
アニメーションのトークンを作成し、テーマに設定します。
./theme/tokens/animations.ts
import { ThemeAnimationTokens } from "@yamada-ui/react"export const animations: ThemeAnimationTokens = {gradient: {keyframes: {"0%": {bg: "red.500",},"20%": {bg: "green.500",},"40%": {bg: "purple.500",},"60%": {bg: "yellow.500",},"80%": {bg: "blue.500",},"100%": {bg: "red.500",},},duration: "10s",iterationCount: "infinite",timingFunction: "linear",},}
./theme/tokens/index.ts
import { animations } from "./animations"export const tokens = { animations }
./theme/index.ts
import { extendTheme, UsageTheme } from "@yamada-ui/react"import { tokens } from "./tokens"const customTheme: UsageTheme = {...tokens,}export const theme = extendTheme(customTheme)()
作成したアニメーションのトークンは、型補完をすることをオススメします。型補完は、CLIを使用します。
作成したトークンは、コンポーネントのpropsのanimationで指定します。useAnimationとuseDynamicAnimationでも使用することができます。
編集可能な例
return <Box w="full" h="xs" animation="gradient" />
Motion
Motionコンポーネントの使用することで多くのアニメーションを簡単に実装することができます。
アニメーション
initial・animate・exitにスタイルを設定することで、要素にアニメーションを実装できます。また、transitionに所要時間や遅延時間を設定することもできます。
initial: 要素の初期値のスタイルオブジェクトです。animate:initialから変化(アニメーション)させたいスタイルオブジェクトです。exit: 要素がDOMツリー上から削除されたときに変化(アニメーション)させたいスタイルオブジェクトです。transition: 所要時間や遅延時間を設定するオブジェクトです。
initial・animate・exit・transitionで使われるスタイルオブジェクトは、Yamada UIのStyle propsではありません。スタイルオブジェクトのプロパティは、Motionのドキュメントをご覧ください。
編集可能な例
<Center p="lg"> <Motion initial={{ x: -100 }} animate={{ x: 100 }} transition={{ duration: 2, ease: "easeInOut", repeat: Infinity, }} p="md" rounded="md" bg="primary" color="white" > Look me! </Motion> </Center>
AnimatePresenceを使う
Reactでは、要素がDOMツリーから削除されるとアニメーションは維持されません。AnimatePresenceコンポーネントでラッピングすることで、アニメーションが終了するまで要素はDOMツリー上で維持されます。
編集可能な例
const [isVisible, { toggle }] = useBoolean() return ( <> <Button onClick={toggle}>Click me!</Button> <Center h="3xs" gap="md"> <AnimatePresence> {isVisible ? ( <Motion initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 1 }} bg="primary" color="white" p="md" rounded="md" > Enabled "AnimatePresence" </Motion> ) : null} </AnimatePresence> {isVisible ? ( <Motion initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 1 }} bg="secondary" color="white" p="md" rounded="md" > Disabled "AnimatePresence" </Motion> ) : null} </Center> </> )
useAnimationFrameを使う
useAnimationFrameは、要素のアニメーションフレームごとに1回コールバックを実行します。コールバックは、コールバックが最初に呼び出されてからの合計時間と最後のアニメーションフレームからの合計時間を取得することができます。
編集可能な例
const containerRef = useRef<HTMLDivElement>(null) useAnimationFrame((time, delta) => { const rotate = Math.sin(time / 10000) * 200 const y = (1 + Math.sin(time / 1000)) * -50 containerRef.current.style.transform = `translateY(${y}px) rotateX(${rotate}deg) rotateY(${rotate}deg)` }) const sides = useMemo( () => [ { transform: "rotateY(0deg) translateZ(60px)", bg: "red.500" }, { transform: "rotateY(90deg) translateZ(60px)", bg: "orange.500" }, { transform: "rotateY(180deg) translateZ(60px)", bg: "pink.500" }, { transform: "rotateY(-90deg) translateZ(60px)", bg: "purple.500" }, { transform: "rotateX(90deg) translateZ(60px);", bg: "blue.500" }, { transform: "rotateX(-90deg) translateZ(60px)", bg: "green.500" }, ], [], ) return ( <Center h="md"> <Box css={{ perspective: "800px" }} w="120px" h="120px"> <Box ref={containerRef} position="relative" w="120px" h="120px" transformStyle="preserve-3d" > {sides.map(({ transform, bg }) => ( <Box key={bg.split(".")[0]} position="absolute" w="full" h="full" bg={bg} opacity="0.6" transform={transform} /> ))} </Box> </Box> </Center> )
キーフレーム
スタイルオブジェクトのプロパティに配列を渡すことで、キーフレームを設定することができます。各キーフレームは、アニメーション全体で均等な間隔で配置されます。transitionにtimesを設定すると間隔をオーバーライドすることができます。
編集可能な例
<Center h="md"> <Motion animate={{ scale: [1, 2, 2, 1, 1], rotate: [0, 0, 180, 180, 0], borderRadius: ["0%", "0%", "50%", "50%", "0%"], }} transition={{ duration: 2, ease: "easeInOut", times: [0, 0.2, 0.5, 0.8, 1], repeat: Infinity, repeatDelay: 1, }} w="3xs" h="3xs" bg="primary" /> </Center>
アニメーションをもっと知りたい場合は、こちらをご覧ください。
ジェスチャー
ホバー・クリック・タップ・フォーカスを検出し、アニメーションを実装することができます。
ホバー
ホバーのジェスチャは、ポインタが要素上に移動したか離れたかを検出します。
onMouseEnterとonMouseLeaveとの違いは、実際のマウスイベントの結果としてのみ発火することが保証されています。
whileHover: 要素がホバーされたときに発火するアニメーションです。onHoverStart: ポインタが要素上に移動したときに発火するコールバック関数です。onHoverEnd: ポインタが要素上から離れたときに発火するコールバック関数です。
編集可能な例
<Center p="lg"> <Motion as="button" whileHover={{ scale: 1.1 }} onHoverStart={(ev) => console.log("Hover starts")} onHoverEnd={(ev) => console.log("Hover ends")} p="md" rounded="md" bg="primary" color="white" > Hover me! </Motion> </Center>
ホバーのアニメーションをもっと知りたい場合は、こちらをご覧ください。
クリック・タップ
クリック・タップのジェスチャは、主ポインタ(左クリックやタッチなど)が同じ要素を押し下げか、放したかを検出します。
whileTap: 要素がクリック・タップされたときに発火するアニメーションです。onTap: 要素上でクリック・タップが正常に終了したときに発火するコールバック関数です。onTapStart: 要素上でクリック・タップが開始されたときに発火するコールバック関数です。onTapCancel: クリック・タップがキャンセルされた(要素外で放した)ときに発火するコールバック関数です。
編集可能な例
<Center p="lg"> <Motion as="button" whileTap={{ scale: 1.1 }} onTap={(ev, { point }) => console.log("Tap ends", "x:", point.x, "y:", point.y) } onTapStart={(ev, { point }) => console.log("Tap starts", "x:", point.x, "y:", point.y) } onTapCancel={(ev, { point }) => console.log("Tap cancels", "x:", point.x, "y:", point.y) } p="md" rounded="md" bg="primary" color="white" > Click me! </Motion> </Center>
クリック・タップのアニメーションをもっと知りたい場合は、こちらをご覧ください。
フォーカス
フォーカスのジェスチャは、CSSのセレクターであるfocus-visibleと同じルールに従って、要素がフォーカスをされたかを検出します。
編集可能な例
<Center p="lg"> <Motion as="button" whileFocus={{ scale: 1.1 }} p="md" rounded="md" bg="primary" color="white" > Focus me! </Motion> </Center>
フォーカスのアニメーションをもっと知りたい場合は、こちらをご覧ください。
ドラッグ
ポインタの移動を検出し、要素を追従させます。
要素がドラッグを有効にする場合は、dragをtrueにするか、xまたはyを渡します。xまたはyを渡した場合、x軸またはy軸のみ追従します。
onDrag: ドラッグ中に発火するコールバック関数です。onDragStart: ドラッグが開始されたときに発火するコールバック関数です。onDragEnd: ドラッグが終了したときに発火するコールバック関数です。
編集可能な例
<Center h="md" gap="md"> <Motion drag onDrag={(ev, { point }) => console.log("Drag", "x:", point.x, "y:", point.y) } onDragStart={(ev, { point }) => console.log("Drag starts", "x:", point.x, "y:", point.y) } onDragEnd={(ev, { point }) => console.log("Drag ends", "x:", point.x, "y:", point.y) } p="md" cursor="grab" _active={{ cursor: "grabbing" }} rounded="md" bg="primary" color="white" > Drag me! </Motion> <Motion drag="x" p="md" cursor="grab" _active={{ cursor: "grabbing" }} rounded="md" bg="secondary" color="white" > Only X </Motion> <Motion drag="y" p="md" cursor="grab" _active={{ cursor: "grabbing" }} rounded="md" bg="warning" color="white" > Only Y </Motion> </Center>
可能領域を制約する
dragConstraintsにtop・right・bottom・leftごとに値(ピクセル単位)を設定したオブジェクトか、refを渡すことでドラッグの可能領域を制約することができます。
編集可能な例
const containerRef = useRef<HTMLDivElement>(null) return ( <Center ref={containerRef} h="md" gap="md"> <Motion drag dragConstraints={containerRef} p="md" cursor="grab" _active={{ cursor: "grabbing" }} rounded="md" bg="primary" color="white" > Drag me! </Motion> <Motion drag dragConstraints={{ top: 0, right: 100, bottom: 100, left: 0, }} p="md" cursor="grab" _active={{ cursor: "grabbing" }} rounded="md" bg="secondary" color="white" > Only right & bottom </Motion> </Center> )
弾力と勢い
dragElasticにtrue・false・数値またはtop・right・bottom・leftごとに数値を設定したオブジェクトを渡すことで、制約の外側で許可された動きの程度を設定できます。0が動きなし、1が完全に動きます。デフォルトは0.5が設定されています。
編集可能な例
const containerRef = useRef<HTMLDivElement>(null) return ( <Center ref={containerRef} h="md" p="md" gap="md"> <Motion drag dragConstraints={containerRef} dragElastic={0} p="md" cursor="grab" _active={{ cursor: "grabbing" }} rounded="md" bg="primary" color="white" > Drag me! </Motion> </Center> )
dragMomentumにtrueまたはfalseを渡すことで、パンのジェスチャーの勢いを要素に適応します。デフォルトは、trueが設定されています。
編集可能な例
const containerRef = useRef<HTMLDivElement>(null) return ( <Center ref={containerRef} h="md" p="md" gap="md"> <Motion drag dragConstraints={containerRef} dragMomentum={false} p="md" cursor="grab" _active={{ cursor: "grabbing" }} rounded="md" bg="primary" color="white" > Drag me! </Motion> </Center> )
ドラッグのアニメーションをもっと知りたい場合は、こちらをご覧ください。
スクロール
要素がビューポートに出入りしたことを検出し、アニメーションを実装することができます。
whileInView: 要素がビューポートに入ったときに発火するアニメーションです。viewport: ビューポートの検出方法を設定するオブジェクトです。once:trueの場合、要素がビューポートに入った1回だけアニメーションが発火します。root:refを渡すことで、指定された要素のビューポートが仕様されます。指定された要素がない場合は、ウィンドウのビューポートが指定されます。margin: 要素がビューポートに入ったかどうかを検出するときにビューポートに追加するマージンです。デフォルトでは"0px"です。CSSのmarginのように記述("0px -20px 0px 100px")します。amount:"some"・"all"・数値を渡すことでビューポートと交差する必要がある要素の高さを設定します。デフォルトは、"some"です。
onViewportEnter: 要素がビューポートに入ったときに発火するコールバック関数です。onViewportLeave: 要素がビューポートに離れたときに発火するコールバック関数です。
編集可能な例
const containerRef = useRef<HTMLDivElement>(null) return ( <> <Text>Please scroll</Text> <Box ref={containerRef} h="md" p="md" overflowY="auto"> <HStack mt="24rem" justifyContent="center"> <Motion initial={{ x: -100 }} whileInView={{ x: 0 }} viewport={{ root: containerRef }} transition={{ duration: 1 }} onViewportEnter={(entry) => console.log("Scroll entires", entry)} onViewportLeave={(entry) => console.log("Scroll leaves", entry)} p="md" rounded="md" bg="primary" color="white" > You found me! </Motion> <Motion initial={{ x: 100 }} whileInView={{ x: 0 }} viewport={{ once: true, root: containerRef }} transition={{ duration: 1 }} onViewportEnter={(entry) => console.log("Scroll entires", entry)} onViewportLeave={(entry) => console.log("Scroll leaves", entry)} p="md" rounded="md" bg="secondary" color="white" > Once me! </Motion> </HStack> </Box> </> )
useScrollを使う
useScrollは、4つのインスタンスを返します。そのインスタンスを使うことで、スクロールに合わせたアニメーションを実装することができます。
scrollX: ウィンドウのオフセット間のスクロール(x軸)情報のインスタンス。scrollY: ウィンドウのオフセット間のスクロール(x軸)情報のインスタンス。scrollXProgress: 引数で指定された要素のオフセット間のスクロール(x軸)情報のインスタンス。scrollYProgress: 引数で指定された要素のオフセット間のスクロール(y軸)情報のインスタンス。
編集可能な例
const containerRef = useRef<HTMLDivElement>(null) const { scrollYProgress } = useScroll({ container: containerRef }) const scale = useTransform(scrollYProgress, [0, 1], [0.2, 2]) return ( <Box ref={containerRef} position="relative" h="md" overflowY="auto"> <Box h="9xl"> <Box w="2xs" h="2xs" position="sticky" top="50%" left="50%" transform="translate(-50%, -50%)" > <Motion style={{ scale }} w="full" h="full" overflow="hidden" bg={["blackAlpha.200", "whiteAlpha.200"]} rounded="3xl" > <Motion style={{ scaleY: scrollYProgress }} w="inherit" h="inherit" bg="primary" transformOrigin="50% 100%" /> </Motion> </Box> </Box> </Box> )
スクロールのアニメーションをもっと知りたい場合は、こちらをご覧ください。
グローバルなコンフィグ
プロジェクト全体でMotionコンポーネントに共通の設定を付与したい場合は、UIProviderのconfigに設定します。
import { UIProvider, extendConfig } from "@yamada-ui/react"const customConfig = extendConfig({motion: {config: {transition: { duration: 2 },},},})const App = () => {return (<UIProvider config={customConfig}><YourApplication /></UIProvider>)}
コンフィグをカスタマイズしたい場合は、こちらををご覧ください。
GitHubでこのページを編集する

