useSvgAnimation.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { ref } from 'vue';
  2. /**
  3. * svg二维模型动画使用的钩子,需要配合指定的组件使用,即svg模型组件(README里有更详细的说明)
  4. *
  5. * 备注:一个元素的动画仅有两种状态,正常播放、倒放;例如:`triggerAnimation(id1, false)`代表触发id1对应的动画,false代表触发正常播放的动画
  6. */
  7. export function useSvgAnimation(elementInfo: Map<string, { key: string; transforms: string[] }>) {
  8. /** 所有动画元素 */
  9. const animationElements = new Map<string, HTMLElement>();
  10. /** 管理节点是否处于初始状态 */
  11. const animationManager = ref<{ [id: string]: boolean }>({});
  12. /**
  13. * 触发动画函数,该函数用来根据id查找SVG图片中的对应group,然后触发绑定在此group上的动画
  14. *
  15. * 动画有且仅有两个状态,一种是初始状态,一种是结束状态,当动画触发后,会根据reverse传参自动切换状态
  16. *
  17. * @param id 标识符号(可以在页面中使用元素选择器选择具体元素后查询其id),可以传数组
  18. * @param reverse 是否需要反向执行动画,如果id传了数组该参数可以传数组以一一匹配,默认为false
  19. */
  20. function triggerAnimation(id: string | string[], reverse: boolean | boolean[] = false, duration = 3000) {
  21. const idArray = typeof id === 'string' ? [id] : id;
  22. const reverseArray = typeof reverse === 'boolean' ? idArray.map(() => reverse) : reverse;
  23. idArray.forEach((id, index) => {
  24. if (animationManager.value[id] === undefined) {
  25. animationManager.value[id] = true;
  26. }
  27. const unchanged = animationManager.value[id];
  28. const reverse = reverseArray[index] || false;
  29. // 不指定反向播放且group处于初始状态时播放正常动画
  30. if (!reverse && unchanged) {
  31. animationManager.value[id] = false;
  32. animateByKey(id, true, duration);
  33. return;
  34. }
  35. if (reverse && !unchanged) {
  36. animationManager.value[id] = true;
  37. animateByKey(id, false, duration);
  38. return;
  39. }
  40. });
  41. }
  42. // 直接控制动画的方法
  43. const animateElement = (elementId: string, toEnd: boolean, duration: number = 3000) => {
  44. const el = animationElements.get(elementId);
  45. const info = elementInfo.get(elementId);
  46. if (el && info && info.transforms.length > 1) {
  47. el.style.transition = `transform ${duration}ms`;
  48. el.setAttribute('transform', toEnd ? info.transforms[info.transforms.length - 1] : info.transforms[0]);
  49. }
  50. };
  51. // 批量控制同一key的所有元素
  52. const animateByKey = (key: string, toEnd: boolean, duration: number = 3000) => {
  53. animationElements.forEach((__, elementId) => {
  54. const info = elementInfo.get(elementId);
  55. if (info && info.key === key) {
  56. animateElement(elementId, toEnd, duration);
  57. }
  58. });
  59. };
  60. // watch(
  61. // () => animationManager,
  62. // () => {
  63. // Object.keys(animationManager).forEach((key) => {
  64. // const unchanged = animationManager[key];
  65. // // 找到所有属于这个key的元素
  66. // animateByKey(key, !unchanged);
  67. // });
  68. // },
  69. // { deep: true }
  70. // );
  71. return {
  72. animationElements,
  73. triggerAnimation,
  74. animateElement,
  75. animateByKey,
  76. };
  77. }