bottom.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <template>
  2. <div class="top-bar">
  3. <div class="play-bar">
  4. <ui-icon
  5. :type="play ? 'a-pause' : 'a-play'"
  6. ctrl
  7. @click="
  8. () => {
  9. play = !play;
  10. $emit('update:follow', play);
  11. }
  12. "
  13. />
  14. </div>
  15. <div class="scale-bar">
  16. <ui-icon
  17. type="reduce"
  18. class="icon"
  19. ctrl
  20. @click="scale = Math.max(0.5, scale - 0.5)"
  21. />
  22. <Slider
  23. class="slider"
  24. v-model:value="scale"
  25. :min="0.5"
  26. :max="8"
  27. :step="0.01"
  28. :tooltipOpen="false"
  29. />
  30. <ui-icon
  31. type="magnify"
  32. class="icon"
  33. ctrl
  34. @click="scale = Math.min(8, scale + 0.5)"
  35. />
  36. </div>
  37. </div>
  38. <div class="oper-bar" :class="{ disabled: play }">
  39. <Renderer v-model:scale="scale" ref="renderer">
  40. <v-group>
  41. <template v-if="am">
  42. <template v-for="prop in tlProps">
  43. <TimeLine
  44. v-if="am[prop.attr].length"
  45. :items="am[prop.attr]"
  46. :height="prop.height"
  47. :background="prop.background"
  48. :opacity="prop.opacity"
  49. :top="prop.top"
  50. :itemsRenderer="prop.component"
  51. @update="({ ndx, time }) => (am![prop.attr][ndx].time = time)"
  52. @add="
  53. (item) => {
  54. am![prop.attr].push({...item, id: onlyId()});
  55. $emit('update:active', {
  56. key: prop.attr,
  57. ndx: am![prop.attr].length - 1,
  58. });
  59. }
  60. "
  61. @del="(ndx) => am![prop.attr].splice(ndx, 1)"
  62. :active="prop.attr === active?.key ? am[prop.attr][active.ndx] : undefined"
  63. @update:active="(active: any) => $emit('update:active', active && { key: prop.attr, ndx: am![prop.attr].indexOf(active) })"
  64. />
  65. </template>
  66. <empty v-if="!count" />
  67. </template>
  68. </v-group>
  69. <v-group>
  70. <Time
  71. @update-current-time="
  72. (time) => {
  73. $emit('update:currentTime', time);
  74. }
  75. "
  76. >
  77. <TimeCurrent
  78. :currentTime="currentTime"
  79. @update:current-time="(time) => $emit('update:currentTime', time)"
  80. :follow="follow"
  81. />
  82. </Time>
  83. </v-group>
  84. </Renderer>
  85. </div>
  86. </template>
  87. <script lang="ts" setup>
  88. import { Slider } from "ant-design-vue";
  89. import { computed, ref, watch, watchEffect } from "vue";
  90. import { ams, AnimationModel } from "@/store/animation";
  91. import Renderer from "@/components/drawing/renderer.vue";
  92. import Time from "@/components/drawing-time/time.vue";
  93. import TimeCurrent from "@/components/drawing-time/current.vue";
  94. import TimeLine from "@/components/drawing-time-line/index.vue";
  95. import TimeLineFrame from "@/components/drawing-time-line/frame.vue";
  96. import TimeLineAction from "@/components/drawing-time-line/action.vue";
  97. import empty from "@/components/drawing-time-line/empty.vue";
  98. import { Active } from "./type";
  99. import { animationGroup, currentTime, endTime } from "@/sdk/association/animation";
  100. import { onlyId } from "@/components/drawing/hook";
  101. const props = defineProps<{
  102. am?: AnimationModel;
  103. active?: Active;
  104. currentTime: number;
  105. follow: boolean;
  106. }>();
  107. const emit = defineEmits<{
  108. (e: "update:active", data: Active | undefined): void;
  109. (e: "update:follow", data: boolean): void;
  110. (e: "update:currentTime", v: number): void;
  111. }>();
  112. const scale = ref(1);
  113. const tlProps = [
  114. {
  115. attr: "frames",
  116. component: TimeLineFrame,
  117. height: 30,
  118. top: 24,
  119. background: "#000",
  120. opacity: 0.5,
  121. },
  122. {
  123. attr: "actions",
  124. component: TimeLineAction,
  125. height: 30,
  126. top: 65,
  127. background: "#000",
  128. opacity: 0.5,
  129. },
  130. {
  131. attr: "subtitles",
  132. component: TimeLineAction,
  133. height: 30,
  134. top: 105,
  135. background: "#000",
  136. opacity: 0.5,
  137. },
  138. {
  139. attr: "paths",
  140. component: TimeLineAction,
  141. height: 30,
  142. top: 145,
  143. background: "#000",
  144. opacity: 0.5,
  145. },
  146. ] as const;
  147. const count = computed(() =>
  148. Object.values(tlProps).reduce((t, c) => (props.am ? t + props.am[c.attr].length : 0), 0)
  149. );
  150. const play = ref(false);
  151. watch(play, (_a, _b, onCleanup) => {
  152. play.value ? animationGroup.play() : animationGroup.pause();
  153. onCleanup(
  154. watchEffect(() => {
  155. if (currentTime.value >= endTime.value) {
  156. play.value = false;
  157. }
  158. })
  159. );
  160. // let isDes = false;
  161. // let prevNow = Date.now();
  162. // const animation = () => {
  163. // if (play.value && !isDes) {
  164. // const curNow = Date.now();
  165. // emit("update:currentTime", props.currentTime + (curNow - prevNow) * 0.001);
  166. // prevNow = curNow;
  167. // requestAnimationFrame(animation);
  168. // }
  169. // };
  170. // animation();
  171. // onCleanup(() => (isDes = true));
  172. });
  173. const renderer = ref<any>();
  174. watch(
  175. () => props.am,
  176. () => renderer.value.updateSize()
  177. );
  178. </script>
  179. <style scoped lang="scss">
  180. .top-bar {
  181. display: flex;
  182. height: 40px;
  183. align-items: center;
  184. border-bottom: 1px solid rgba(255, 255, 255, 0.16);
  185. .play-bar {
  186. flex: 1;
  187. display: flex;
  188. justify-content: center;
  189. .icon {
  190. font-size: 16px;
  191. }
  192. }
  193. .scale-bar {
  194. width: 180px;
  195. flex: 0 0 auto;
  196. display: flex;
  197. align-items: center;
  198. padding: 0 20px;
  199. position: relative;
  200. &::before {
  201. content: "";
  202. position: absolute;
  203. left: 0;
  204. width: 1px;
  205. height: 16px;
  206. background: rgba(255, 255, 255, 0.16);
  207. }
  208. .slider {
  209. flex: 1;
  210. }
  211. .icon {
  212. margin: 0 5px;
  213. font-size: 16px;
  214. }
  215. }
  216. }
  217. .oper-bar {
  218. height: calc(100% - 40px);
  219. }
  220. </style>