123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- import {
- AnimationModel,
- AnimationModelAction,
- AnimationModelFrame,
- AnimationModelPath,
- AnimationModelSubtitle,
- } from "@/api";
- import sdk, {
- 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, uuid } from "@/components/drawing/hook";
- import { getPathNode } from "./path";
- import { debounce, diffArrayChange, mount } from "@/utils";
- import { Pos } from "@/utils/event";
- import Subtitle from "@/components/subtitle/index.vue";
- import { Size } from "@/components/drawing/dec";
- import router, { RoutesName } from "@/router";
- import { paths } from "@/store";
- export let animationGroup: AnimationGroup;
- export const getAMKey = (am: AnimationModel) => am.key || am.id;
- export const amMap: Record<
- string,
- {
- am?: AnimationModel3D;
- globalFrame?: AnimationModelFrame3D;
- frames: Record<string, AnimationModelFrame3D>;
- actions: Record<string, AnimationModelAction3D>;
- paths: Record<string, AnimationModelPath3D>;
- subtitles: Record<string, () => void>;
- }
- > = reactive({});
- export const addAM = (data: AnimationModel): Promise<AnimationModel3D> => {
- 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;
- console.log('0.0', 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<AnimationModelFrame3D> => {
- 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);
- amMap[key]?.am;
- return [amMap[key], exists] as const;
- },
- ([map, exists]) => {
- if (!map?.am) return;
- if (exists && !map.frames[data.id]) {
- 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<AnimationModelAction3D> => {
- 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);
- amMap[key]?.am;
- 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<AnimationModelPath3D> => {
- 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 pathData = computed(() =>
- paths.value.find((item) => item.id === data.pathId)
- );
- const key = getAMKey(am);
- const stopLoad = watch(
- () => {
- const exists = am.paths.some(({ id }) => id === data.id);
- amMap[key]?.am;
- 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(() => {
- path.value && 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<Pos>();
- const stopLoad = watch(
- () => {
- const exists = am.subtitles.some(({ id }) => id === data.id);
- amMap[key]?.am;
- return [amMap[key], exists] as const;
- },
- ([map, exists]) => {
- if (!map?.am) return;
- if (exists && !map.subtitles[data.id]) {
- const mountEl = document.querySelector("#app")!;
- const layer = document.createElement("div");
- layer.className = "subtitle";
- mountEl.appendChild(layer);
- const cleanups = [
- watchEffect(() => {
- layer.innerHTML = data.content;
- size.value = {
- width: layer.offsetWidth,
- height: layer.offsetHeight,
- };
- }),
- watchEffect(() => (layer.style.background = data.background)),
- watchEffect(() => {
- layer.style.visibility =
- pixel.value && show.value ? "visible" : "hidden";
- }),
- watchEffect(() => {
- if (pixel.value) {
- layer.style.transform = `translate(${pixel.value.x}px, calc(${pixel.value.y}px - 50%))`;
- }
- }),
- () => mountEl.removeChild(layer),
- ];
- map.subtitles[data.id] = mergeFuns(cleanups);
- } else if (!exists && map.subtitles[data.id]) {
- map.subtitles[data.id]();
- delete map.subtitles[data.id];
- }
- },
- { immediate: true }
- );
- let isRun = false;
- const update = () => {
- // if (isRun) return;
- // isRun = true
- // setTimeout(() => {
- pixel.value = amMap[am.id]?.am?.getCurrentSubtitlePixel(size.value);
- // isRun = false
- // }, 16);
- };
- const stopAttrib = mergeFuns(
- watch(
- [currentTime, () => amMap[am.id]?.am, size, play, () => amMap[key]?.am],
- (_a, _b, onCleanup) => {
- if (
- !play.value &&
- router.currentRoute.value.name !== RoutesName.animation
- ) {
- show.value = false;
- } else if (
- currentTime.value >= data.time &&
- currentTime.value <= data.time + data.duration
- ) {
- update();
- show.value = true;
- onCleanup(
- watch(
- play,
- (play, _a, onCleanup) => {
- if (!play && _sdk) {
- _sdk.sceneBus.on("cameraChange", update);
- onCleanup(() => _sdk.sceneBus.off("cameraChange", update));
- }
- },
- { immediate: true }
- )
- );
- } 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);
- });
- console.log('amsEndTime', amsEndTime, ams.value)
- return (
- Math.max(...amsEndTime) +
- ((animationGroup.delayEndTime && animationGroup.delayEndTime()) || 0)
- );
- });
- export const play = ref(false);
- watch(play, (_a, _b, onCleanup) => {
- play.value ? animationGroup?.play() : animationGroup?.pause();
- onCleanup(
- watchEffect(() => {
- if (currentTime.value >= endTime.value) {
- play.value = false;
- }
- })
- );
- });
- 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 = []) => {
- console.log("diffam", newv, oldv);
- const { added } = diffArrayChange(newv, oldv);
- added.forEach(addAM);
- },
- { immediate: true }
- );
- watch(
- () => ams.value.flatMap((am) => am.frames),
- (newv, oldv = []) => {
- const { added } = diffArrayChange(newv, oldv);
- added.forEach(addFrame);
- },
- { immediate: true }
- );
- watch(
- () => ams.value.flatMap((am) => am.actions),
- (newv, oldv = []) => {
- const { added } = diffArrayChange(newv, oldv);
- added.forEach(addAction);
- },
- { immediate: true }
- );
- watch(
- () => ams.value.flatMap((am) => am.paths),
- (newv, oldv = []) => {
- const { added } = diffArrayChange(newv, oldv);
- added.forEach(addPath);
- },
- { immediate: true }
- );
- watch(
- () => ams.value.flatMap((am) => am.subtitles),
- (newv, oldv = []) => {
- const { added } = diffArrayChange(newv, oldv);
- added.forEach(addSubtitle);
- },
- { immediate: true }
- );
- let cleanupMap: Record<string, () => void> = {};
- watch(
- () => {
- const gAms = ams.value.filter(
- (am) => !am.frames.length && amMap[am.id]?.am
- );
- return gAms;
- },
- (am3ds, oldAm3ds = []) => {
- const { added, deleted } = diffArrayChange(am3ds, oldAm3ds);
- for (const am of added) {
- const am3d = amMap[am.id];
- if (!am3d || !am3d.am) continue;
- const frame = am3d.am!.addFrame({
- id: uuid(),
- mat: am.mat || am3d.am.getModelPose(),
- name: "global-frame",
- time: 0,
- });
- am3d.globalFrame = frame;
- cleanupMap[am.id] = mergeFuns(
- watchEffect(() => {
- am.mat && frame.setMat(am.mat);
- console.log("set-global-frame", am.mat);
- }),
- () => {
- frame.destroy();
- am3d.globalFrame = undefined;
- delete cleanupMap[am.id];
- }
- );
- }
- for (const am of deleted) {
- cleanupMap[am.id] && cleanupMap[am.id]();
- }
- },
- { flush: "post", immediate: true }
- );
- };
|