123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- <template>
- <ui-editor-toolbar toolbar class="animation-toolbar">
- <div class="top-bar">
- <div class="play-bar">
- <ui-icon
- :type="play ? 'a-pause' : 'a-play'"
- ctrl
- @click="
- () => {
- play = !play;
- $emit('update:follow', play);
- }
- "
- />
- </div>
- <div class="scale-bar">
- <ui-icon
- type="reduce"
- class="icon"
- ctrl
- @click="scale = Math.max(0.5, scale - 0.5)"
- />
- <Slider
- class="slider"
- v-model:value="scale"
- :min="0.5"
- :max="8"
- :step="0.01"
- :tooltipOpen="false"
- />
- <ui-icon
- type="magnify"
- class="icon"
- ctrl
- @click="scale = Math.min(8, scale + 0.5)"
- />
- </div>
- </div>
- <div class="oper-bar" :class="{ disabled: play }">
- <Renderer v-model:scale="scale" ref="renderer">
- <v-group>
- <template v-if="am">
- <template v-for="prop in tlProps">
- <TimeLine
- v-if="am[prop.attr].length"
- :items="am[prop.attr]"
- :height="prop.height"
- :background="prop.background"
- :opacity="prop.opacity"
- :top="prop.top"
- :itemsRenderer="prop.component"
- @update="({ ndx, time }) => (am![prop.attr][ndx].time = time)"
- @add="
- (item) => {
- am![prop.attr].push(item);
- $emit('update:active', {
- key: prop.attr,
- ndx: am![prop.attr].length - 1,
- });
- }
- "
- @del="(ndx) => am![prop.attr].splice(ndx, 1)"
- :active="
- prop.attr === active?.key ? am[prop.attr][active.ndx] : undefined
- "
- @update:active="(active: any) => $emit('update:active', active && { key: prop.attr, ndx: am![prop.attr].indexOf(active) })"
- />
- </template>
- <empty v-if="!count" />
- </template>
- </v-group>
- <v-group>
- <Time @update-current-time="(time) => $emit('update:currentTime', time)">
- <TimeCurrent
- :currentTime="currentTime"
- @update:current-time="(time) => $emit('update:currentTime', time)"
- :follow="follow"
- />
- </Time>
- </v-group>
- </Renderer>
- </div>
- </ui-editor-toolbar>
- </template>
- <script lang="ts" setup>
- import { Slider } from "ant-design-vue";
- import { computed, ref, watch, watchEffect } from "vue";
- import { AnimationModel } from "@/store/animation";
- import Renderer from "@/components/drawing/renderer.vue";
- import Time from "@/components/drawing-time/time.vue";
- import TimeCurrent from "@/components/drawing-time/current.vue";
- import TimeLine from "@/components/drawing-time-line/index.vue";
- import TimeLineFrame from "@/components/drawing-time-line/frame.vue";
- import TimeLineAction from "@/components/drawing-time-line/action.vue";
- import empty from "@/components/drawing-time-line/empty.vue";
- import { Active } from "./type";
- const props = defineProps<{
- am?: AnimationModel;
- active?: Active;
- currentTime: number;
- follow: boolean;
- }>();
- const emit = defineEmits<{
- (e: "update:active", data: Active | undefined): void;
- (e: "update:follow", data: boolean): void;
- (e: "update:currentTime", v: number): void;
- }>();
- const scale = ref(1);
- const tlProps = [
- {
- attr: "frames",
- component: TimeLineFrame,
- height: 30,
- top: 24,
- background: "#000",
- opacity: 0.5,
- },
- {
- attr: "actions",
- component: TimeLineAction,
- height: 30,
- top: 65,
- background: "#000",
- opacity: 0.5,
- },
- {
- attr: "subtitles",
- component: TimeLineAction,
- height: 30,
- top: 105,
- background: "#000",
- opacity: 0.5,
- },
- {
- attr: "paths",
- component: TimeLineAction,
- height: 30,
- top: 145,
- background: "#000",
- opacity: 0.5,
- },
- ] as const;
- const count = computed(() =>
- Object.values(tlProps).reduce((t, c) => (props.am ? t + props.am[c.attr].length : 0), 0)
- );
- const play = ref(false);
- watch(play, (_a, _b, onCleanup) => {
- let isDes = false;
- let prevNow = Date.now();
- const animation = () => {
- if (play.value && !isDes) {
- const curNow = Date.now();
- emit("update:currentTime", props.currentTime + (curNow - prevNow) * 0.001);
- prevNow = curNow;
- requestAnimationFrame(animation);
- }
- };
- animation();
- onCleanup(() => (isDes = true));
- });
- const renderer = ref<any>();
- watch(
- () => props.am,
- () => renderer.value.updateSize()
- );
- </script>
- <style scoped lang="scss">
- .top-bar {
- display: flex;
- height: 40px;
- align-items: center;
- border-bottom: 1px solid rgba(255, 255, 255, 0.16);
- .play-bar {
- flex: 1;
- display: flex;
- justify-content: center;
- .icon {
- font-size: 16px;
- }
- }
- .scale-bar {
- width: 180px;
- flex: 0 0 auto;
- display: flex;
- align-items: center;
- padding: 0 20px;
- position: relative;
- &::before {
- content: "";
- position: absolute;
- left: 0;
- width: 1px;
- height: 16px;
- background: rgba(255, 255, 255, 0.16);
- }
- .slider {
- flex: 1;
- }
- .icon {
- margin: 0 5px;
- font-size: 16px;
- }
- }
- }
- .oper-bar {
- height: calc(100% - 40px);
- }
- </style>
|