PenAnimates.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. <template>
  2. <div class="animations">
  3. <template v-if="props.pen.animations.length">
  4. <t-collapse
  5. v-model="openedCollapses"
  6. :borderless="true"
  7. :expand-on-row-click="true"
  8. >
  9. <t-collapse-panel v-for="(item, i) in props.pen.animations" :value="i">
  10. <template #header>
  11. <div class="flex middle" @click.stop>
  12. <t-input v-model="item.name" autoWidth class="mr-8" />
  13. <t-icon
  14. v-if="isPlaying === i"
  15. name="stop-circle-1"
  16. class="hover primary"
  17. style="font-size: 16px"
  18. @click="stop"
  19. />
  20. <t-icon
  21. v-else
  22. name="play-circle"
  23. class="hover"
  24. style="font-size: 16px"
  25. @click="play(i)"
  26. />
  27. </div>
  28. </template>
  29. <template #headerRightContent>
  30. <t-space size="small" @click.stop>
  31. <t-icon
  32. v-if="!props.pen.type && item.animate"
  33. name="edit"
  34. class="hover mr-4"
  35. @click="animate = item"
  36. />
  37. <t-popconfirm
  38. content="确认删除该动画吗"
  39. placement="left"
  40. @confirm="
  41. props.pen.animations.splice(i, 1);
  42. animate = undefined;
  43. "
  44. >
  45. <t-icon name="delete" class="hover" />
  46. </t-popconfirm>
  47. </t-space>
  48. </template>
  49. <template v-if="props.pen.type">
  50. <div class="form-item">
  51. <label>动画类型</label>
  52. <t-select v-model="item.lineAnimateType" placeholder="水流">
  53. <t-option :key="0" :value="0" label="水流" />
  54. <t-option :key="1" :value="1" label="水珠" />
  55. <t-option :key="2" :value="2" label="圆点" />
  56. </t-select>
  57. </div>
  58. <div class="form-item mt-8">
  59. <label>运动速度</label>
  60. <t-slider
  61. class="ml-12"
  62. v-model="item.animateSpan"
  63. :show-tooltip="true"
  64. :min="1"
  65. :max="10"
  66. />
  67. </div>
  68. <div class="form-item mt-8">
  69. <label>动画颜色</label>
  70. <t-color-picker
  71. class="w-full"
  72. format="CSS"
  73. :enable-alpha="true"
  74. :color-modes="['monochrome']"
  75. :show-primary-color-preview="false"
  76. :clearable="true"
  77. v-model="item.animateColor"
  78. />
  79. </div>
  80. <div class="form-item mt-8">
  81. <label>轨迹宽度</label>
  82. <t-input-number
  83. v-model="item.animateLineWidth"
  84. theme="column"
  85. :min="1"
  86. placeholder="默认"
  87. />
  88. </div>
  89. <div class="form-item mt-8">
  90. <label>反向流动</label>
  91. <t-switch
  92. class="ml-8 mt-8"
  93. size="small"
  94. v-model="item.animateReverse"
  95. />
  96. </div>
  97. <div class="form-item mt-8">
  98. <label>播放次数</label>
  99. <t-input-number
  100. v-model="item.animateCycle"
  101. theme="column"
  102. :min="1"
  103. placeholder="无限"
  104. title="缺省无限循环播放"
  105. />
  106. </div>
  107. <div class="form-item mt-8">
  108. <label>自动播放</label>
  109. <t-switch
  110. class="ml-8 mt-8"
  111. size="small"
  112. v-model="item.autoPlay"
  113. />
  114. </div>
  115. <div
  116. class="form-item mt-8"
  117. title="当前动画结束后自动播放下一个对象的动画"
  118. >
  119. <label>下个动画</label>
  120. <t-tree-select
  121. v-model="item.nextAnimate"
  122. :data="penTree"
  123. filterable
  124. placeholder="无"
  125. />
  126. </div>
  127. </template>
  128. <template v-else>
  129. <div class="form-item">
  130. <label>动画类型</label>
  131. <t-select
  132. v-model="item.animate"
  133. clearable
  134. placeholder="动画"
  135. :options="animateList"
  136. @change="changeAnimate(item)"
  137. />
  138. </div>
  139. <div class="form-item mt-8">
  140. <label>播放次数</label>
  141. <t-input-number
  142. v-model="item.animateCycle"
  143. theme="column"
  144. :min="1"
  145. placeholder="无限"
  146. title="缺省无限循环播放"
  147. />
  148. </div>
  149. <div class="form-item mt-8">
  150. <label>结束状态</label>
  151. <t-select
  152. v-model="item.keepanimationstate"
  153. placeholder="初始状态"
  154. >
  155. <t-option :key="false" :value="false" label="初始状态" />
  156. <t-option :key="true" :value="true" label="当前状态" />
  157. </t-select>
  158. </div>
  159. <div class="form-item mt-8">
  160. <label>线性播放</label>
  161. <t-tooltip content="仅支持数字属性匀速线性播放" placement="top">
  162. <t-select v-model="item.linear" placeholder="是">
  163. <t-option :key="true" :value="true" label="是" />
  164. <t-option :key="false" :value="false" label="否" />
  165. </t-select>
  166. </t-tooltip>
  167. </div>
  168. <div class="form-item mt-8">
  169. <label>自动播放</label>
  170. <t-switch
  171. class="ml-8 mt-8"
  172. size="small"
  173. v-model="item.autoPlay"
  174. @change="changeAnimateAutoPlay($event,item)"
  175. />
  176. </div>
  177. <div
  178. class="form-item mt-8"
  179. title="当前动画结束后自动播放下一个对象的动画"
  180. >
  181. <label>下个动画</label>
  182. <t-tree-select
  183. v-model="item.nextAnimate"
  184. :data="penTree"
  185. filterable
  186. placeholder="无"
  187. />
  188. </div>
  189. </template>
  190. </t-collapse-panel>
  191. </t-collapse>
  192. <t-divider />
  193. <div class="p-16">
  194. <t-button class="w-full" @click="addAnimate" style="height: 30px">
  195. 添加动画
  196. </t-button>
  197. </div>
  198. </template>
  199. <div class="flex column center blank" v-else>
  200. <img src="/img/blank.png" />
  201. <div class="gray center">还没有动画</div>
  202. <div class="mt-8">
  203. <t-button @click="addAnimate" style="height: 30px">添加动画</t-button>
  204. </div>
  205. </div>
  206. </div>
  207. <AnimateFrames
  208. v-if="animate"
  209. :animate="animate"
  210. @close="animate = undefined"
  211. />
  212. </template>
  213. <script lang="ts" setup>
  214. import { onBeforeMount, ref, watch, onUnmounted } from 'vue';
  215. import { getPenTree } from '@/services/common';
  216. import { deepClone } from '@meta2d/core';
  217. import AnimateFrames from './AnimateFrames.vue';
  218. const props = defineProps<{
  219. pen: any;
  220. }>();
  221. function changeAnimateAutoPlay(value,item) {
  222. props.pen.animations.forEach(i=>{
  223. if(i.name !== item.name){
  224. i.autoPlay = false
  225. }
  226. })
  227. item.autoPlay = true
  228. }
  229. const penTree: any = ref([]);
  230. const openedCollapses = ref([0]);
  231. const animate: any = ref(undefined);
  232. const animateList = [
  233. {
  234. label: '闪烁',
  235. value: '闪烁',
  236. data: [
  237. {
  238. visible: true,
  239. duration: 100,
  240. },
  241. {
  242. visible: false,
  243. duration: 100,
  244. },
  245. ],
  246. },
  247. {
  248. label: '缩放',
  249. value: '缩放',
  250. data: [
  251. {
  252. scale: 1.1,
  253. duration: 100,
  254. },
  255. {
  256. scale: 1,
  257. duration: 400,
  258. },
  259. ],
  260. },
  261. {
  262. label: '旋转',
  263. value: '旋转',
  264. data: [
  265. {
  266. rotate: 360,
  267. duration: 1000,
  268. },
  269. ],
  270. },
  271. {
  272. label: '上下跳动',
  273. value: '上下跳动',
  274. data: [
  275. {
  276. y: -10,
  277. duration: 100,
  278. },
  279. { y: 0, duration: 100 },
  280. { y: -10, duration: 200 },
  281. ],
  282. },
  283. {
  284. label: '左右跳动',
  285. value: '左右跳动',
  286. data: [
  287. {
  288. x: -10,
  289. duration: 100,
  290. },
  291. {
  292. x: 10,
  293. duration: 80,
  294. },
  295. {
  296. x: -10,
  297. duration: 50,
  298. },
  299. {
  300. x: 10,
  301. duration: 30,
  302. },
  303. {
  304. x: 0,
  305. duration: 300,
  306. },
  307. ],
  308. },
  309. {
  310. label: '颜色变化',
  311. value: '颜色变化',
  312. data: [
  313. { color: '#4583ff', duration: 200 },
  314. { color: '#ff4000', duration: 200 },
  315. ],
  316. },
  317. {
  318. label: '背景变化',
  319. value: '背景变化',
  320. data: [
  321. { background: '#4583ff', duration: 200 },
  322. { background: '#ff4000', duration: 200 },
  323. ],
  324. },
  325. {
  326. label: '文字变化',
  327. value: '文字变化',
  328. data: [
  329. { text: '乐吾乐', duration: 200 },
  330. { text: 'le5le', duration: 200 },
  331. ],
  332. },
  333. {
  334. label: '状态变化',
  335. value: '状态变化',
  336. data: [
  337. { showChild: 0, duration: 200 },
  338. { showChild: 1, duration: 200 },
  339. ],
  340. },
  341. {
  342. label: '翻转',
  343. value: '翻转',
  344. data: [
  345. { flipX: true, flipY: true, duration: 200 },
  346. { flipX: false, flipY: false, duration: 200 },
  347. ],
  348. },
  349. {
  350. label: '自定义',
  351. value: 'custom',
  352. data: [],
  353. },
  354. ];
  355. const isPlaying = ref(-1);
  356. onBeforeMount(() => {
  357. if (!props.pen.animations) {
  358. props.pen.animations = [];
  359. }
  360. const p = meta2d.findOne(props.pen.id);
  361. if (p?.calculative?.start) {
  362. // @ts-ignore
  363. isPlaying.value = p?.currentAnimation;
  364. }
  365. penTree.value = getPenTree();
  366. });
  367. const watcher = watch(
  368. () => props.pen.id,
  369. () => {
  370. const p = meta2d.findOne(props.pen.id);
  371. if (p?.calculative?.start) {
  372. // @ts-ignore
  373. isPlaying.value = p?.currentAnimation;
  374. } else {
  375. isPlaying.value = -1;
  376. }
  377. }
  378. );
  379. const addAnimate = () => {
  380. openedCollapses.value.push(props.pen.animations.length);
  381. props.pen.animations.push({
  382. name: '动画' + (props.pen.animations.length + 1),
  383. });
  384. };
  385. const changeAnimate = (item: any) => {
  386. const animate: any = animateList.find((elem: any) => {
  387. return elem.value === item.animate;
  388. });
  389. if (!animate) {
  390. return;
  391. }
  392. item.frames = deepClone(animate.data);
  393. };
  394. const play = (i: number) => {
  395. meta2d.startAnimate(props.pen.id, i);
  396. isPlaying.value = i;
  397. };
  398. const stop = () => {
  399. meta2d.stopAnimate(props.pen.id);
  400. isPlaying.value = -1;
  401. };
  402. onUnmounted(() => {
  403. watcher();
  404. });
  405. </script>
  406. <style lang="postcss" scoped>
  407. .animations {
  408. height: 100%;
  409. .blank {
  410. height: 70%;
  411. img {
  412. padding: 16px;
  413. opacity: 0.9;
  414. }
  415. }
  416. :deep(.t-collapse) {
  417. .t-collapse-panel__header {
  418. .t-input {
  419. border-color: transparent;
  420. &:hover {
  421. border-color: var(--color-border-input);
  422. }
  423. }
  424. }
  425. .t-collapse-panel__icon:hover {
  426. background: none;
  427. svg {
  428. color: var(--color-primary);
  429. }
  430. }
  431. }
  432. }
  433. </style>