Motion
Motionは、MotionにYamada UIのStyle Propsを拡張した便利なコンポーネントです。
<Center h="sm">
<Motion
boxSize="4xs"
bg="bg.contrast"
color="fg.contrast"
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,
}}
/>
</Center>
使い方
import { Motion } from "@yamada-ui/react"
import { Motion } from "@/components/ui"
import { Motion } from "@workspaces/ui"
initial: コンポーネントの初期状態。animate: コンポーネントがマウントされた時や更新された時に実行されるアニメーション。exit: コンポーネントがアンマウントされた時に実行されるアニメーション。transition: 所用時間や遅延時間を設定するオブジェクト。
initial・animate・exitで使われるスタイルオブジェクトは、Yamada UIのStyle Propsではありません。スタイルオブジェクトの各プロパティは、Motionのドキュメントをご覧ください。exitのアニメーションを有効にする場合は、コンポーネントがAnimatePresenceの子要素である必要があります。バリアント
バリアントを設定すると、動的なアニメーションの実装に便利です。他にもアニメーションをオーケストレーションすることも可能です。
const [visible, { toggle }] = useBoolean()
return (
<VStack>
<Button alignSelf="flex-start" onClick={toggle}>
Click me!
</Button>
<Motion
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
initial={false}
animate={visible ? "visible" : "hidden"}
transition={{ duration: 1 }}
variants={{
visible: { opacity: 1 },
hidden: { opacity: 0 },
}}
>
Look at me!
</Motion>
</VStack>
)
"use client"をファイルの上部に追加する必要があります。AnimatePresenceを使う
Reactでは、コンポーネントがアンマウントされた時、アニメーションは維持されません。AnimatePresenceを使用することで、アニメーションが終了するまでコンポーネントはアンマウントされません。
const [visible, { toggle }] = useBoolean()
return (
<VStack h="sm">
<Button alignSelf="flex-start" onClick={toggle}>
Click me!
</Button>
<Center flex="1" gap="md">
<AnimatePresence>
{visible ? (
<Motion
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 1 }}
>
Enabled "AnimatePresence"
</Motion>
) : null}
</AnimatePresence>
{visible ? (
<Motion
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 1 }}
>
Disabled "AnimatePresence"
</Motion>
) : null}
</Center>
</VStack>
)
"use client"をファイルの上部に追加する必要があります。キーフレーム
値を配列にすることで、キーフレームを設定できます。各フレームは、均等な間隔で処理されます。transitionのtimesに間隔を設定した配列を設定するとオーバーライドできます。
<Center h="sm">
<Motion
boxSize="4xs"
bg="bg.contrast"
color="fg.contrast"
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,
}}
/>
</Center>
ジェスチャー
ホバー・クリック・タップ・フォーカスを検出し、アニメーションを実行することができます。
ホバー
whileHover: ポインターがコンポーネント上を移動したときに実行されるアニメーション。onHoverStart: ポインターがコンポーネント上を移動したときに実行されるコールバック関数。onHoverEnd: ポインターがコンポーネントから離れたときに実行されるコールバック関数。
<Motion
cursor="pointer"
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
whileHover={{ scale: 1.1 }}
onHoverStart={(ev) => console.log("Hover starts")}
onHoverEnd={(ev) => console.log("Hover ends")}
>
Hover me!
</Motion>
"use client"をファイルの上部に追加する必要があります。クリック・タップ
whileTap: ポインターがコンポーネントをクリックまたはタップしたときに実行されるアニメーション。onTapStart: ポインターがコンポーネントを押し始めたときに実行されるコールバック関数。onTap: ポインターがコンポーネントの押し下げをキャンセルし、コンポーネント内でポインターが解放されたときに実行されるコールバック関数。onTapCancel: ポインターがコンポーネントの押し下げをキャンセルし、コンポーネント外でポインターが解放されたときに実行されるコールバック関数。
<Motion
cursor="pointer"
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
whileTap={{ scale: 0.9 }}
onTapStart={(ev) => console.log("Tap starts")}
onTap={(ev) => console.log("Tap")}
onTapCancel={(ev) => console.log("Tap cancels")}
>
Click and Tap me!
</Motion>
"use client"をファイルの上部に追加する必要があります。フォーカス
whileFocus: コンポーネントがフォーカスされたときに実行されるアニメーション。
<Motion
cursor="pointer"
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
tabIndex={0}
whileFocus={{ scale: 1.1 }}
>
Focus me!
</Motion>
"use client"をファイルの上部に追加する必要があります。ドラッグ
コンポーネントのドラッグを有効にする場合は、dragをtrueに設定します。または"x"または"y"を設定することで、x軸またはy軸のみを追従します。
whileDrag: コンポーネントがドラッグされたときに実行されるアニメーション。onDrag: コンポーネントがドラッグ中に実行されるコールバック関数。onDragStart: コンポーネントがドラッグを開始したときに実行されるコールバック関数。onDragEnd: コンポーネントがドラッグを終了したときに実行されるコールバック関数。
<Center h="sm" gap="md">
<For each={[true, "x", "y"]}>
{(drag) => (
<Motion
cursor={{ base: "grab", _active: "grabbing" }}
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
drag={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)
}
>
{drag === true ? "Drag me!" : drag === "x" ? "Only X" : "Only Y"}
</Motion>
)}
</For>
</Center>
"use client"をファイルの上部に追加する必要があります。可能領域を制限する
可能領域を制限する場合は、dragConstraintsのtop・bottom・left・rightに値(ピクセル)を設定します。
<Center h="sm" gap="md">
<Motion
cursor={{ base: "grab", _active: "grabbing" }}
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
drag
dragConstraints={{ top: 0, right: 100, bottom: 100, left: 0 }}
>
Only Right & Bottom
</Motion>
</Center>
または、refを設定することで可能領域を制限することも可能です。
const ref = useRef<HTMLDivElement>(null)
return (
<Center ref={ref} h="sm" gap="md">
<Motion
cursor={{ base: "grab", _active: "grabbing" }}
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
drag
dragConstraints={ref}
>
Drag me!
</Motion>
</Center>
)
"use client"をファイルの上部に追加する必要があります。弾力を設定する
弾力を設定する場合は、dragElasticに真偽値・数値またはtop・bottom・left・rightごとに値(ピクセル)を設定したオブジェクトを設定します。
const ref = useRef<HTMLDivElement>(null)
return (
<Center ref={ref} h="sm" gap="md">
<Motion
cursor={{ base: "grab", _active: "grabbing" }}
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
drag
dragElastic={0}
dragConstraints={ref}
>
Drag me!
</Motion>
</Center>
)
"use client"をファイルの上部に追加する必要があります。勢いを設定する
勢いを設定する場合は、dragMomentumに真偽値を設定します。
const ref = useRef<HTMLDivElement>(null)
return (
<Center ref={ref} h="sm" gap="md">
<Motion
cursor={{ base: "grab", _active: "grabbing" }}
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
drag
dragMomentum={false}
dragConstraints={ref}
>
Drag me!
</Motion>
</Center>
)
"use client"をファイルの上部に追加する必要があります。方向を制限する
方向を制限する場合は、dragDirectionLockをtrueに設定します。
const [direction, setDirection] = useState<"x" | "y" | null>(null)
return (
<Center position="relative" h="sm" gap="md">
<Box
h="full"
border={`1px dashed {colors.${direction === "y" ? "bg.contrast" : "border"}}`}
position="absolute"
top="50%"
left="50%"
transform="translate(-50%, -50%)"
zIndex="-1"
/>
<Box
w="full"
border={`1px dashed {colors.${direction === "x" ? "bg.contrast" : "border"}}`}
position="absolute"
top="50%"
left="50%"
transform="translate(-50%, -50%)"
zIndex="-1"
/>
<Motion
cursor={{ base: "grab", _active: "grabbing" }}
w="fit-content"
p="md"
border="1px solid {colors.bg.contrast}"
drag
dragDirectionLock
dragConstraints={{ top: 0, right: 0, bottom: 0, left: 0 }}
dragTransition={{ bounceStiffness: 500, bounceDamping: 15 }}
dragElastic={0.2}
onDirectionLock={(direction) => setDirection(direction)}
onDragEnd={() => setDirection(null)}
>
Drag me!
</Motion>
</Center>
)
"use client"をファイルの上部に追加する必要があります。スクロール
whileInView: コンポーネントがビューポートに入ったときに実行されるアニメーション。viewport: ビューポートの検出方法を設定するオブジェクト。once:trueの場合は、コンポーネントがビューポートに入った1回目にアニメーションを実行します。root: スクロール可能な要素(ref)を設定することで、windowの代わりにその要素をビューポートとして使用します。margin: ビューポートに追加するマージン。amount:"some"・"all"・数値を設定することで、ビューポートと交差する必要がある要素の高さを設定します。
onViewportEnter: コンポーネントがビューポートに入ったときに実行されるコールバック関数。onViewportLeave: コンポーネントがビューポートから出たときに実行されるコールバック関数。
Scroll me!
const ref = useRef<HTMLDivElement>(null)
return (
<VStack ref={ref} maxH="sm" overflowY="auto">
<Text>Scroll me!</Text>
<Spacer />
<Center gap="md" mt="{sizes.xl}">
<For each={[false, true]}>
{(once) => (
<Motion
cursor="pointer"
w="fit-content"
p="md"
bg="bg.contrast"
color="fg.contrast"
initial={{ x: once ? 100 : -100 }}
whileInView={{ x: 0 }}
viewport={{ root: ref, once }}
transition={{ duration: 1 }}
onViewportEnter={(entry) => console.log("Scroll entires", entry)}
onViewportLeave={(entry) => console.log("Scroll leaves", entry)}
>
{once ? "Once me!" : "You found me!"}
</Motion>
)}
</For>
</Center>
</VStack>
)
"use client"をファイルの上部に追加する必要があります。設定
プロジェクト全体でMotionの共通の設定をする場合は、MotionConfigを使用します。
import { MotionConfig } from "motion/react"
import { UIProvider } from "@yamada-ui/react"
const App = () => {
return (
<MotionConfig transition={{ duration: 1 }}>
<UIProvider>
<YourApplication />
</UIProvider>
</MotionConfig>
)
}
類似のコンポーネント
Rotate
Rotateは、2つの要素を回転させながら切り替えるアニメーションを提供するコンポーネントです。
Ripple
Rippleは、要素に波及効果を付与し、ユーザーがクリックしたかどうかを認識させるコンポーネントです。
Flip
Flipは、2つの要素をフリップさせながら切り替えるアニメーションを提供するコンポーネントです。
Airy
Airyは、2つの要素をふわっと切り替えるアニメーションを提供するコンポーネントです。
SlideFade
SlideFadeは、要素を指定位置から移動しながら次第に表示または非表示にするコンポーネントです。
Slide
Slideは、要素をページの隅から表示または非表示にするコンポーネントです。
Skeleton
Skeletonは、コンテンツが読み込まれるまでのプレースホルダーとして機能するコンポーネントです。
Fade
Fadeは、要素を次第に表示または非表示にするコンポーネントです。
使用されているコンポーネント・フック
Accordion
Accordionは、情報を展開または折りたたんで表示するリストのコンポーネントです。
Airy
Airyは、2つの要素をふわっと切り替えるアニメーションを提供するコンポーネントです。
Collapse
Collapseは、要素を展開または折りたたんで表示するコンポーネントです。
Drawer
Drawerは、画面の端から表示されるパネルのコンポーネントです。
Fade
Fadeは、要素を次第に表示または非表示にするコンポーネントです。
FadeScale
FadeScaleは、要素を次第に拡大して表示または縮小して非表示にするコンポーネントです。
Flip
Flipは、2つの要素をフリップさせながら切り替えるアニメーションを提供するコンポーネントです。
Loading
Loadingは、データの読み込み中などの待機時間に表示するコンポーネントです。
Modal
Modalは、主要なコンテンツに重ねて表示され、ユーザーの注意を情報のみに集中させるコンポーネントです。
Popover
Popoverは、要素の周りにフローティングして情報を表示するコンポーネントです。
Reorder
Reorderは、項目の順序をドラッグアンドドロップで変更できるコンポーネントです。
Ripple
Rippleは、要素に波及効果を付与し、ユーザーがクリックしたかどうかを認識させるコンポーネントです。
Rotate
Rotateは、2つの要素を回転させながら切り替えるアニメーションを提供するコンポーネントです。
SegmentedControl
SegmentedControlは、ユーザーが複数の選択肢の中から1つを選択するために使用されるコンポーネントです。
Slide
Slideは、要素をページの隅から表示または非表示にするコンポーネントです。
SlideFade
SlideFadeは、要素を指定位置から移動しながら次第に表示または非表示にするコンポーネントです。
Snacks
Snacksは、フォームなどで使用される通知を制御するコンポーネントです。
Tooltip
Tooltipは、要素の補足など短い情報を表示するコンポーネントです。