tunFaceSVG.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. <template>
  2. <!-- 二维模型:掘进工作面(模型包 public/svg-models/tun-face-2d/);本组件只保留业务驱动 -->
  3. <SvgModelViewer ref="viewerRef" model-id="tun-face-2d" :identify="identify" />
  4. </template>
  5. <script setup lang="ts">
  6. import { onBeforeUnmount, ref, watch } from 'vue';
  7. import SvgModelViewer from '/@/components/vent/SvgModelViewer.vue';
  8. import { useEngineAnim } from '/@/utils/svg-model/useEngineAnim';
  9. withDefaults(
  10. defineProps<{
  11. /** 组件唯一标识符,提供后可以确保多个组件复用时的元素定位依旧准确 */
  12. identify?: string;
  13. }>(),
  14. { identify: '' }
  15. );
  16. /**
  17. * 掘进工作面流动箭头(与 model.json 的 elements 一一对应):
  18. * 模型里 3 个箭头元素共用一个开关,运行 → 循环流动,停止 → 隐藏。
  19. */
  20. const ARROW_IDS = ['auto_anim_0', 'auto_anim_1', 'auto_anim_2'];
  21. /** 一轮循环时长(10 帧,按观感可调) */
  22. const LOOP_PERIOD_MS = 2200;
  23. const viewerRef = ref<any>(null);
  24. const { engine } = useEngineAnim(viewerRef);
  25. /** 每个箭头元素上的 WAAPI 循环动画 */
  26. let anims: Animation[] = [];
  27. let running = true;
  28. function stop() {
  29. anims.forEach((a) => {
  30. try {
  31. a.cancel();
  32. } catch (e) {
  33. /* ignore */
  34. }
  35. });
  36. anims = [];
  37. }
  38. function setVisible(visible: boolean) {
  39. const eng = engine.value;
  40. if (!eng) return;
  41. ARROW_IDS.forEach((id) => {
  42. const el = eng.getElement(id) as HTMLElement | undefined;
  43. if (el) el.style.display = visible ? '' : 'none';
  44. });
  45. }
  46. /** 开始循环流动:帧序列本身就是一段完整的小动画,直接循环播放 */
  47. function start() {
  48. stop();
  49. const eng = engine.value;
  50. if (!eng) return;
  51. ARROW_IDS.forEach((id) => {
  52. const el = eng.getElement(id);
  53. const frames = eng.getElementInfo(id)?.frames;
  54. if (!el || !frames || frames.length < 2) return;
  55. // model.json 里这排箭头的首尾位移 = 相邻箭头图案间距,循环回绕时正好落到相邻箭头上
  56. const keyframes = frames.map((transform) => ({ transform }));
  57. const anim = (el as HTMLElement).animate(keyframes, {
  58. duration: LOOP_PERIOD_MS,
  59. iterations: Infinity,
  60. easing: 'linear',
  61. fill: 'both',
  62. });
  63. anims.push(anim);
  64. });
  65. }
  66. /**
  67. * 掘进工作面二维模型开关
  68. *
  69. * @param flag 是否运行:true(默认)→ 箭头显示并循环流动;false → 箭头隐藏(停止态)
  70. */
  71. function animate(flag: boolean = true) {
  72. running = flag;
  73. setVisible(flag);
  74. if (flag) start();
  75. else stop();
  76. }
  77. watch(engine, (eng) => {
  78. if (!eng) return;
  79. if (running) {
  80. setVisible(true);
  81. start();
  82. } else {
  83. setVisible(false);
  84. }
  85. });
  86. onBeforeUnmount(() => {
  87. stop();
  88. });
  89. /** 页面通过 modelRef.value?.animate?.(running) 控制(当前页面打开即常驻循环) */
  90. defineExpose({ animate });
  91. </script>