Yamada UIにスターをあげる

スター
Yamada UIYamada UIv1.3.10

Motion

Motionは、framer-motionにYamada UIのスタイルシステムを拡張した便利なコンポーネントです。

ソース@yamada-ui/motion

インポート

import { Motion } from "@yamada-ui/react"

使い方

initialanimateexitにスタイルを設定することで、要素にアニメーションを実装できます。また、transitionに所要時間や遅延時間を設定することもできます。

  • initial: 要素の初期値のスタイルオブジェクトです。
  • animate: initialから変化(アニメーション)させたいスタイルオブジェクトです。
  • exit: 要素がDOMツリー上から削除されたときに変化(アニメーション)させたいスタイルオブジェクトです。
  • transition: 所要時間や遅延時間を設定するオブジェクトです。

編集可能な例

<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"
  >
    Hover 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>
  </>
)

useMotionAnimationFrameを使う

useMotionAnimationFrameは、要素のアニメーションフレームごとに1回コールバックを実行します。コールバックは、コールバックが最初に呼び出されてからの合計時間最後のアニメーションフレームからの合計時間を取得することができます。

編集可能な例

const containerRef = useRef<HTMLDivElement>(null)

useMotionAnimationFrame((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>
)

キーフレーム

スタイルオブジェクトのプロパティに配列を渡すことで、キーフレームを設定することができます。各キーフレームは、アニメーション全体で均等な間隔で配置されます。transitiontimesを設定すると間隔をオーバーライドすることができます。

編集可能な例

<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>

ジェスチャー

ホバー・クリック・タップ・フォーカスを検出し、アニメーションを実装することができます。

ホバー

ホバーのジェスチャは、ポインタが要素上に移動したか離れたかを検出します。

onMouseEnteronMouseLeaveとの違いは、実際のマウスイベントの結果としてのみ発火することが保証されています。

  • 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>

ドラッグ

ポインタの移動を検出し、要素を追従させます。

要素がドラッグを有効にする場合は、dragtrueにするか、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>

可能領域を制約する

dragConstraintstoprightbottomleftごとに値(ピクセル単位)を設定したオブジェクトか、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>
)

弾力と勢い

dragElastictruefalse数値またはtoprightbottomleftごとに数値を設定したオブジェクトを渡すことで、制約の外側で許可された動きの程度を設定できます。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>
)

dragMomentumtrueまたは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"です。CSSmarginのように記述("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"
        >
          Abled to meet 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コンポーネントに共通の設定を付与したい場合は、UIProviderconfigに設定します。

import { UIProvider, extendConfig } from "@yamada-ui/react"
const customConfig = extendConfig({
motion: {
config: {
transition: { duration: 2 },
},
},
})
const App = () => {
return (
<UIProvider config={customConfig}>
<YourApplication />
</UIProvider>
)
}

GitHubでこのページを編集する

FocusLockPortal