基于HDialog的特殊动画效果实现
业务场景
在开发过程中直接使用HDialog所展现的效果很快,同时不能够与用户所点击位置进行交互,会造成用户的体验观感不够好。因此需要实现一种能够从用户点击按钮位置以可变动画效果所展现的Dialog效果。
工作原理及实现思路
(1) 基本定位:图1标识了实现特殊动画效果所需要的主要位置信息,其中最大的蓝色框表示浏览器大小,蓝色三角表示用户点击位置。由于需要从用户点击位置对动画进行触发,因此考虑到使用transform-origin属性。随后需要通过计算得到用户点击位置相对于HDialog弹出框的位置(即offsetTop-clickpos.y与offsetLeft-clickpos.x),将transform-origin属性设置为此相对位置即可完成基本定位。
(2) 事件处理:根据(1)的分析,用户点击按钮以触发弹出框的过程中需要得到用户所点击的位置,因此需要在当前document.documentElement上添加额外的点击事件以获取用户点击位置,同时需要触发捕获使此点击事件在弹出框触发点击事件前执行。
(3) 添加动画:通过控制台调试得知,在HDialog弹出与隐藏的过程中,其Dom元素会分别动态添加dialog-fade-enter-active类与dialog-fade-leave-active类,通过在此动态类的子自定义组件类(custom-class所设置)上添加动画即可完成HDialog的特殊动画效果。
代码实现
<script setup lang="ts">
import { nextTick, ref, watch } from 'vue'
const visible = ref(false)
const visible_or = ref(false)
const mousePosition = {
x: 0,
y: 0,
};
function handleMouseMove(e: MouseEvent) {
mousePosition.x = e.x;
mousePosition.y = e.y;
}
document.documentElement.addEventListener('click', handleMouseMove, true);
const modalRef = ref<any>();
watch(
() => visible.value,
async(value) => {
console.log("visible change", value);
if (value) {
await nextTick();
const node: HTMLElement = document.querySelector(".h-dialog.dialog-demo") as HTMLElement;
const computedStyle = window.getComputedStyle(node);
let width;
if (/px/g.test(computedStyle.width)) {
// 如果宽度是像素类型
// 正则替换像素成为数字格式
width = Number(computedStyle.width.replace(/px/g, ""));
} else {
// 宽度为百分比类型
// 正则替换百分比并转化为数字格式
width = document.documentElement.clientWidth * (Number(computedStyle.width.replace(/%/g, "")) / 100);
}
const top = computedStyle.marginTop.replace(/px/g, ""); // 对话框距离顶部的距离
// 计算变换偏移
const transformLeft = mousePosition.x - (document.documentElement.clientWidth - width) / 2; // 本质上为对话框左上角的 x 距离触发点 x 的距离
const transformTop = mousePosition.y - Number(top); // 本质上为对话框左上角的 y 距离触发点 y 的距离
node.style.transformOrigin = `${transformLeft}px ${transformTop}px`;
}
}
)
</script>
<template>
<div class="flex flex-col gap-2">
<div>修改版本:</div>
<div class="flex test">
<h-button @click="visible = true">
基础用法
</h-button>
<h-dialog ref="modalRef" v-model="visible" title="提示" custom-class="dialog-demo">
你好啊!这是一个弹框的基本功能
<template #footer>
<h-button type="primary" @click="visible = false">
确 定
</h-button>
<h-button @click="visible = false">
取 消
</h-button>
</template>
</h-dialog>
</div>
</div>
<div class="flex flex-col gap-2">
<div>原始版本:</div>
<div class="flex test">
<h-button @click="visible_or = true">
基础用法
</h-button>
<h-dialog ref="modalRef" v-model="visible_or" title="提示">
你好啊!这是一个弹框的基本功能
<template #footer>
<h-button type="primary" @click="visible_or = false">
确 定
</h-button>
<h-button @click="visible_or = false">
取 消
</h-button>
</template>
</h-dialog>
</div>
</div>
</template>
<style>
.dialog-fade-enter-active .h-dialog.dialog-demo {
animation: ani-open .3s ease;
}
.dialog-fade-leave-active .h-dialog.dialog-demo {
animation: ani-close .3s ease;
}
@keyframes ani-open {
0% {
transform: scale(0);
}
100% {
transition: scale(1);
}
}
@keyframes ani-close {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
</style>
frames ani-close {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
</style>