index.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. <template>
  2. <div :class="{ focusAM: focusAM }" class="animation-layout">
  3. <Left
  4. :focus="focusAM"
  5. @update:focus="updateFocus"
  6. class="animation-left"
  7. @change-select="changeSelect"
  8. @delete="deleteAm"
  9. />
  10. <Right
  11. v-if="focusAM"
  12. :am="focusAM"
  13. :frameAction="frameAction"
  14. @change-frame-action="(f) => (frameAction = f.action)"
  15. class="animation-right"
  16. v-model:activeAttrib="activeAttrib"
  17. @add-frame="add('frames', { duration: 0 })"
  18. @add-path="(preset) => add('paths', { reverse: false, ...preset })"
  19. @add-subtitle="add('subtitles', { content: '', background: '#fff' })"
  20. @add-action="(preset) => add('actions', preset)"
  21. @apply-global="k => ams.forEach((am: any) => (am[k] = focusAM![k]))"
  22. />
  23. <BottomPano>
  24. <Bottom
  25. :am="focusAM"
  26. v-model:follow="follow"
  27. v-model:current-time="currentTime"
  28. v-model:active="activeAttrib"
  29. />
  30. </BottomPano>
  31. </div>
  32. </template>
  33. <script lang="ts" setup>
  34. import Left from "./left.vue";
  35. import Right from "./right/index.vue";
  36. import Bottom from "./bottom.vue";
  37. import BottomPano from "@/layout/bottom-pano.vue";
  38. import router from "@/router";
  39. import { enterEdit } from "@/store";
  40. import { useViewStack } from "@/hook";
  41. import {
  42. ams,
  43. AnimationModel,
  44. autoSaveAnimationModel,
  45. initAnimationActions,
  46. initialAnimationModels,
  47. } from "@/store/animation";
  48. import { computed, nextTick, reactive, ref, watch, watchEffect } from "vue";
  49. import { Active } from "./type";
  50. import { getAddTLItemAttr } from "@/components/drawing-time-line/check";
  51. import { Message } from "bill/expose-common";
  52. import { mergeFuns, uuid } from "@/components/drawing/hook";
  53. import { title } from "./type";
  54. import { amMap, getAMKey, currentTime } from "@/sdk/association/animation";
  55. import { AnimationModel3D } from "@/sdk";
  56. import { bottomBarHeightStack, showBottomBarStack } from "@/env";
  57. enterEdit(() => router.back());
  58. initialAnimationModels();
  59. initAnimationActions();
  60. useViewStack(autoSaveAnimationModel);
  61. useViewStack(() => showBottomBarStack.push(ref(true)));
  62. const focusAM = ref<AnimationModel>();
  63. const activeAttrib = ref<Active>();
  64. const follow = ref(false);
  65. const frameAction = ref<string>();
  66. const amM = computed(() => focusAM.value && amMap[getAMKey(focusAM.value)]);
  67. const asyncOper = (item: AnimationModel, oper: (obj: AnimationModel3D) => void) => {
  68. let onCleanup = () => {};
  69. if (amMap[getAMKey(item)]?.am) {
  70. oper(amMap[getAMKey(item)]!.am!);
  71. } else {
  72. onCleanup = watchEffect(() => {
  73. if (amMap[getAMKey(item)]?.am!) {
  74. oper(amMap[getAMKey(item)]!.am!);
  75. onCleanup && onCleanup();
  76. }
  77. });
  78. }
  79. return onCleanup;
  80. };
  81. watchEffect((onCleanup) => {
  82. if (!amM.value || activeAttrib.value?.key !== "frames") return;
  83. const frame = focusAM.value!.frames[activeAttrib.value.ndx];
  84. const am3d = amM.value.am;
  85. if (!am3d) return;
  86. const updateMat = () => {
  87. frame.mat = JSON.parse(JSON.stringify(am3d.getModelPose()));
  88. };
  89. am3d.bus.on("transformChanged", updateMat);
  90. switch (frameAction.value) {
  91. case "translate":
  92. am3d.enterMoveMode();
  93. break;
  94. case "rotate":
  95. am3d.enterRotateMode();
  96. break;
  97. case "scale":
  98. am3d.enterScaleMode();
  99. break;
  100. }
  101. onCleanup(() => {
  102. am3d.leaveTransform();
  103. am3d.bus.off("transformChanged", updateMat);
  104. });
  105. });
  106. const updateFocus = (am?: AnimationModel) => {
  107. if (focusAM.value) {
  108. asyncOper(focusAM.value, (item) => {
  109. item.changeSelect(false);
  110. });
  111. }
  112. activeAttrib.value = undefined;
  113. focusAM.value = am;
  114. am && asyncOper(am, (item) => item.changeSelect(true));
  115. };
  116. watchEffect((onCleanup) => {
  117. onCleanup(
  118. mergeFuns(
  119. ams.value.map((item) => {
  120. return asyncOper(item, (s) =>
  121. s.bus.on("changeSelect", (f) => {
  122. if (f) {
  123. focusAM.value = item;
  124. } else if (focusAM.value === item) {
  125. focusAM.value = undefined;
  126. }
  127. })
  128. );
  129. })
  130. )
  131. );
  132. });
  133. watch(activeAttrib, (_a, _b, onCleanup) => {
  134. if (!activeAttrib.value) return;
  135. const cur = focusAM.value![activeAttrib.value.key][activeAttrib.value.ndx];
  136. const updateFocus = () => {
  137. const rang = [cur.time, cur.time + (cur.duration || 0)];
  138. console.log(rang, cur, cur.time);
  139. if (currentTime.value < rang[0] || currentTime.value > rang[1]) {
  140. activeAttrib.value = undefined;
  141. }
  142. };
  143. follow.value = true;
  144. currentTime.value = cur.time!;
  145. nextTick(() => (follow.value = false));
  146. onCleanup(
  147. watch(
  148. () => [currentTime.value, cur.time, cur.time + (cur.duration || 0)],
  149. updateFocus
  150. )
  151. );
  152. });
  153. const add = <T extends Active["key"]>(
  154. key: T,
  155. preset: Partial<AnimationModel[T][0]> = {}
  156. ) => {
  157. const attr = getAddTLItemAttr(focusAM.value![key], currentTime.value, 10, 1);
  158. if (!attr) {
  159. Message.error("当前时间已存在其他" + title[key]);
  160. } else {
  161. const item = reactive({
  162. id: uuid(),
  163. name: title[key],
  164. ...attr,
  165. ...preset,
  166. } as any);
  167. if (key === "frames") {
  168. console.log(preset, item);
  169. asyncOper(
  170. focusAM.value!,
  171. (am3d) => (item.mat = JSON.parse(JSON.stringify(am3d.getModelPose())))
  172. );
  173. }
  174. focusAM.value![key].push(item);
  175. activeAttrib.value = {
  176. ndx: focusAM.value![key].length - 1,
  177. key,
  178. };
  179. }
  180. };
  181. let cleanSelect: (() => void) | null = null;
  182. const changeSelect = ({ select, unSelect }: Record<string, AnimationModel[]>) => {
  183. cleanSelect && cleanSelect();
  184. cleanSelect = mergeFuns(
  185. ...select.map((item) => asyncOper(item, (am) => am.changeShow(true))),
  186. ...unSelect.map((item) =>
  187. asyncOper(item, (am) => {
  188. if (item === focusAM.value) {
  189. focusAM.value = undefined;
  190. }
  191. am.changeShow(false);
  192. })
  193. )
  194. );
  195. };
  196. const deleteAm = (am: AnimationModel) => {
  197. if (am === focusAM.value) {
  198. activeAttrib.value = undefined;
  199. focusAM.value = undefined;
  200. }
  201. ams.value.splice(ams.value.indexOf(am), 1);
  202. };
  203. </script>
  204. <style lang="scss" scoped>
  205. .animation-layout {
  206. --bottom-height: 70px;
  207. &.focusAM {
  208. --bottom-height: 225px;
  209. }
  210. }
  211. .animation-left {
  212. height: calc(100vh - var(--bottom-height));
  213. position: absolute;
  214. width: var(--left-pano-width);
  215. }
  216. .animation-right {
  217. height: calc(100vh - var(--bottom-height));
  218. padding-top: 0;
  219. position: absolute;
  220. right: 0;
  221. top: 0;
  222. }
  223. </style>
  224. <style>
  225. .animation-left .left-pano {
  226. bottom: 0 !important;
  227. }
  228. </style>