[vue] vue3封装clip动画, 实现元素的国度效果
import { nextTick } from "vue"; // 数据类型 function getDataType() { return Object.prototype.toString.call(arguments[0]).slice(8, -1).toLowerCase(); } /** * * @param {*} els 单元素节点 或者 元素节点集合 * @param {*} fn 数据变更的函数,通过调用函数导致外部数据变更 * @param {*} duration 动画执行时间,默认500ms */ export default function (els, fn, duration = 500) { // 整合成数组格式 let originList = []; if (getDataType(els) === "htmlcollection") { originList = [...els] } else { originList = [els] } // 生成原始位置 const originPositions = []; originList.forEach(el => { const info = el.getBoundingClientRect() originPositions.push({ top: info.top, left: info.left, width: info.width, height: info.height, bottom: info.bottom, right: info.right, x: info.x, y: info.y, }) }) // 执行函数 fn && fn() nextTick(() => { // 页面更新后获取最新位置 const newPositions = []; originList.forEach(el => { const info = el.getBoundingClientRect() newPositions.push({ top: info.top, left: info.left, width: info.width, height: info.height, bottom: info.bottom, right: info.right, x: info.x, y: info.y, }) }) // 计算移动距离 const movePositions = []; originPositions.forEach((origin, index) => { const newPosition = newPositions[index] const moveX = newPosition.left - origin.left const moveY = newPosition.top - origin.top const newWidth = newPosition.width const newHeight = newPosition.height movePositions.push({ moveX, moveY, newWidth, newHeight, }) }) // 做动画 originList.forEach((el, index) => { const moveX = movePositions[index].moveX const moveY = movePositions[index].moveY el.animate([ { transform: `translate(${-moveX}px, ${-moveY}px)`, width: `${originPositions[index].width}px`, height: `${originPositions[index].height}px`, }, { transform: `translate(0, 0)`, width: `${movePositions[index].newWidth}px`, height: `${movePositions[index].newHeight}px`, } ], duration) }) }) }
解释: js也能做相当于css的动画, 用元素的animate方法
css的写法等同于用js操作dom的css
el.animate([
{css},
{css}
])
核心原理是: 知道元素移动的终点位置(endx,endy), 和元素的起点位置 (startx, starty)
假设: 我们现在处于元素 最终 所在的位置 (endx,endy), 然后通过transform...等手段, 将元素变更到 (startx, starty)位置 (这时候和没移动之前的位置是一样的)
然后通过animate执行动画, 位置就从 (startx, starty) `还原` 到 (endx, endy), 形成动画
在用户看来, 元素是从起点慢慢移动到终点的, 对于程序而言, 元素立即就移动到了终点, 通过 css 变化位置到了起点,慢慢还原
vue的nextTick提供了很好的 虚拟dom支持, 在界面没有渲染之前, 我么就能通过虚拟dom拿到元素变更后的位置信息, 更容易和 animate 结合使用
如果是原生html想做clip动画, 就需要通过其他方法计算提前计算出终点的位置, 然后运用动画
本想把生活活成一首诗, 时而优雅 , 时而豪放 , 结果活成了一首歌 , 时而不靠谱 , 时而不着调