Alsmile 2 years ago
parent
commit
e1129412b7

+ 76 - 38
src/services/common.ts

@@ -1,18 +1,18 @@
-import { ref } from "vue";
-import { useRouter, useRoute } from "vue-router";
-import router from "@/router/index";
-import { useUser } from "@/services/user";
+import { ref } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+import router from '@/router/index';
+import { useUser } from '@/services/user';
 import {
 import {
   showNotification,
   showNotification,
   Meta2dBackData,
   Meta2dBackData,
   dealwithFormatbeforeOpen,
   dealwithFormatbeforeOpen,
   gotoAccount,
   gotoAccount,
   checkData,
   checkData,
-} from "@/services/utils";
-import { NotifyPlugin, MessagePlugin } from "tdesign-vue-next";
-import localforage from "localforage";
-import { noLoginTip, localMeta2dDataName } from "@/services/utils";
-import { readFile, upload, dataURLtoBlob } from "@/services/file";
+} from '@/services/utils';
+import { NotifyPlugin, MessagePlugin } from 'tdesign-vue-next';
+import localforage from 'localforage';
+import { noLoginTip, localMeta2dDataName } from '@/services/utils';
+import { readFile, upload, dataURLtoBlob } from '@/services/file';
 import {
 import {
   delImage,
   delImage,
   getFolders,
   getFolders,
@@ -21,8 +21,8 @@ import {
   updateFolders,
   updateFolders,
   cdn,
   cdn,
   upCdn,
   upCdn,
-} from "@/services/api";
-import { compareVersion, baseVer, upgrade } from "@/services/upgrade";
+} from '@/services/api';
+import { compareVersion, baseVer, upgrade } from '@/services/upgrade';
 
 
 const dot = ref(false);
 const dot = ref(false);
 
 
@@ -81,10 +81,10 @@ export const save = async (
   if (!(data.teams && data.owner?.id !== user.id)) {
   if (!(data.teams && data.owner?.id !== user.id)) {
     let blob: Blob;
     let blob: Blob;
     try {
     try {
-      blob = dataURLtoBlob(meta2d.toPng(10) + "");
+      blob = dataURLtoBlob(meta2d.toPng(10) + '');
     } catch (e) {
     } catch (e) {
       MessagePlugin.error(
       MessagePlugin.error(
-        "无法下载,宽度不合法,画布可能没有画笔/画布大小超出浏览器最大限制"
+        '无法下载,宽度不合法,画布可能没有画笔/画布大小超出浏览器最大限制'
       );
       );
       return;
       return;
     }
     }
@@ -115,7 +115,7 @@ export const save = async (
   } else {
   } else {
     data.component = false; // 必要值
     data.component = false; // 必要值
   }
   }
-  let collection = data.component ? "le5le2d-components" : "le5le2d";
+  let collection = data.component ? 'le5le2d-components' : 'le5le2d';
   let ret: any;
   let ret: any;
   if (!data.name) {
   if (!data.name) {
     // 文件名称
     // 文件名称
@@ -147,24 +147,24 @@ export const save = async (
   }
   }
 */
 */
   if (!data.folder) {
   if (!data.folder) {
-    data.folder = "大屏";
-    data.tags = ["大屏"];
+    data.folder = '大屏';
+    data.tags = ['大屏'];
   }
   }
   if (type === SaveType.SaveAs) {
   if (type === SaveType.SaveAs) {
     // 另存为一定走 新增 ,由于后端 未控制 userId 等属性,清空一下
     // 另存为一定走 新增 ,由于后端 未控制 userId 等属性,清空一下
     const delAttrs = [
     const delAttrs = [
-      "userId",
-      "id",
-      "shared",
-      "star",
-      "view",
-      "username",
-      "editorName",
-      "editorId",
-      "createdAt",
-      "updatedAt",
-      "recommend",
-      "team",
+      'userId',
+      'id',
+      'shared',
+      'star',
+      'view',
+      'username',
+      'editorName',
+      'editorId',
+      'createdAt',
+      'updatedAt',
+      'recommend',
+      'team',
     ];
     ];
     for (const k of delAttrs) {
     for (const k of delAttrs) {
       delete (data as any)[k];
       delete (data as any)[k];
@@ -246,18 +246,18 @@ export const save = async (
     data._id = ret._id;
     data._id = ret._id;
     (meta2d.store.data as Meta2dBackData)._id = data._id;
     (meta2d.store.data as Meta2dBackData)._id = data._id;
     router.replace({
     router.replace({
-      path: "/",
+      path: '/',
       query: {
       query: {
         id: data._id,
         id: data._id,
-        r: Date.now() + "",
-        component: data.component + "",
+        r: Date.now() + '',
+        component: data.component + '',
       },
       },
     });
     });
   }
   }
 
 
-  MessagePlugin.success("保存成功!");
+  MessagePlugin.success('保存成功!');
   // 保存成功,重新请求文件夹
   // 保存成功,重新请求文件夹
-  meta2d.emit("t-save-success", true);
+  meta2d.emit('t-save-success', true);
   // 已保存,不再是新的,无需提示保存
   // 已保存,不再是新的,无需提示保存
   // setDot(false);
   // setDot(false);
   dot.value = false;
   dot.value = false;
@@ -319,9 +319,9 @@ export const showMap = () => {
   map.value = meta2d.map?.isShow;
   map.value = meta2d.map?.isShow;
 };
 };
 
 
-export const title = "系统可能不会保存您所做的更改,是否继续?";
-export const unLogin = "未登录,系统可能不会保存您的文件,是否继续?";
-export const unsave = "当前文件未保存,是否继续?(开通vip可享受自动保存服务)";
+export const title = '系统可能不会保存您所做的更改,是否继续?';
+export const unLogin = '未登录,系统可能不会保存您的文件,是否继续?';
+export const unsave = '当前文件未保存,是否继续?(开通vip可享受自动保存服务)';
 
 
 //未登录,当前文件可能不会保存
 //未登录,当前文件可能不会保存
 //
 //
@@ -401,7 +401,45 @@ export const newfile = async (noRouter: boolean = false) => {
   // const router = useRouter();
   // const router = useRouter();
   !noRouter &&
   !noRouter &&
     router.replace({
     router.replace({
-      path: "/",
-      query: { r: Date.now() + "" },
+      path: '/',
+      query: { r: Date.now() + '' },
     });
     });
 };
 };
+
+export const getPenTree = () => {
+  const tree = [];
+  for (const item of meta2d.store.data.pens) {
+    if (item.parentId) {
+      continue;
+    }
+    const elem = calcElem(item);
+    elem && tree.push(elem);
+  }
+
+  return tree;
+};
+
+const calcElem = (node: any) => {
+  if (!node) {
+    return;
+  }
+
+  const elem: any = {
+    label: (node as any).description || node.name,
+    value: node.id,
+    locked: node.locked,
+    visible: node.visible,
+  };
+
+  if (!node.children) {
+    return elem;
+  }
+
+  elem.children = [];
+  for (const id of node.children) {
+    const child = calcElem(meta2d.store.pens[id]);
+    child && elem.children.push(child);
+  }
+
+  return elem;
+};

+ 1 - 0
src/styles/props.css

@@ -33,6 +33,7 @@
           background: none;
           background: none;
           border-color: transparent;
           border-color: transparent;
           color: var(--color-title);
           color: var(--color-title);
+          box-shadow: none;
 
 
           &:hover,
           &:hover,
           &.t-is-focused {
           &.t-is-focused {

+ 0 - 46
src/styles/tdesign.css

@@ -57,52 +57,6 @@
   }
   }
 }
 }
 
 
-.t-tree {
-  min-width: 100%;
-  width: max-content;
-
-  .t-tree__item {
-    line-height: 30px;
-    height: 30px;
-    padding-left: calc(var(--td-comp-margin-xxl) * (var(--level) + 0.4));
-
-    &:hover {
-      background-color: var(--color-background);
-    }
-
-    .t-tree__label {
-      margin-left: 0;
-    }
-
-    &.t-is-active {
-      background-color: var(--color-background);
-      .t-tree__label {
-        background: none;
-        color: var(--color-primary);
-      }
-    }
-  }
-
-  .t-tree__icon:not(:empty):hover {
-    i {
-      color: var(--color-primary-hover);
-    }
-
-    &::after {
-      background: none;
-    }
-  }
-}
-
-.t-tree__empty {
-  font-size: 12px;
-  margin-left: 16px;
-}
-
-.t-tree__item--open .t-icon {
-  color: var(--color);
-}
-
 .t-color-picker__format-mode-select .t-select,
 .t-color-picker__format-mode-select .t-select,
 .t-color-picker__format-mode-select .t-input {
 .t-color-picker__format-mode-select .t-input {
   background-color: var(--color-background-popup);
   background-color: var(--color-background-popup);

+ 309 - 0
src/views/components/AnimatesFrame.vue

@@ -0,0 +1,309 @@
+<template>
+  <div class="animates">
+    <template v-if="pen.animates.length">
+      <t-collapse
+        v-model="openedCollapses"
+        :borderless="true"
+        :expand-on-row-click="false"
+      >
+        <t-collapse-panel v-for="(item, i) in pen.animates" :value="i">
+          <template #header>
+            <t-input v-model="item.name" autoWidth class="mr-8" />
+            <t-icon
+              v-if="isPlaying"
+              name="stop-circle-1"
+              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)"
+            />
+          </template>
+          <template #headerRightContent>
+            <t-space size="small">
+              <t-icon name="edit" class="hover mr-4" />
+
+              <t-popconfirm
+                content="确认删除该动画吗"
+                placement="left"
+                @confirm="pen.animates.splice(i, 1)"
+              >
+                <t-icon name="delete" class="hover" />
+              </t-popconfirm>
+            </t-space>
+          </template>
+          <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-tree-select
+              v-model="item.nextAnimate"
+              :data="penTree"
+              filterable
+              placeholder="下个动画对象"
+            />
+          </div>
+          <div class="form-item mt-8">
+            <label>自动播放</label>
+            <t-switch class="ml-8 mt-8" size="small" v-model="item.autoPlay" />
+          </div>
+        </t-collapse-panel>
+      </t-collapse>
+      <div class="mt-8 px-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>
+</template>
+
+<script lang="ts" setup>
+import { onBeforeMount, ref, toRaw } from 'vue';
+
+import { getPenTree } from '@/services/common';
+import { deepClone } from '@meta2d/core';
+
+const { pen } = defineProps<{
+  pen: any;
+}>();
+
+const penTree: any = ref([]);
+
+const openedCollapses = ref([0]);
+
+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: [
+      {
+        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: [
+      { 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(false);
+
+onBeforeMount(() => {
+  if (!pen.animates) {
+    pen.animates = [];
+  }
+
+  const p = meta2d.findOne(pen.id);
+  isPlaying.value = !!p?.calculative?.start;
+
+  penTree.value = getPenTree();
+});
+
+const addAnimate = () => {
+  openedCollapses.value.push(pen.animates.length);
+  pen.animates.push({
+    name: '动画' + (pen.animates.length + 1),
+  });
+};
+
+const changeAnimate = (item: any) => {
+  const animate: any = animateList.find((elem: any) => {
+    return elem.label === item.animate;
+  });
+
+  if (!animate) {
+    return;
+  }
+
+  item.frames = deepClone(animate.data);
+};
+
+const play = (i: number) => {
+  meta2d.startAnimate(pen.id, i);
+  isPlaying.value = true;
+};
+
+const stop = () => {
+  meta2d.stopAnimate(pen.id);
+  isPlaying.value = false;
+};
+</script>
+<style lang="postcss" scoped>
+.animates {
+  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>

+ 4 - 9
src/views/components/ElementTree.vue

@@ -135,9 +135,11 @@
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
 import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue';
 import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue';
-import { LockState, Pen } from '@meta2d/core';
 import { MessagePlugin } from 'tdesign-vue-next';
 import { MessagePlugin } from 'tdesign-vue-next';
 
 
+import { LockState, Pen } from '@meta2d/core';
+import { getPenTree } from '@/services/common';
+
 const tree = ref<any>(null);
 const tree = ref<any>(null);
 const data = reactive<any>({
 const data = reactive<any>({
   tree: [],
   tree: [],
@@ -163,14 +165,7 @@ onBeforeMount(() => {
 });
 });
 
 
 const getTree = () => {
 const getTree = () => {
-  data.tree = [];
-  for (const item of meta2d.store.data.pens) {
-    if (item.parentId) {
-      continue;
-    }
-    const elem = calcElem(item);
-    elem && data.tree.push(elem);
-  }
+  data.tree = getPenTree();
 };
 };
 
 
 const calcElem = (node: Pen) => {
 const calcElem = (node: Pen) => {

+ 207 - 43
src/views/components/PenAnimates.vue

@@ -1,35 +1,101 @@
 <template>
 <template>
-  <div class="animates">
-    <template v-if="pen.animates.length">
+  <div class="animations">
+    <template v-if="pen.animations.length">
       <t-collapse
       <t-collapse
         v-model="openedCollapses"
         v-model="openedCollapses"
         :borderless="true"
         :borderless="true"
         :expand-on-row-click="false"
         :expand-on-row-click="false"
       >
       >
-        <t-collapse-panel v-for="(item, i) in pen.animates" :value="i">
+        <t-collapse-panel v-for="(item, i) in pen.animations" :value="i">
           <template #header>
           <template #header>
-            <input v-model="item.name" />
+            <t-input v-model="item.name" autoWidth class="mr-8" />
+            <t-icon
+              v-if="isPlaying === i"
+              name="stop-circle-1"
+              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)"
+            />
           </template>
           </template>
           <template #headerRightContent>
           <template #headerRightContent>
-            <t-popconfirm
-              content="确认删除该动画吗"
-              @confirm="pen.animates.splice(i, 1)"
-            >
-              <t-space size="small">
+            <t-space size="small">
+              <t-icon v-if="!pen.type" name="edit" class="hover mr-4" />
+
+              <t-popconfirm
+                content="确认删除该动画吗"
+                placement="left"
+                @confirm="pen.animations.splice(i, 1)"
+              >
                 <t-icon name="delete" class="hover" />
                 <t-icon name="delete" class="hover" />
-              </t-space>
-            </t-popconfirm>
+              </t-popconfirm>
+            </t-space>
+          </template>
+          <template v-if="pen.type"></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.keepanimationstate"
+                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-tree-select
+                v-model="item.nextAnimate"
+                :data="penTree"
+                filterable
+                placeholder="下个动画对象"
+              />
+            </div>
+            <div class="form-item mt-8">
+              <label>自动播放</label>
+              <t-switch
+                class="ml-8 mt-8"
+                size="small"
+                v-model="item.autoPlay"
+              />
+            </div>
           </template>
           </template>
-          <div class="form-item">
-            <label>动画类型</label>
-            <t-select
-              v-model="item.animate"
-              clearable
-              placeholder="动画"
-              :options="animateList"
-              @change="changeAnimate(item)"
-            />
-          </div>
         </t-collapse-panel>
         </t-collapse-panel>
       </t-collapse>
       </t-collapse>
       <div class="mt-8 px-16">
       <div class="mt-8 px-16">
@@ -49,59 +115,127 @@
 </template>
 </template>
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
-import { onBeforeMount, onUnmounted, reactive, ref } from 'vue';
+import { onBeforeMount, ref, toRaw } from 'vue';
+
+import { getPenTree } from '@/services/common';
+import { deepClone } from '@meta2d/core';
 
 
 const { pen } = defineProps<{
 const { pen } = defineProps<{
   pen: any;
   pen: any;
 }>();
 }>();
 
 
+const penTree: any = ref([]);
+
 const openedCollapses = ref([0]);
 const openedCollapses = ref([0]);
 
 
 const animateList = [
 const animateList = [
   {
   {
     label: '闪烁',
     label: '闪烁',
     value: '闪烁',
     value: '闪烁',
-    data: [],
+    data: [
+      {
+        visible: true,
+        duration: 100,
+      },
+      {
+        visible: false,
+        duration: 100,
+      },
+    ],
   },
   },
   {
   {
     label: '缩放',
     label: '缩放',
     value: '缩放',
     value: '缩放',
-    data: [],
+    data: [
+      {
+        scale: 1.1,
+        duration: 100,
+      },
+      {
+        scale: 1,
+        duration: 400,
+      },
+    ],
   },
   },
   {
   {
     label: '旋转',
     label: '旋转',
     value: '旋转',
     value: '旋转',
-    data: [],
+    data: [
+      {
+        rotate: 360,
+        duration: 1000,
+      },
+    ],
   },
   },
   {
   {
     label: '上下跳动',
     label: '上下跳动',
     value: '上下跳动',
     value: '上下跳动',
-    data: [],
+    data: [
+      {
+        y: -10,
+        duration: 100,
+      },
+      { y: 0, duration: 100 },
+      { y: -10, duration: 200 },
+    ],
   },
   },
   {
   {
     label: '左右跳动',
     label: '左右跳动',
     value: '左右跳动',
     value: '左右跳动',
-    data: [],
+    data: [
+      {
+        x: -10,
+        duration: 100,
+      },
+      {
+        x: 10,
+        duration: 80,
+      },
+      {
+        x: -10,
+        duration: 50,
+      },
+      {
+        x: 10,
+        duration: 30,
+      },
+      {
+        x: 0,
+        duration: 300,
+      },
+    ],
   },
   },
   {
   {
     label: '颜色变化',
     label: '颜色变化',
     value: '颜色变化',
     value: '颜色变化',
-    data: [],
+    data: [
+      { color: '#4583ff', duration: 200 },
+      { color: '#ff4000', duration: 200 },
+    ],
   },
   },
   {
   {
     label: '文字变化',
     label: '文字变化',
     value: '文字变化',
     value: '文字变化',
-    data: [],
+    data: [
+      { text: '乐吾乐', duration: 200 },
+      { text: 'le5le', duration: 200 },
+    ],
   },
   },
   {
   {
     label: '状态变化',
     label: '状态变化',
     value: '状态变化',
     value: '状态变化',
-    data: [],
+    data: [
+      { showChild: 0, duration: 200 },
+      { showChild: 1, duration: 200 },
+    ],
   },
   },
   {
   {
     label: '翻转',
     label: '翻转',
     value: '翻转',
     value: '翻转',
-    data: [],
+    data: [
+      { flipX: true, flipY: true, duration: 200 },
+      { flipX: false, flipY: false, duration: 200 },
+    ],
   },
   },
   {
   {
     label: '自定义',
     label: '自定义',
@@ -110,23 +244,53 @@ const animateList = [
   },
   },
 ];
 ];
 
 
+const isPlaying = ref(-1);
+
 onBeforeMount(() => {
 onBeforeMount(() => {
-  if (!pen.animates) {
-    pen.animates = [];
+  if (!pen.animations) {
+    pen.animations = [];
+  }
+
+  const p = meta2d.findOne(pen.id);
+  if (p?.calculative?.start) {
+    // @ts-ignore
+    isPlaying.value = p?.calculative?.playAnimate;
   }
   }
+
+  penTree.value = getPenTree();
 });
 });
 
 
 const addAnimate = () => {
 const addAnimate = () => {
-  openedCollapses.value.push(pen.animates.length);
-  pen.animates.push({
-    name: '动画' + (pen.animates.length + 1),
+  openedCollapses.value.push(pen.animations.length);
+  pen.animations.push({
+    name: '动画' + (pen.animations.length + 1),
+  });
+};
+
+const changeAnimate = (item: any) => {
+  const animate: any = animateList.find((elem: any) => {
+    return elem.label === item.animate;
   });
   });
+
+  if (!animate) {
+    return;
+  }
+
+  item.frames = deepClone(animate.data);
 };
 };
 
 
-const changeAnimate = (item: any) => {};
+const play = (i: number) => {
+  meta2d.startAnimate(pen.id, i);
+  isPlaying.value = i;
+};
+
+const stop = () => {
+  meta2d.stopAnimate(pen.id);
+  isPlaying.value = -1;
+};
 </script>
 </script>
 <style lang="postcss" scoped>
 <style lang="postcss" scoped>
-.animates {
+.animations {
   height: 100%;
   height: 100%;
 
 
   .blank {
   .blank {
@@ -139,11 +303,11 @@ const changeAnimate = (item: any) => {};
 
 
   :deep(.t-collapse) {
   :deep(.t-collapse) {
     .t-collapse-panel__header {
     .t-collapse-panel__header {
-      input {
-        outline: none;
-        border: none;
-        background: none;
-        color: var(--color);
+      .t-input {
+        border-color: transparent;
+        &:hover {
+          border-color: var(--color-border-input);
+        }
       }
       }
     }
     }