import { AnimationModel, AnimationModelAction, AnimationModelFrame, AnimationModelPath, AnimationModelSubtitle, } from "@/api"; import { AnimationGroup, AnimationModel3D, AnimationModelAction3D, AnimationModelFrame3D, AnimationModelPath3D, SDK, sdk as _sdk, } from "../sdk"; import { computed, nextTick, reactive, ref, watch, watchEffect } from "vue"; import { ams } from "@/store/animation"; import { mergeFuns } from "@/components/drawing/hook"; import { getPathNode } from "./path"; import { diffArrayChange, mount } from "@/utils"; import { Pos } from "@/utils/event"; import Subtitle from "@/components/subtitle/index.vue"; import { Size } from "@/components/drawing/dec"; export let animationGroup: AnimationGroup; export const getAMKey = (am: AnimationModel) => am.key || am.id; export const amMap: Record< string, { am?: AnimationModel3D; frames: Record; actions: Record; paths: Record; subtitles: Record void>; } > = reactive({}); export const addAM = (data: AnimationModel): Promise => { const key = getAMKey(data); const stopLoad = watch( () => { const exixts = ams.value.some((am) => getAMKey(am) === key); return [key, exixts] as const; }, ([key, exixts]) => { if (!exixts) { const des = amMap[key]; if (!des) return; Object.values(des.frames || {}).forEach((frame) => frame.destroy()); Object.values(des.actions || {}).forEach((frame) => frame.destroy()); Object.values(des.paths || {}).forEach((frame) => frame.destroy()); des.am?.destroy(); delete amMap[key]; } else if (!amMap[key]) { amMap[key] = { frames: {}, actions: {}, paths: {}, subtitles: {}, }; const am = animationGroup.addAnimationModel(data); am.bus.on("loadDone", () => { amMap[key].am = am; }); } }, { immediate: true } ); const stopAttrib = mergeFuns( watchEffect(() => amMap[key].am?.changeVisibilityRange( data.globalVisibility ? undefined : data.visibilityRange ) ), watchEffect(() => amMap[key].am?.changeTitle(data.title)), watchEffect(() => amMap[key].am?.visibilityTitle(data.showTitle)), watchEffect(() => amMap[key].am?.changeFontSize(data.fontSize)) ); const stopWatch = watch( () => ams.value.includes(data), (exists) => { if (!exists) { stopLoad(); stopAttrib(); stopWatch(); } }, { flush: "post" } ); return new Promise((resolve) => { const stopWatch = watchEffect(() => { if (amMap[key]?.am) { resolve(amMap[key]!.am!); nextTick(() => stopWatch()); } }); }); }; export const addFrame = ( data: AnimationModelFrame ): Promise => { console.log('addFrame') const am = ams.value.find((item) => item.frames.find(({ id }) => id === data.id) ); if (!am) { throw "找不到am数据"; } const key = getAMKey(am); const stopLoad = watch( () => { const exists = am.frames.some(({ id }) => id === data.id); return [amMap[key], exists] as const; }, ([map, exists]) => { if (!map.am) return; if (exists && !map.frames[data.id]) { console.log('add?') map.frames[data.id] = map.am.addFrame(data); } else if (!exists && map.frames[data.id]) { map.frames[data.id].destroy(); delete map.frames[data.id]; } }, { immediate: true } ); const stopAttrib = mergeFuns( watchEffect(() => amMap[key].frames[data.id]?.changeTime(data.time)), watchEffect(() => data.mat && amMap[key].frames[data.id]?.setMat(data.mat)) ); const stopWatch = watch( () => am.frames.includes(data), (exists) => { if (!exists) { stopLoad(); stopAttrib(); stopWatch(); } }, { flush: "post" } ); return new Promise((resolve) => { const stopWatch = watchEffect(() => { if (amMap[key]?.frames[data.id]) { resolve(amMap[key].frames[data.id]); nextTick(() => stopWatch()); } }); }); }; export const addAction = ( data: AnimationModelAction ): Promise => { const am = ams.value.find((item) => item.actions.find(({ id }) => id === data.id) ); if (!am) { throw "找不到am数据"; } const key = getAMKey(am); const stopLoad = watch( () => { const exists = am.actions.some(({ id }) => id === data.id); return [amMap[key], exists] as const; }, ([map, exists]) => { if (!map.am) return; if (exists && !map.actions[data.id]) { map.actions[data.id] = map.am.addAction(data); } else if (!exists && map.actions[data.id]) { map.actions[data.id].destroy(); delete map.actions[data.id]; } }, { immediate: true } ); const stopAttrib = mergeFuns( watchEffect(() => amMap[key].actions[data.id]?.changeTime(data.time)), watchEffect(() => { amMap[key].actions[data.id]?.changeAmplitude(data.amplitude) }), watchEffect(() => amMap[key].actions[data.id]?.changeSpeed(data.speed)), watchEffect(() => amMap[key].actions[data.id]?.changeDuration(data.duration) ) ); const stopWatch = watch( () => am.actions.includes(data), (exists) => { if (!exists) { stopLoad(); stopAttrib(); stopWatch(); } }, { flush: "post" } ); return new Promise((resolve) => { const stopWatch = watchEffect(() => { if (amMap[key]?.actions[data.id]) { resolve(amMap[key].actions[data.id]); nextTick(() => stopWatch()); } }); }); }; export const addPath = ( data: AnimationModelPath ): Promise => { const am = ams.value.find((item) => item.paths.find(({ id }) => id === data.id) ); if (!am) { throw "找不到am数据"; } const path = computed(() => data.pathId ? getPathNode(data.pathId) : undefined ); const key = getAMKey(am); const stopLoad = watch( () => { const exists = am.paths.some(({ id }) => id === data.id); return [amMap[key], exists, path.value] as const; }, ([map, exists, path]) => { if (!map.am || !path) return; if (exists && !map.paths[data.id]) { map.paths[data.id] = map.am.addPath({ ...data, path }); } else if (!exists && map.paths[data.id]) { map.paths[data.id].destroy(); delete map.paths[data.id]; } }, { immediate: true } ); const stopAttrib = mergeFuns( watchEffect(() => amMap[key].paths[data.id]?.changeTime(data.time)), watchEffect(() => amMap[key].paths[data.id]?.changeReverse(data.reverse)), watchEffect(() => amMap[key].paths[data.id]?.changeDuration(data.duration)), watchEffect(() => amMap[key].paths[data.id]?.changePath(path.value)) ); const stopWatch = watch( () => am.paths.includes(data), (exists) => { if (!exists) { stopLoad(); stopAttrib(); stopWatch(); } }, { flush: "post" } ); return new Promise((resolve) => { const stopWatch = watchEffect(() => { if (amMap[key]?.paths[data.id]) { resolve(amMap[key].paths[data.id]); nextTick(() => stopWatch()); } }); }); }; export const addSubtitle = (data: AnimationModelSubtitle) => { const am = ams.value.find((item) => item.subtitles.find(({ id }) => id === data.id) ); if (!am) { throw "找不到am数据"; } const key = getAMKey(am); const size = ref({ width: 0, height: 0 }); const show = ref(false); const pixel = ref(); const stopLoad = watch( () => { const exists = am.subtitles.some(({ id }) => id === data.id); return [amMap[key], exists] as const; }, ([map, exists]) => { if (!map.am) return; if (exists && !map.subtitles[data.id]) { map.subtitles[data.id] = mount( document.querySelector("#app")!, Subtitle, reactive({ pixel, show, data, sizeChang: (csize: Size) => (size.value = csize), }) ); } else if (!exists && map.subtitles[data.id]) { map.subtitles[data.id](); delete map.subtitles[data.id]; } }, { immediate: true } ); const stopAttrib = mergeFuns( watch([currentTime, () => amMap[am.id].am, size], (_a, _b, onCleanup) => { if ( currentTime.value >= data.time && currentTime.value <= (data.time + data.duration) ) { const update = () => pixel.value = amMap[am.id].am?.getCurrentSubtitlePixel(size.value); update() show.value = true; _sdk.sceneBus.on("cameraChange", update); onCleanup(() => _sdk.sceneBus.off("cameraChange", update)) } else { show.value = false; } }, {immediate: true}) ); const stopWatch = watch( () => am.subtitles.includes(data), (exists) => { if (!exists) { stopLoad(); stopAttrib(); stopWatch(); } }, { flush: "post" } ); }; export const endTime = computed(() => { const amsEndTime = ams.value.map((am) => { const endPoints = [...am.frames, ...am.actions, ...am.subtitles, ...am.paths].map( (item) => item.time + (item.duration || 0) ); return Math.max(...endPoints); }) return Math.max(...amsEndTime) + animationGroup.delayEndTime() }) export const currentTime = ref(0); export const associationAnimation = (sdk: SDK, el: HTMLDivElement) => { animationGroup = sdk.createAnimationGroup(); watchEffect(() => { animationGroup.setCurrentTime(currentTime.value); }); animationGroup.bus.on("currentTime", (time) => { currentTime.value = time; }); watch( () => [...ams.value], (newv, oldv = []) => { const { added } = diffArrayChange(newv, oldv); added.forEach(addAM); } ); watch( () => ams.value.flatMap((am) => am.frames), (newv, oldv = []) => { const { added } = diffArrayChange(newv, oldv); added.forEach(addFrame); } ); watch( () => ams.value.flatMap((am) => am.actions), (newv, oldv = []) => { const { added } = diffArrayChange(newv, oldv); added.forEach(addAction); } ); watch( () => ams.value.flatMap((am) => am.paths), (newv, oldv = []) => { const { added } = diffArrayChange(newv, oldv); added.forEach(addPath); } ); watch( () => ams.value.flatMap((am) => am.subtitles), (newv, oldv = []) => { const { added } = diffArrayChange(newv, oldv); added.forEach(addSubtitle); } ); };