アニメーション
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
コンポーネントの使用することで多くのアニメーションを簡単に実装することができます。
Motion
コンポーネントは、内部的にFramer Motionを使用しています。さらに詳しいコンポーネントの機能を知りたい場合は、こちらをご覧ください。
アニメーション
initial
・animate
・exit
にスタイルを設定することで、要素にアニメーションを実装できます。また、transition
に所要時間や遅延時間を設定することもできます。
initial
: 要素の初期値のスタイルオブジェクトです。animate
:initial
から変化(アニメーション)させたいスタイルオブジェクトです。exit
: 要素がDOMツリー
上から削除されたときに変化(アニメーション)させたいスタイルオブジェクトです。transition
: 所要時間や遅延時間を設定するオブジェクトです。
initial
・animate
・exit
・transition
で使われるスタイルオブジェクトは、Yamada UIのStyle props
ではありません。スタイルオブジェクトのプロパティは、Framer 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でこのページを編集する