feat: add animation effects to VbenModal component

This commit is contained in:
 panda7
2025-07-18 00:15:40 +08:00
parent 1bc5d2986b
commit 1a9b0509d5
6 changed files with 67 additions and 2 deletions

View File

@@ -56,6 +56,15 @@ Modal 内的内容一般业务中,会比较复杂,所以我们可以将 moda
<DemoPreview dir="demos/vben-modal/shared-data" /> <DemoPreview dir="demos/vben-modal/shared-data" />
## 动画类型
通过 `animationType` 属性可以控制弹窗的动画效果:
- `slide`(默认):从顶部向下滑动进入/退出
- `scale`:缩放淡入/淡出效果
<DemoPreview dir="demos/vben-modal/animation-type" />
::: info 注意 ::: info 注意
- `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。 - `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
@@ -112,6 +121,7 @@ const [Modal, modalApi] = useVbenModal({
| bordered | 是否显示border | `boolean` | `false` | | bordered | 是否显示border | `boolean` | `false` |
| zIndex | 弹窗的ZIndex层级 | `number` | `1000` | | zIndex | 弹窗的ZIndex层级 | `number` | `1000` |
| overlayBlur | 遮罩模糊度 | `number` | - | | overlayBlur | 遮罩模糊度 | `number` | - |
| animationType | 动画类型 | `'slide' \| 'scale'` | `'slide'` |
| submitting | 标记为提交中,锁定弹窗当前状态 | `boolean` | `false` | | submitting | 标记为提交中,锁定弹窗当前状态 | `boolean` | `false` |
::: info appendToMain ::: info appendToMain

View File

@@ -0,0 +1,36 @@
<script lang="ts" setup>
import { useVbenModal, VbenButton } from '@vben/common-ui';
const [SlideModal, slideModalApi] = useVbenModal({
animationType: 'slide',
});
const [ScaleModal, scaleModalApi] = useVbenModal({
animationType: 'scale',
});
function openSlideModal() {
slideModalApi.open();
}
function openScaleModal() {
scaleModalApi.open();
}
</script>
<template>
<div class="space-y-4">
<div class="flex gap-4">
<VbenButton @click="openSlideModal">滑动动画</VbenButton>
<VbenButton @click="openScaleModal">缩放动画</VbenButton>
</div>
<SlideModal title="滑动动画示例" class="w-[500px]">
<p>这是使用滑动动画的弹窗从顶部向下滑动进入</p>
</SlideModal>
<ScaleModal title="缩放动画示例" class="w-[500px]">
<p>这是使用缩放动画的弹窗以缩放淡入淡出的方式显示</p>
</ScaleModal>
</div>
</template>

View File

@@ -59,6 +59,7 @@ export class ModalApi {
showCancelButton: true, showCancelButton: true,
showConfirmButton: true, showConfirmButton: true,
title: '', title: '',
animationType: 'slide',
}; };
this.store = new Store<ModalState>( this.store = new Store<ModalState>(

View File

@@ -5,6 +5,11 @@ import type { MaybePromise } from '@vben-core/typings';
import type { ModalApi } from './modal-api'; import type { ModalApi } from './modal-api';
export interface ModalProps { export interface ModalProps {
/**
* 动画类型
* @default 'slide'
*/
animationType?: 'scale' | 'slide';
/** /**
* 是否要挂载到内容区域 * 是否要挂载到内容区域
* @default false * @default false

View File

@@ -94,6 +94,7 @@ const {
submitting, submitting,
title, title,
titleTooltip, titleTooltip,
animationType,
zIndex, zIndex,
} = usePriorityValues(props, state); } = usePriorityValues(props, state);
@@ -244,6 +245,7 @@ function handleClosed() {
:modal="modal" :modal="modal"
:open="state?.isOpen" :open="state?.isOpen"
:show-close="closable" :show-close="closable"
:animation-type="animationType"
:z-index="zIndex" :z-index="zIndex"
:overlay-blur="overlayBlur" :overlay-blur="overlayBlur"
close-class="top-3" close-class="top-3"

View File

@@ -20,6 +20,7 @@ import DialogOverlay from './DialogOverlay.vue';
const props = withDefaults( const props = withDefaults(
defineProps< defineProps<
DialogContentProps & { DialogContentProps & {
animationType?: 'scale' | 'slide';
appendTo?: HTMLElement | string; appendTo?: HTMLElement | string;
class?: ClassType; class?: ClassType;
closeClass?: ClassType; closeClass?: ClassType;
@@ -31,7 +32,12 @@ const props = withDefaults(
zIndex?: number; zIndex?: number;
} }
>(), >(),
{ appendTo: 'body', closeDisabled: false, showClose: true }, {
appendTo: 'body',
animationType: 'slide',
closeDisabled: false,
showClose: true,
},
); );
const emits = defineEmits< const emits = defineEmits<
DialogContentEmits & { close: []; closed: []; opened: [] } DialogContentEmits & { close: []; closed: []; opened: [] }
@@ -43,6 +49,7 @@ const delegatedProps = computed(() => {
modal: _modal, modal: _modal,
open: _open, open: _open,
showClose: __, showClose: __,
animationType: ___,
...delegated ...delegated
} = props; } = props;
@@ -100,7 +107,11 @@ defineExpose({
v-bind="forwarded" v-bind="forwarded"
:class=" :class="
cn( cn(
'z-popup bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-top-[48%] w-full p-6 shadow-lg outline-none sm:rounded-xl', 'z-popup bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 w-full p-6 shadow-lg outline-none sm:rounded-xl',
{
'data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-top-[48%]':
animationType === 'slide',
},
props.class, props.class,
) )
" "