import { ref } from 'vue'; import _ from 'lodash'; /** * svg二维模型动画使用的钩子,需要配合指定的组件使用,即svg模型组件(README里有更详细的说明) * * 备注:一个元素的动画仅有两种状态,正常播放、倒放;例如:`triggerAnimation(id1, false)`代表触发id1对应的动画,false代表触发正常播放的动画 */ export function useSvgAnimation(elementInfo: Map) { /** 所有动画元素 */ const animationElements = new Map(); /** 管理节点是否处于初始状态 */ const animationManager = ref<{ [id: string]: boolean }>({}); /** * 触发动画函数,该函数用来根据id查找SVG图片中的对应group,然后触发绑定在此group上的动画 * * 动画有且仅有两个状态,一种是初始状态,一种是结束状态,当动画触发后,会根据reverse传参自动切换状态 * * @param id 标识符号(可以在页面中使用元素选择器选择具体元素后查询其id),可以传数组 * @param reverse 是否需要反向执行动画,如果id传了数组该参数可以传数组以一一匹配,默认为false * @param duration 动画持续时长,越长动画执行的越慢 * @param progress 指定动画执行的进度,默认为1,即动画执行到100%,该数字范围为0-1 */ function triggerAnimation(id: string | string[], reverse: boolean | boolean[] = false, duration = 3000, progress = 1) { const idArray = typeof id === 'string' ? [id] : id; const reverseArray = typeof reverse === 'boolean' ? idArray.map(() => reverse) : reverse; idArray.forEach((id, index) => { if (animationManager.value[id] === undefined) { animationManager.value[id] = true; } const unchanged = animationManager.value[id]; const reverse = reverseArray[index] || false; // 不指定反向播放且group处于初始状态时播放正常动画 if (!reverse && unchanged) { animationManager.value[id] = false; animateByKey(id, true, duration, progress); return; } if (reverse && !unchanged) { animationManager.value[id] = true; animateByKey(id, false, duration, progress); return; } }); } // 直接控制动画的方法 const animateElement = (elementId: string, toEnd: boolean, duration = 3000, progress = 1) => { const el = animationElements.get(elementId); const info = elementInfo.get(elementId); if (el && info && info.transforms.length > 1) { const endTransform = _.nth(info.transforms, Math.floor(info.transforms.length * progress)); const startTransform = _.nth(info.transforms, -Math.ceil(info.transforms.length * progress)); el.style.transition = `transform ${duration}ms`; el.setAttribute('transform', toEnd ? endTransform : startTransform); } }; // 批量控制同一key的所有元素 const animateByKey = (key: string, toEnd: boolean, duration = 3000, progress = 1) => { animationElements.forEach((__, elementId) => { const info = elementInfo.get(elementId); if (info && info.key === key) { animateElement(elementId, toEnd, duration, progress); } }); }; // watch( // () => animationManager, // () => { // Object.keys(animationManager).forEach((key) => { // const unchanged = animationManager[key]; // // 找到所有属于这个key的元素 // animateByKey(key, !unchanged); // }); // }, // { deep: true } // ); return { animationElements, triggerAnimation, animateElement, animateByKey, }; }