Lzh on GitHub

动画

使用 CSS 关键帧、原生 Vue Transition 或您选择的 JavaScript 动画库为 Reka UI 添加动画。

为 Reka UI 添加动画应类似于其他组件,但需注意:使用 JS 动画库时,退出动画存在某些注意事项。

使用 CSS 动画

动画化 Primitives 最简单的方法是使用 CSS。

您可以使用 CSS 动画来动画挂载和卸载阶段。后者之所以可能,是因为 Reka UI 会在动画播放期间暂停卸载。

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

.DialogOverlay[data-state="open"],
.DialogContent[data-state="open"] {
  animation: fadeIn 300ms ease-out;
}

.DialogOverlay[data-state="closed"],
.DialogContent[data-state="closed"] {
  animation: fadeOut 300ms ease-in;
}

使用 Vue Transition 动画

除了使用 CSS 动画之外,您可能更倾向于使用原生 Vue <Transition>。好消息!只需包裹带有 forceMount prop 的组件即可轻松完成!

<script setup lang="ts">
import {
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogOverlay,
  DialogPortal,
  DialogRoot,
  DialogTitle,
  DialogTrigger,
} from 'reka-ui'
</script>

<template>
  <DialogRoot v-model:open="open">
    <DialogTrigger>
      Edit profile
    </DialogTrigger>
    <DialogPortal>
      <Transition name="fade">
        <DialogOverlay />
      </Transition>
      <Transition name="fade">
        <DialogContent>
          <h1>Hello from inside the Dialog!</h1>
          <DialogClose>Close</DialogClose>
        </DialogContent>
      </Transition>
    </DialogPortal>
  </DialogRoot>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

⭐️ 使用 Motion Vue 动画

Motion Vue 是 Reka UI 推荐的动画库。这个轻量级、强大的库与组件无缝集成,并为创建流畅、高性能的动画提供了广泛的灵活性。

<script setup lang="ts">
import { AnimatePresence, Motion } from 'motion-v'
import {
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogOverlay,
  DialogPortal,
  DialogRoot,
  DialogTitle,
  DialogTrigger,
} from 'reka-ui'
</script>

<template>
  <DialogRoot>
    <DialogTrigger>
      Edit profile
    </DialogTrigger>
    <DialogPortal>
      <AnimatePresence multiple>
        <DialogOverlay as-child>
          <Motion
            :initial="{ opacity: 0, scale: 0 }"
            :animate="{ opacity: 1, scale: 1 }"
            :exit="{ opacity: 0, scale: 0.6 }"
          />
        </DialogOverlay>
        
        <DialogContent as-child>
          <Motion
            :initial="{ opacity: 0, top: '0%' }"
            :animate="{ opacity: 1, top: '50%' }"
            :exit="{ opacity: 0, top: '30%' }"
          >
            <h1>Hello from inside the Dialog!</h1>
            <DialogClose>Close</DialogClose>
          </Motion>
        </DialogContent>
      </AnimatePresence>
    </DialogPortal>
  </DialogRoot>
</template>
查看这个 Stackblitz 演示 🤩

委托 JavaScript 动画的卸载控制

当许多有状态的 Primitives 从视图中隐藏时,它们实际上是从 DOM 中移除的。JavaScript 动画库需要控制卸载阶段,因此我们在许多组件上提供了 forceMount prop,以允许消费者根据这些库确定的动画状态来委托子组件的挂载和卸载。

例如,如果您想使用 @vueuse/motion 来动画化 Dialog,您可以通过根据其可组合项(例如 useSpring)的动画状态有条件地渲染对话框 OverlayContent 部分来做到这一点:

<script setup lang="ts">
import { useSpring } from '@vueuse/motion'
import {
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogOverlay,
  DialogPortal,
  DialogRoot,
  DialogTitle,
  DialogTrigger,
} from 'reka-ui'
import { reactive, ref, watch } from 'vue'

const stages = {
  initial: { opacity: 0, scale: 0, top: 0 },
  enter: { opacity: 1, scale: 1, top: 50 },
  leave: { opacity: 0, scale: 0.6, top: 30 },
}

const styles = reactive(stages.initial)
const { set } = useSpring(styles, {
  damping: 8,
  stiffness: 200,
})

const open = ref(false)

watch(open, () => {
  if (open.value) set(stages.enter)
  else set(stages.leave)
})
</script>

<template>
  <DialogRoot v-model:open="open">
    <DialogTrigger>
      Edit profile
    </DialogTrigger>
    <DialogPortal v-if="styles.opacity !== 0">
      <DialogOverlay
        force-mount
        :style="{
          opacity: styles.opacity,
          transform: `scale(${styles.scale})`,
        }"
      />
      <DialogContent
        force-mount
        :style="{
          opacity: styles.opacity,
          top: `${styles.top}%`,
        }"
      >
        <h1>Hello from inside the Dialog!</h1>
        <DialogClose>Close</DialogClose>
      </DialogContent>
    </DialogPortal>
  </DialogRoot>
</template>
查看这个 Stackblitz 演示