アニメーション
Radixプリミティブへのアニメーションの追加は、他のコンポーネントと似たように感じるはずですが、JSアニメーションライブラリを使用した終了アニメーションに関しては、ここでいくつかの注意点があります。
CSSアニメーションによるアニメーション
プリミティブをアニメーション化する最も簡単な方法はCSSを使用することです。
CSSアニメーションを使用して、マウントフェーズとアンマウントフェーズの両方をアニメーション化できます。後者は、Radixプリミティブがアニメーションの再生中にアンマウントを一時停止するため可能です。
@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トランジションによるアニメーション
CSSアニメーションを使用する以外に、ネイティブのVue <Transition>
を使用することを好むかもしれません。朗報です!コンポーネント(forceMount
プロパティを持つ)をラップするだけで、完了です!
<script setup lang="ts">
import { DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, } from 'radix-vue'
</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>
ヒント
さらに、Web Animations APIベースのアニメーションライブラリであるMotion Oneは、Radix Vueと完全に連携することがわかりました。
このStackblitzデモをチェックしてください🤩
JavaScriptアニメーションのためのアンマウントの委任
多くのステートフルプリミティブがビューから非表示になると、実際にはDOMから削除されます。JavaScriptアニメーションライブラリはアンマウントフェーズを制御する必要があるため、多くコンポーネントにforceMount
プロパティを提供して、コンシューマーがこれらのライブラリによって決定されたアニメーションの状態に基づいて子要素のマウントとアンマウントを委任できるようにします。
たとえば、@vueuse/motionを使用してDialog
をアニメーション化する場合は、コンポーザブルの1つ(useSpring
など)からのアニメーションの状態に基づいて、ダイアログのOverlay
とContent
の部分を条件付きでレンダリングします。
<script setup lang="ts">
import { DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, } from 'radix-vue'
import { reactive, ref, watch } from 'vue'
import { useSpring } from '@vueuse/motion'
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デモをチェックしてください