123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- <template>
- <div class="animations">
- <template v-if="props.pen.animations.length">
- <t-collapse
- v-model="openedCollapses"
- :borderless="true"
- :expand-on-row-click="true"
- >
- <t-collapse-panel v-for="(item, i) in props.pen.animations" :value="i">
- <template #header>
- <div class="flex middle" @click.stop>
- <t-input
- :title="item.name"
- :value="item.name"
- autoWidth
- class="mr-8"
- style="max-width: 60px"
- @change="changeAnimateName($event, item)"
- @blur="checkAnimateName(item)"
- @focus="bakAnimateName(item.name)"
- />
- <stop-circle-icon v-if="isPlaying === i"
- class="hover primary"
- style="font-size: 16px"
- @click="stop"/>
- <play-circle-icon v-else
- class="hover"
- style="font-size: 16px"
- @click="play(i)"/>
- <!-- <t-icon
- v-if="isPlaying === i"
- name="stop-circle"
- class="hover primary"
- style="font-size: 16px"
- @click="stop"
- />
- <t-icon
- v-else
- name="play-circle"
- class="hover"
- style="font-size: 16px"
- @click="play(i)"
- /> -->
- </div>
- </template>
- <template #headerRightContent>
- <t-space size="small" @click.stop>
- <edit-icon v-if="!props.pen.type && item.animate"
- class="hover mr-4"
- @click="animate = item"/>
- <!-- <t-icon
- v-if="!props.pen.type && item.animate"
- name="edit"
- class="hover mr-4"
- @click="animate = item"
- /> -->
- <t-popconfirm
- content="确认删除该动画吗"
- placement="left"
- @confirm="delAnimate(i)"
- >
- <delete-icon class="hover"/>
- <!-- <t-icon name="delete" class="hover" /> -->
- </t-popconfirm>
- </t-space>
- </template>
- <template v-if="props.pen.type">
- <div class="form-item">
- <label>动画类型</label>
- <t-select v-model="item.lineAnimateType" placeholder="水流" @change="changeValue(i)">
- <t-option :key="0" :value="0" label="水流" />
- <t-option :key="1" :value="1" label="水珠" />
- <t-option :key="2" :value="2" label="圆点" />
- <t-option v-if="['polyline','line'].includes(props.pen.lineName)" :key="3" :value="3" label="箭头" />
- <t-option v-if="['polyline','line'].includes(props.pen.lineName)" :key="4" :value="4" label="水滴" />
- </t-select>
- </div>
- <div class="form-item mt-8">
- <label>运动速度</label>
- <t-slider
- class="ml-12"
- v-model="item.animateSpan"
- :show-tooltip="true"
- :min="1"
- :max="10"
- @change="changeValue(i)"
- />
- </div>
- <div class="form-item mt-8">
- <label>动画颜色</label>
- <t-color-picker
- class="w-full"
- format="CSS"
- :enable-alpha="true"
- :recent-colors="null"
- :swatch-colors="defaultPureColor"
- :color-modes="['monochrome']"
- :show-primary-color-preview="false"
- :clearable="true"
- v-model="item.animateColor"
- @change="changeValue(i)"
- />
- </div>
- <div class="form-item mt-8">
- <label>发光效果</label>
- <t-switch
- class="ml-8 mt-8"
- size="small"
- v-model="item.animateShadow"
- @change="changeValue(i)"
- />
- </div>
- <div v-if="item.animateShadow" class="form-item mt-8">
- <label>发光颜色</label>
- <t-color-picker
- class="w-full"
- format="CSS"
- :enable-alpha="true"
- :recent-colors="null"
- :swatch-colors="defaultPureColor"
- :color-modes="['monochrome']"
- :show-primary-color-preview="false"
- :clearable="true"
- v-model="item.animateShadowColor"
- @change="changeValue(i)"
- />
- </div>
- <div v-if="item.animateShadow" class="form-item mt-8">
- <label>发光模糊</label>
- <t-input-number
- v-model="item.animateShadowBlur"
- theme="column"
- :min="1"
- placeholder="默认6"
- @change="changeValue(i)"
- />
- </div>
- <div class="form-item mt-8">
- <label>轨迹宽度</label>
- <t-input-number
- v-model="item.animateLineWidth"
- theme="column"
- :min="1"
- placeholder="默认6"
- @change="changeValue(i)"
- />
- </div>
- <div v-if="item.lineAnimateType===3||item.lineAnimateType===4" class="form-item mt-8">
- <label>轨迹间隔</label>
- <t-input-number
- v-model="item.animateInterval"
- theme="column"
- :min="1"
- placeholder="默认100"
- @change="changeValue(i)"
- />
- </div>
- <div class="form-item mt-8">
- <label>反向流动</label>
- <t-switch
- class="ml-8 mt-8"
- size="small"
- v-model="item.animateReverse"
- @change="changeValue(i)"
- />
- </div>
- <div class="form-item mt-8">
- <label>播放次数</label>
- <t-input-number
- v-model="item.animateCycle"
- theme="column"
- :min="1"
- placeholder="无限"
- title="缺省无限循环播放"
- @change="changeValue(i)"
- />
- </div>
- <div class="form-item mt-8">
- <label>自动播放</label>
- <t-switch
- class="ml-8 mt-8"
- size="small"
- v-model="item.autoPlay"
- @change="changeAnimateAutoPlay($event, item)"
- />
- </div>
- <div
- class="form-item mt-8"
- >
- <label>下个动画类型</label>
- <t-radio-group class="ml-8" v-model="item.temType" @change="item.nextAnimate = ''">
- <t-radio value="id">图元</t-radio>
- <t-radio value="tag">组</t-radio>
- </t-radio-group>
- </div>
- <div
- class="form-item mt-8"
- title="当前动画结束后自动播放下一个对象的动画"
- >
- <label>下个动画</label>
- <t-tree-select
- v-if="item.temType === 'id'"
- v-model="item.nextAnimate"
- :data="penTree"
- filterable
- placeholder="无"
- />
- <t-select
- v-else
- v-model="item.nextAnimate"
- :options="groups"
- placeholder="组"
- />
- </div>
- </template>
- <template v-else>
- <div class="form-item">
- <label>动画类型</label>
- <t-select
- v-model="item.animate"
- clearable
- placeholder="动画"
- :options="animateList"
- @change="changeAnimate(item)"
- />
- </div>
- <div class="form-item mt-8">
- <label>播放次数</label>
- <t-input-number
- v-model="item.animateCycle"
- theme="column"
- :min="1"
- placeholder="无限"
- title="缺省无限循环播放"
- />
- </div>
- <div class="form-item mt-8">
- <label>结束状态</label>
- <t-select v-model="item.keepAnimateState" placeholder="初始状态">
- <t-option :key="false" :value="false" label="初始状态" />
- <t-option :key="true" :value="true" label="当前状态" />
- </t-select>
- </div>
- <div class="form-item mt-8">
- <label>线性播放</label>
- <t-tooltip content="仅支持数字属性匀速线性播放" placement="top">
- <t-select v-model="item.linear" placeholder="是">
- <t-option :key="true" :value="true" label="是" />
- <t-option :key="false" :value="false" label="否" />
- </t-select>
- </t-tooltip>
- </div>
- <div class="form-item mt-8">
- <label>自动播放</label>
- <t-switch
- class="ml-8 mt-8"
- size="small"
- v-model="item.autoPlay"
- @change="changeAnimateAutoPlay($event, item)"
- />
- </div>
- <div
- class="form-item mt-8"
- >
- <label>下个动画类型</label>
- <t-radio-group class="ml-8" v-model="item.temType" @change="item.nextAnimate = ''">
- <t-radio value="id">图元</t-radio>
- <t-radio value="tag">组</t-radio>
- </t-radio-group>
- </div>
- <div
- class="form-item mt-8"
- title="当前动画结束后自动播放下一个对象的动画"
- >
- <label>下个动画</label>
- <t-tree-select
- v-if="item.temType === 'id'"
- v-model="item.nextAnimate"
- :data="penTree"
- filterable
- placeholder="无"
- />
- <t-select
- v-else
- v-model="item.nextAnimate"
- :options="groups"
- placeholder="组"
- />
- </div>
- </template>
- </t-collapse-panel>
- </t-collapse>
- <t-divider />
- <div class="p-16">
- <t-button class="w-full" @click="addAnimate" style="height: 30px">
- 添加动画
- </t-button>
- </div>
- </template>
- <div class="flex column center blank" v-else>
- <img src="/img/blank.png" />
- <div class="gray center">还没有动画</div>
- <div class="mt-8">
- <t-button @click="addAnimate" style="height: 30px">添加动画</t-button>
- </div>
- </div>
- </div>
- <AnimateFrames
- v-if="animate"
- :animate="animate"
- @close="animate = undefined"
- />
- </template>
- <script lang="ts" setup>
- import { onBeforeMount, ref, watch, onUnmounted,onBeforeUnmount } from 'vue';
- import { getPenTree } from '@/services/common';
- import { deepClone } from '@meta2d/core';
- import AnimateFrames from './AnimateFrames.vue';
- import { defaultPureColor } from '@/services/defaults';
- import { MessagePlugin } from 'tdesign-vue-next';
- import {StopCircleIcon, PlayCircleIcon, EditIcon, DeleteIcon} from 'tdesign-icons-vue-next';
- const props = defineProps<{
- pen: any;
- }>();
- let animateNameBak = '';
- function changeAnimateAutoPlay(value, item) {
- if (value) {
- props.pen.autoPlay = true;
- if (props.pen.animations.length === 1) {
- item.autoPlay = true;
- return;
- }
- props.pen.animations.forEach((i) => {
- if (i.name !== item.name) {
- i.autoPlay = false;
- }
- });
- item.autoPlay = true;
- } else {
- let allFalse = props.pen.animations.every((i) => !i.autoPlay);
- if (allFalse) {
- props.pen.autoPlay = false;
- props.pen.frames = null;
- }
- }
- }
- const checkAnimateName = (item:any) => {
- if(!item.name) {
- MessagePlugin.warning('动画名不能为空!');
- item.name = animateNameBak;
- }
- }
- const bakAnimateName = (name:string) => {
- animateNameBak = name;
- }
- const changeAnimateName = (event, item) => {
- let result = props.pen.animations.filter(
- (animation) => animation.name === event
- );
- if (result && result.length) {
- MessagePlugin.warning('已存在同名动画!');
- return;
- } else {
- item.name = event;
- }
- };
- const penTree: any = ref([]);
- const groups: any = ref([]);
- const openedCollapses = ref([0]);
- const animate: any = ref(undefined);
- const animateList = [
- {
- label: '闪烁',
- value: '闪烁',
- data: [
- {
- visible: true,
- duration: 100,
- },
- {
- visible: false,
- duration: 100,
- },
- ],
- },
- {
- label: '缩放',
- value: '缩放',
- data: [
- {
- scale: 1.1,
- duration: 100,
- },
- {
- scale: 1,
- duration: 400,
- },
- ],
- },
- {
- label: '旋转',
- value: '旋转',
- data: [
- {
- rotate: 360,
- duration: 1000,
- },
- ],
- },
- {
- label: '逆向旋转',
- value: '逆向旋转',
- data: [
- {
- rotate: -360,
- duration: 1000,
- },
- ],
- },
- {
- label: '上下跳动',
- value: '上下跳动',
- data: [
- {
- y: -10,
- duration: 100,
- },
- { y: 0, duration: 100 },
- { y: -10, duration: 200 },
- ],
- },
- {
- label: '左右跳动',
- value: '左右跳动',
- data: [
- {
- x: -10,
- duration: 100,
- },
- {
- x: 10,
- duration: 80,
- },
- {
- x: -10,
- duration: 50,
- },
- {
- x: 10,
- duration: 30,
- },
- {
- x: 0,
- duration: 300,
- },
- ],
- },
- {
- label: '颜色变化',
- value: '颜色变化',
- data: [
- { color: '#4583ff', duration: 200 },
- { color: '#ff4000', duration: 200 },
- ],
- },
- {
- label: '背景变化',
- value: '背景变化',
- data: [
- { background: '#4583ff', duration: 200 },
- { background: '#ff4000', duration: 200 },
- ],
- },
- {
- label: '文字变化',
- value: '文字变化',
- data: [
- { text: '乐吾乐', duration: 200 },
- { text: 'le5le', duration: 200 },
- ],
- },
- {
- label: '状态变化',
- value: '状态变化',
- data: [
- { showChild: 0, duration: 200 },
- { showChild: 1, duration: 200 },
- ],
- },
- {
- label: '翻转',
- value: '翻转',
- data: [
- { flipX: true, flipY: true, duration: 200 },
- { flipX: false, flipY: false, duration: 200 },
- ],
- },
- {
- label: '自定义',
- value: 'custom',
- data: [],
- },
- ];
- const isPlaying = ref(-1);
- onBeforeMount(() => {
- if (!props.pen.animations) {
- props.pen.animations = [];
- }
- const p = meta2d.findOne(props.pen.id);
- if (p?.calculative?.start) {
- // @ts-ignore
- isPlaying.value = p?.currentAnimation;
- }
- meta2d.on('animateEnd', cancleAnimatePlayState);
- penTree.value = getPenTree().map(i=>{
- i.label+='-'+ i.value;
- return i
- }
- );
- groups.value = [];
- const d = meta2d.store.data as any;
- if (d.groups) {
- for (const item of d.groups) {
- groups.value.push({ label: item, value: item });
- }
- }
- });
- onBeforeUnmount(() => {
- meta2d.off('animateEnd', cancleAnimatePlayState);
- })
- const cancleAnimatePlayState = (pen) => {
- if(pen.id === props.pen.id) {
- isPlaying.value = -1;
- }
- }
- const watcher = watch(
- () => props.pen.id,
- () => {
- const p = meta2d.findOne(props.pen.id);
- if (p?.calculative?.start) {
- // @ts-ignore
- isPlaying.value = p?.currentAnimation;
- } else {
- isPlaying.value = -1;
- }
- }
- );
- const addAnimate = () => {
- openedCollapses.value.push(props.pen.animations.length);
- props.pen.animations.push({
- name: '动画' + (props.pen.animations.length + 1),
- temType: 'id'
- });
- };
- const delAnimate = (i: number) => {
- if (isPlaying.value === i) {
- meta2d.stopAnimate(props.pen.id);
- isPlaying.value = undefined;
- }
- if(props.pen.animate) {
- meta2d.setValue({
- id:props.pen.id,
- animate:undefined,
- animateCycle:undefined,
- autoPlay:undefined,
- frames:[]
- })
- }
- if(props.pen.type){
- meta2d.setValue({
- id:props.pen.id,
- autoPlay:undefined,
- });
- }
- props.pen.animations.splice(i, 1);
- animate.value = undefined;
- };
- const changeAnimate = (item: any) => {
- const animate: any = animateList.find((elem: any) => {
- return elem.value === item.animate;
- });
- if (!animate) {
- return;
- }
- item.frames = deepClone(animate.data);
- };
- const play = (i: number) => {
- meta2d.startAnimate(props.pen.id, i);
- isPlaying.value = i;
- };
- const stop = () => {
- meta2d.stopAnimate(props.pen.id);
- isPlaying.value = -1;
- };
- const changeValue = (i) => {
- if(i===isPlaying.value){
- play(i);
- }
- };
- onUnmounted(() => {
- watcher();
- });
- </script>
- <style lang="postcss" scoped>
- .animations {
- height: 100%;
- .blank {
- height: 70%;
- img {
- padding: 16px;
- opacity: 0.9;
- }
- }
- :deep(.t-collapse) {
- .t-collapse-panel__header {
- .t-input {
- border-color: transparent;
- &:hover {
- border-color: var(--color-border-input);
- }
- }
- }
- .t-collapse-panel__icon:hover {
- background: none;
- svg {
- color: var(--color-primary);
- }
- }
- }
- }
- </style>
|