mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 支持元素多动画,动画启动方式,延迟,退场动画
This commit is contained in:
parent
ff03c2acd2
commit
b5383c45fe
@ -9,6 +9,7 @@ export const ANIMATIONS = [
|
|||||||
{ name: '向上弹入', value: 'bounceInUp' },
|
{ name: '向上弹入', value: 'bounceInUp' },
|
||||||
{ name: '向下弹入', value: 'bounceInDown' },
|
{ name: '向下弹入', value: 'bounceInDown' },
|
||||||
],
|
],
|
||||||
|
hiddenElement: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'fade',
|
type: 'fade',
|
||||||
@ -28,6 +29,7 @@ export const ANIMATIONS = [
|
|||||||
{ name: '从左下浮入', value: 'fadeInBottomLeft' },
|
{ name: '从左下浮入', value: 'fadeInBottomLeft' },
|
||||||
{ name: '从右下浮入', value: 'fadeInBottomRight' },
|
{ name: '从右下浮入', value: 'fadeInBottomRight' },
|
||||||
],
|
],
|
||||||
|
hiddenElement: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'rotate',
|
type: 'rotate',
|
||||||
@ -39,6 +41,7 @@ export const ANIMATIONS = [
|
|||||||
{ name: '绕左上旋转进入', value: 'rotateInUpLeft' },
|
{ name: '绕左上旋转进入', value: 'rotateInUpLeft' },
|
||||||
{ name: '绕右上旋转进入', value: 'rotateInUpRight' },
|
{ name: '绕右上旋转进入', value: 'rotateInUpRight' },
|
||||||
],
|
],
|
||||||
|
hiddenElement: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'zoom',
|
type: 'zoom',
|
||||||
@ -50,6 +53,7 @@ export const ANIMATIONS = [
|
|||||||
{ name: '从右放大进入', value: 'zoomInRight' },
|
{ name: '从右放大进入', value: 'zoomInRight' },
|
||||||
{ name: '向上放大进入', value: 'zoomInUp' },
|
{ name: '向上放大进入', value: 'zoomInUp' },
|
||||||
],
|
],
|
||||||
|
hiddenElement: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'slide',
|
type: 'slide',
|
||||||
@ -60,6 +64,7 @@ export const ANIMATIONS = [
|
|||||||
{ name: '从左滑入', value: 'slideInRight' },
|
{ name: '从左滑入', value: 'slideInRight' },
|
||||||
{ name: '向上滑入', value: 'slideInUp' },
|
{ name: '向上滑入', value: 'slideInUp' },
|
||||||
],
|
],
|
||||||
|
hiddenElement: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'flip',
|
type: 'flip',
|
||||||
@ -68,6 +73,7 @@ export const ANIMATIONS = [
|
|||||||
{ name: 'X轴翻转进入', value: 'flipInX' },
|
{ name: 'X轴翻转进入', value: 'flipInX' },
|
||||||
{ name: 'Y轴翻转进入', value: 'flipInY' },
|
{ name: 'Y轴翻转进入', value: 'flipInY' },
|
||||||
],
|
],
|
||||||
|
hiddenElement: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'back',
|
type: 'back',
|
||||||
@ -78,6 +84,7 @@ export const ANIMATIONS = [
|
|||||||
{ name: '从右放大滑入', value: 'backInRight' },
|
{ name: '从右放大滑入', value: 'backInRight' },
|
||||||
{ name: '向上放大滑入', value: 'backInUp' },
|
{ name: '向上放大滑入', value: 'backInUp' },
|
||||||
],
|
],
|
||||||
|
hiddenElement: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'lightSpeed',
|
type: 'lightSpeed',
|
||||||
@ -86,5 +93,105 @@ export const ANIMATIONS = [
|
|||||||
{ name: '从右飞入', value: 'lightSpeedInRight' },
|
{ name: '从右飞入', value: 'lightSpeedInRight' },
|
||||||
{ name: '从左飞入', value: 'lightSpeedInLeft' },
|
{ name: '从左飞入', value: 'lightSpeedInLeft' },
|
||||||
],
|
],
|
||||||
|
hiddenElement: []
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const ANIMATIONS_EXITS = [
|
||||||
|
{
|
||||||
|
type: 'bounce',
|
||||||
|
name: '弹跳',
|
||||||
|
children: [
|
||||||
|
{ name: '弹出', value: 'bounceOut' },
|
||||||
|
{ name: '向左弹出', value: 'bounceOutLeft' },
|
||||||
|
{ name: '向右弹出', value: 'bounceOutRight' },
|
||||||
|
{ name: '向上弹出', value: 'bounceOutUp' },
|
||||||
|
{ name: '向下弹出', value: 'bounceOutDown' },
|
||||||
|
],
|
||||||
|
hiddenElement: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'fade',
|
||||||
|
name: '浮现',
|
||||||
|
children: [
|
||||||
|
{ name: '浮出', value: 'fadeOut' },
|
||||||
|
{ name: '向下浮出', value: 'fadeOutDown' },
|
||||||
|
{ name: '向下长距浮出', value: 'fadeOutDownBig' },
|
||||||
|
{ name: '向左浮出', value: 'fadeOutLeft' },
|
||||||
|
{ name: '向左长距浮出', value: 'fadeOutLeftBig' },
|
||||||
|
{ name: '向右浮出', value: 'fadeOutRight' },
|
||||||
|
{ name: '向右长距浮出', value: 'fadeOutRightBig' },
|
||||||
|
{ name: '向上浮出', value: 'fadeOutUp' },
|
||||||
|
{ name: '向上长距浮出', value: 'fadeOutUpBig' },
|
||||||
|
{ name: '从左上浮出', value: 'fadeOutTopLeft' },
|
||||||
|
{ name: '从右上浮出', value: 'fadeOutTopRight' },
|
||||||
|
{ name: '从左下浮出', value: 'fadeOutBottomLeft' },
|
||||||
|
{ name: '从右下浮出', value: 'fadeOutBottomRight' },
|
||||||
|
],
|
||||||
|
hiddenElement: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'rotate',
|
||||||
|
name: '旋转',
|
||||||
|
children: [
|
||||||
|
{ name: '旋转退出', value: 'rotateOut' },
|
||||||
|
{ name: '绕左下旋转退出', value: 'rotateOutDownLeft' },
|
||||||
|
{ name: '绕右下旋转退出', value: 'rotateOutDownRight' },
|
||||||
|
{ name: '绕左上旋转退出', value: 'rotateOutUpLeft' },
|
||||||
|
{ name: '绕右上旋转退出', value: 'rotateOutUpRight' },
|
||||||
|
],
|
||||||
|
hiddenElement: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'zoom',
|
||||||
|
name: '缩放',
|
||||||
|
children: [
|
||||||
|
{ name: '缩小退出', value: 'zoomOut' },
|
||||||
|
{ name: '向下缩小退出', value: 'zoomOutDown' },
|
||||||
|
{ name: '从左缩小退出', value: 'zoomOutLeft' },
|
||||||
|
{ name: '从右缩小退出', value: 'zoomOutRight' },
|
||||||
|
{ name: '向上缩小退出', value: 'zoomOutUp' },
|
||||||
|
],
|
||||||
|
hiddenElement: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'slide',
|
||||||
|
name: '滑出',
|
||||||
|
children: [
|
||||||
|
{ name: '向下滑出', value: 'slideOutDown' },
|
||||||
|
{ name: '从左滑出', value: 'slideOutLeft' },
|
||||||
|
{ name: '从右滑出', value: 'slideOutRight' },
|
||||||
|
{ name: '向上滑出', value: 'slideOutUp' },
|
||||||
|
],
|
||||||
|
hiddenElement: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'flip',
|
||||||
|
name: '翻转',
|
||||||
|
children: [
|
||||||
|
{ name: 'X轴翻转退出', value: 'flipOutX' },
|
||||||
|
{ name: 'Y轴翻转退出', value: 'flipOutY' },
|
||||||
|
],
|
||||||
|
hiddenElement: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'back',
|
||||||
|
name: '缩小滑出',
|
||||||
|
children: [
|
||||||
|
{ name: '向下缩小滑出', value: 'backOutDown' },
|
||||||
|
{ name: '从左缩小滑出', value: 'backOutLeft' },
|
||||||
|
{ name: '从右缩小滑出', value: 'backOutRight' },
|
||||||
|
{ name: '向上缩小滑出', value: 'backOutUp' },
|
||||||
|
],
|
||||||
|
hiddenElement: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'lightSpeed',
|
||||||
|
name: '飞出',
|
||||||
|
children: [
|
||||||
|
{ name: '从右飞出', value: 'lightSpeedOutRight' },
|
||||||
|
{ name: '从左飞出', value: 'lightSpeedOutLeft' },
|
||||||
|
],
|
||||||
|
hiddenElement: []
|
||||||
},
|
},
|
||||||
]
|
]
|
@ -58,8 +58,10 @@ export default () => {
|
|||||||
element.id = elIdMap[element.id]
|
element.id = elIdMap[element.id]
|
||||||
if (element.groupId) element.groupId = groupIdMap[element.groupId]
|
if (element.groupId) element.groupId = groupIdMap[element.groupId]
|
||||||
}
|
}
|
||||||
|
// 动画id替换
|
||||||
if (slide.animations) {
|
if (slide.animations) {
|
||||||
for (const animation of slide.animations) {
|
for (const animation of slide.animations) {
|
||||||
|
animation.id = nanoid(10)
|
||||||
animation.elId = elIdMap[animation.elId]
|
animation.elId = elIdMap[animation.elId]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import {
|
|||||||
Drawer,
|
Drawer,
|
||||||
Spin,
|
Spin,
|
||||||
Alert,
|
Alert,
|
||||||
|
Tabs,
|
||||||
} from 'ant-design-vue'
|
} from 'ant-design-vue'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
@ -62,6 +63,8 @@ app.component('Checkbox', Checkbox)
|
|||||||
app.component('Drawer', Drawer)
|
app.component('Drawer', Drawer)
|
||||||
app.component('Spin', Spin)
|
app.component('Spin', Spin)
|
||||||
app.component('Alert', Alert)
|
app.component('Alert', Alert)
|
||||||
|
app.component('Tabs', Tabs)
|
||||||
|
app.component('TabPane', Tabs.TabPane)
|
||||||
|
|
||||||
app.use(Icon)
|
app.use(Icon)
|
||||||
app.use(Component)
|
app.use(Component)
|
||||||
|
@ -568,16 +568,28 @@ export type PPTElement = PPTTextElement | PPTImageElement | PPTShapeElement | PP
|
|||||||
/**
|
/**
|
||||||
* 元素动画
|
* 元素动画
|
||||||
*
|
*
|
||||||
|
* id: 动画id
|
||||||
|
*
|
||||||
* elId: 元素ID
|
* elId: 元素ID
|
||||||
*
|
*
|
||||||
* type: 动画类型
|
* type: 动画类型
|
||||||
*
|
*
|
||||||
|
* effect: 动画效果
|
||||||
|
*
|
||||||
* duration: 动画持续时间
|
* duration: 动画持续时间
|
||||||
|
*
|
||||||
|
* delay: 动画延迟时间
|
||||||
|
*
|
||||||
|
* implement: 动画启动方式(0->单击、1->与上一个一起、2->在上一个之后)
|
||||||
*/
|
*/
|
||||||
export interface PPTAnimation {
|
export interface PPTAnimation {
|
||||||
|
id: string;
|
||||||
elId: string;
|
elId: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
effect: string;
|
||||||
duration: number;
|
duration: number;
|
||||||
|
delay: number;
|
||||||
|
implement: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,6 +4,8 @@ export const enum EmitterEvents {
|
|||||||
RICH_TEXT_COMMAND = 'RICH_TEXT_COMMAND',
|
RICH_TEXT_COMMAND = 'RICH_TEXT_COMMAND',
|
||||||
OPEN_CHART_DATA_EDITOR = 'OPEN_CHART_DATA_EDITOR',
|
OPEN_CHART_DATA_EDITOR = 'OPEN_CHART_DATA_EDITOR',
|
||||||
OPEN_LATEX_EDITOR = 'OPEN_LATEX_EDITOR',
|
OPEN_LATEX_EDITOR = 'OPEN_LATEX_EDITOR',
|
||||||
|
RUN_ANIMATION = 'RUN_ANIMATION',
|
||||||
|
END_RUN_ANIMATION = 'END_RUN_ANIMATION',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RichTextAction {
|
export interface RichTextAction {
|
||||||
@ -20,6 +22,8 @@ type Events = {
|
|||||||
[EmitterEvents.RICH_TEXT_COMMAND]: RichTextCommand;
|
[EmitterEvents.RICH_TEXT_COMMAND]: RichTextCommand;
|
||||||
[EmitterEvents.OPEN_CHART_DATA_EDITOR]: void;
|
[EmitterEvents.OPEN_CHART_DATA_EDITOR]: void;
|
||||||
[EmitterEvents.OPEN_LATEX_EDITOR]: void;
|
[EmitterEvents.OPEN_LATEX_EDITOR]: void;
|
||||||
|
[EmitterEvents.RUN_ANIMATION]: void;
|
||||||
|
[EmitterEvents.END_RUN_ANIMATION]: void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emitter: Emitter<Events> = mitt<Events>()
|
const emitter: Emitter<Events> = mitt<Events>()
|
||||||
|
@ -4,44 +4,77 @@
|
|||||||
<Popover
|
<Popover
|
||||||
trigger="click"
|
trigger="click"
|
||||||
v-model:visible="animationPoolVisible"
|
v-model:visible="animationPoolVisible"
|
||||||
v-if="!['chart', 'video'].includes(handleElement.type)"
|
|
||||||
@visibleChange="visible => handlePopoverVisibleChange(visible)"
|
@visibleChange="visible => handlePopoverVisibleChange(visible)"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="animation-pool">
|
<Tabs v-model:activeKey="tabsActiveKey" tab-position="left" type="card">
|
||||||
<div class="pool-type" v-for="type in animations" :key="type.name">
|
<TabPane key="animation_in" tab="进场动效">
|
||||||
<div class="type-title">{{type.name}}:</div>
|
<div class="animation-pool">
|
||||||
<div class="pool-item-wrapper">
|
<div class="pool-type" :key="type.name" v-for="type in animations">
|
||||||
<div
|
<div v-if="!type.hiddenElement.includes(handleElement.type)">
|
||||||
class="pool-item"
|
<div class="type-title">{{type.name}}:</div>
|
||||||
v-for="item in type.children" :key="item.name"
|
<div class="pool-item-wrapper">
|
||||||
@mouseenter="hoverPreviewAnimation = item.value"
|
<div
|
||||||
@mouseleave="hoverPreviewAnimation = ''"
|
class="pool-item"
|
||||||
@click="addAnimation(item.value)"
|
v-for="item in type.children" :key="item.name"
|
||||||
>
|
@mouseenter="hoverPreviewAnimation = item.value"
|
||||||
<div
|
@mouseleave="hoverPreviewAnimation = ''"
|
||||||
class="animation-box"
|
@click="addAnimation(item.value, 'in')"
|
||||||
:class="[
|
>
|
||||||
'animate__animated',
|
<div
|
||||||
'animate__faster',
|
class="animation-box"
|
||||||
hoverPreviewAnimation === item.value && `animate__${item.value}`,
|
:class="[
|
||||||
]"
|
'animate__animated',
|
||||||
>
|
'animate__faster',
|
||||||
{{item.name}}
|
hoverPreviewAnimation === item.value && `animate__${item.value}`,
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{item.name}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mask" v-if="!popoverMaskHide"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</TabPane>
|
||||||
<div class="mask" v-if="!popoverMaskHide"></div>
|
<TabPane key="animation_out" tab="退场动效">
|
||||||
</div>
|
<div class="animation-pool">
|
||||||
|
<div class="pool-type" :key="type.name" v-for="type in animationsExits">
|
||||||
|
<div v-if="!type.hiddenElement.includes(handleElement.type)">
|
||||||
|
<div class="type-title">{{type.name}}:</div>
|
||||||
|
<div class="pool-item-wrapper">
|
||||||
|
<div
|
||||||
|
class="pool-item"
|
||||||
|
v-for="item in type.children" :key="item.name"
|
||||||
|
|
||||||
|
@mouseenter="onAnimationOut(item)"
|
||||||
|
@mouseleave="hoverPreviewAnimation = ''"
|
||||||
|
@click="addAnimation(item.value, 'out')"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="animation-box"
|
||||||
|
:class="[
|
||||||
|
'animate__animated',
|
||||||
|
'animate__faster',
|
||||||
|
hoverPreviewAnimation === item.value && `animate__${item.value}`,
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{item.name}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mask" v-if="!popoverMaskHide"></div>
|
||||||
|
</div>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
</template>
|
</template>
|
||||||
<Button class="element-animation-btn">
|
<Button class="element-animation-btn" @click="handleAnimationId = ''">
|
||||||
<IconEffects style="margin-right: 5px;" /> {{handleElementAnimationName || '点击选择动画'}}
|
<IconEffects style="margin-right: 5px;" /> 添加动画
|
||||||
</Button>
|
</Button>
|
||||||
</Popover>
|
</Popover>
|
||||||
<Button class="element-animation-btn" v-else disabled>
|
|
||||||
<IconEffects style="margin-right: 5px;" /> 该元素暂不支持动画
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tip" v-else><IconClick style="margin-right: 5px;" /> 选中画布中的元素添加动画</div>
|
<div class="tip" v-else><IconClick style="margin-right: 5px;" /> 选中画布中的元素添加动画</div>
|
||||||
@ -59,50 +92,89 @@
|
|||||||
>
|
>
|
||||||
<template #item="{ element, index }">
|
<template #item="{ element, index }">
|
||||||
<div class="sequence-item" :class="{ 'active': handleElement?.id === element.elId }">
|
<div class="sequence-item" :class="{ 'active': handleElement?.id === element.elId }">
|
||||||
<div class="index">{{index + 1}}</div>
|
<div class="sequence-content">
|
||||||
<div class="text">【{{element.elType}}】{{element.animationType}}</div>
|
<div class="index">{{index + 1}}</div>
|
||||||
<div class="handler">
|
<div class="text">【{{element.elType}}】{{element.animationType}}</div>
|
||||||
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="预览">
|
<div class="handler">
|
||||||
<IconPlayOne class="handler-btn" @click="runAnimation(element.elId, element.type, element.duration)" />
|
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="预览">
|
||||||
</Tooltip>
|
<IconPlayOne class="handler-btn" @click="runAnimation(element.elId, element.type, element.duration, element.delay)" />
|
||||||
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="删除">
|
</Tooltip>
|
||||||
<IconCloseSmall class="handler-btn" @click="deleteAnimation(element.elId)" />
|
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="删除">
|
||||||
</Tooltip>
|
<IconCloseSmall class="handler-btn" @click="deleteAnimation(element.id)" />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="configs" v-if="handleElementAnimation.length && handleElementAnimation[0].elId === element.elId">
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<div class="duration">
|
||||||
|
<div style="flex: 3 1 0%;">动画时长</div>
|
||||||
|
<InputNumber
|
||||||
|
:min="100"
|
||||||
|
:max="5000"
|
||||||
|
:step="100"
|
||||||
|
:value="element.duration"
|
||||||
|
@change="value => updateElementAnimationDuration(element, value)"
|
||||||
|
style="flex: 4 1 20%;"
|
||||||
|
/>
|
||||||
|
<div class="duration-r"> 毫秒</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="duration">
|
||||||
|
<div style="flex: 3;">动画启动方式</div>
|
||||||
|
<Select
|
||||||
|
v-model:value="element.implement"
|
||||||
|
@change="value => updateElementAnimationImplement(element, value)"
|
||||||
|
style="flex: 4;"
|
||||||
|
>
|
||||||
|
<SelectOption :value="0">单击时</SelectOption>
|
||||||
|
<SelectOption :value="1">与上一个一起</SelectOption>
|
||||||
|
<SelectOption :value="2">在上一个之后</SelectOption>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="duration" v-if="element.implement !== 1">
|
||||||
|
<div style="flex: 1;">延迟</div>
|
||||||
|
<InputNumber
|
||||||
|
:min="0"
|
||||||
|
:max="5000"
|
||||||
|
:step="100"
|
||||||
|
:value="element.delay"
|
||||||
|
@change="value => updateElementAnimationDelay(element, value)"
|
||||||
|
style="flex: 2;"
|
||||||
|
/>
|
||||||
|
<div class="duration-r">毫秒启动效果</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="duration duration-btn">
|
||||||
|
<Button @click="handlePopoverVisibleChange(true), animationPoolVisible = true, handleAnimationId = element.id">更换特效</Button>
|
||||||
|
<Button @click="updateElementAnimationAll(element)">应用到本页所有</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
|
||||||
<div class="configs" v-if="handleElementAnimation">
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<div class="duration">
|
|
||||||
<div style="flex: 4;">持续时间(毫秒):</div>
|
|
||||||
<InputNumber
|
|
||||||
:min="100"
|
|
||||||
:max="5000"
|
|
||||||
:step="100"
|
|
||||||
:value="handleElementAnimation.duration"
|
|
||||||
@change="value => updateElementAnimationDuration(value)"
|
|
||||||
style="flex: 3;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, ref } from 'vue'
|
import { computed, defineComponent, ref, watch } from 'vue'
|
||||||
|
import { nanoid } from 'nanoid'
|
||||||
|
import emitter, { EmitterEvents } from '@/utils/emitter'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useMainStore, useSlidesStore } from '@/store'
|
import { useMainStore, useSlidesStore } from '@/store'
|
||||||
import { PPTAnimation } from '@/types/slides'
|
import { PPTAnimation } from '@/types/slides'
|
||||||
import { ANIMATIONS } from '@/configs/animation'
|
import { ANIMATIONS, ANIMATIONS_EXITS } from '@/configs/animation'
|
||||||
import { ELEMENT_TYPE_ZH } from '@/configs/element'
|
import { ELEMENT_TYPE_ZH } from '@/configs/element'
|
||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
|
||||||
import Draggable from 'vuedraggable'
|
import Draggable from 'vuedraggable'
|
||||||
|
|
||||||
const defaultDuration = 1000
|
const defaultDuration = 1000
|
||||||
|
const defaultDelay = 0
|
||||||
|
const defaultImplement = 0 // (0->单击、1->与上一个一起、2->在上一个之后)
|
||||||
|
|
||||||
const animationTypes: { [key: string]: string } = {}
|
const animationTypes: { [key: string]: string } = {}
|
||||||
for (const type of ANIMATIONS) {
|
for (const type of ANIMATIONS) {
|
||||||
@ -110,6 +182,11 @@ for (const type of ANIMATIONS) {
|
|||||||
animationTypes[animation.value] = animation.name
|
animationTypes[animation.value] = animation.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const type of ANIMATIONS_EXITS) {
|
||||||
|
for (const animation of type.children) {
|
||||||
|
animationTypes[animation.value] = animation.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'element-animation-panel',
|
name: 'element-animation-panel',
|
||||||
@ -121,12 +198,18 @@ export default defineComponent({
|
|||||||
const { handleElement, handleElementId } = storeToRefs(useMainStore())
|
const { handleElement, handleElementId } = storeToRefs(useMainStore())
|
||||||
const { currentSlide, currentSlideAnimations } = storeToRefs(slidesStore)
|
const { currentSlide, currentSlideAnimations } = storeToRefs(slidesStore)
|
||||||
|
|
||||||
|
watch(() => handleElementId.value, () => {
|
||||||
|
animationPoolVisible.value = false
|
||||||
|
})
|
||||||
|
|
||||||
const hoverPreviewAnimation = ref('')
|
const hoverPreviewAnimation = ref('')
|
||||||
|
const tabsActiveKey = ref('animation_in')
|
||||||
const animationPoolVisible = ref(false)
|
const animationPoolVisible = ref(false)
|
||||||
|
|
||||||
const { addHistorySnapshot } = useHistorySnapshot()
|
const { addHistorySnapshot } = useHistorySnapshot()
|
||||||
|
|
||||||
const animations = ANIMATIONS
|
const animations = ANIMATIONS
|
||||||
|
const animationsExits = ANIMATIONS_EXITS
|
||||||
|
|
||||||
// 当前页面的动画列表
|
// 当前页面的动画列表
|
||||||
const animationSequence = computed(() => {
|
const animationSequence = computed(() => {
|
||||||
@ -137,7 +220,6 @@ export default defineComponent({
|
|||||||
if (!el) continue
|
if (!el) continue
|
||||||
const elType = ELEMENT_TYPE_ZH[el.type]
|
const elType = ELEMENT_TYPE_ZH[el.type]
|
||||||
const animationType = animationTypes[animation.type]
|
const animationType = animationTypes[animation.type]
|
||||||
|
|
||||||
animationSequence.push({
|
animationSequence.push({
|
||||||
...animation,
|
...animation,
|
||||||
elType,
|
elType,
|
||||||
@ -150,19 +232,13 @@ export default defineComponent({
|
|||||||
// 当前选中元素的入场动画信息
|
// 当前选中元素的入场动画信息
|
||||||
const handleElementAnimation = computed(() => {
|
const handleElementAnimation = computed(() => {
|
||||||
const animations = currentSlideAnimations.value || []
|
const animations = currentSlideAnimations.value || []
|
||||||
const animation = animations.find(item => item.elId === handleElementId.value)
|
const animation = animations.filter(item => item.elId === handleElementId.value)
|
||||||
return animation || null
|
return animation || []
|
||||||
})
|
|
||||||
|
|
||||||
// 当前选中元素的入场动画名称
|
|
||||||
const handleElementAnimationName = computed(() => {
|
|
||||||
if (!handleElementAnimation.value) return null
|
|
||||||
return animationTypes[handleElementAnimation.value.type]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 删除元素入场动画
|
// 删除元素入场动画
|
||||||
const deleteAnimation = (elId: string) => {
|
const deleteAnimation = (id: string) => {
|
||||||
const animations = (currentSlideAnimations.value as PPTAnimation[]).filter(item => item.elId !== elId)
|
const animations = (currentSlideAnimations.value as PPTAnimation[]).filter(item => item.id !== id)
|
||||||
slidesStore.updateSlide({ animations })
|
slidesStore.updateSlide({ animations })
|
||||||
addHistorySnapshot()
|
addHistorySnapshot()
|
||||||
}
|
}
|
||||||
@ -182,28 +258,89 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 执行入场动画预览
|
// 执行入场动画预览
|
||||||
const runAnimation = (elId: string, animationType: string, duration: number) => {
|
const runAnimation = (elId: string, animationType: string, duration: number, delay: number) => {
|
||||||
const prefix = 'animate__'
|
const prefix = 'animate__'
|
||||||
const elRef = document.querySelector(`#editable-element-${elId} [class^=editable-element-]`)
|
const elRef = document.querySelector(`#editable-element-${elId} [class^=editable-element-]`)
|
||||||
if (elRef) {
|
if (elRef) {
|
||||||
const animationName = `${prefix}${animationType}`
|
setTimeout(() => {
|
||||||
document.documentElement.style.setProperty('--animate-duration', `${duration}ms`)
|
const animationName = `${prefix}${animationType}`
|
||||||
elRef.classList.add(`${prefix}animated`, animationName)
|
document.documentElement.style.setProperty('--animate-duration', `${duration}ms`)
|
||||||
|
elRef.classList.add(`${prefix}animated`, animationName)
|
||||||
|
|
||||||
const handleAnimationEnd = () => {
|
emitter.emit(EmitterEvents.RUN_ANIMATION)
|
||||||
document.documentElement.style.removeProperty('--animate-duration')
|
|
||||||
elRef.classList.remove(`${prefix}animated`, animationName)
|
const handleAnimationEnd = () => {
|
||||||
}
|
document.documentElement.style.removeProperty('--animate-duration')
|
||||||
elRef.addEventListener('animationend', handleAnimationEnd, { once: true })
|
elRef.classList.remove(`${prefix}animated`, animationName)
|
||||||
|
|
||||||
|
emitter.emit(EmitterEvents.END_RUN_ANIMATION)
|
||||||
|
}
|
||||||
|
elRef.addEventListener('animationend', handleAnimationEnd, { once: true })
|
||||||
|
}, delay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改元素入场动画,并执行一次预览
|
// 修改元素入场动画持续时间
|
||||||
const updateElementAnimation = (type: string) => {
|
const updateElementAnimationDuration = (element: { id: string }, duration: number) => {
|
||||||
|
if (!currentSlideAnimations.value) return
|
||||||
|
if (duration < 100 || duration > 5000) return
|
||||||
|
|
||||||
|
const animations = currentSlideAnimations.value.map(item => {
|
||||||
|
if (item.id === element.id) return { ...item, duration }
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
slidesStore.updateSlide({ animations })
|
||||||
|
addHistorySnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改启动方式
|
||||||
|
const updateElementAnimationImplement = (element: { id: string }, implement: number) => {
|
||||||
if (!currentSlideAnimations.value) return
|
if (!currentSlideAnimations.value) return
|
||||||
|
|
||||||
const animations = currentSlideAnimations.value.map(item => {
|
const animations = currentSlideAnimations.value.map(item => {
|
||||||
if (item.elId === handleElementId.value) return { ...item, type }
|
if (item.id === element.id) return { ...item, implement }
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
slidesStore.updateSlide({ animations })
|
||||||
|
addHistorySnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改延迟执行时间
|
||||||
|
const updateElementAnimationDelay = (element: { id: string }, delay: number) => {
|
||||||
|
if (!currentSlideAnimations.value) return
|
||||||
|
if (delay < 0 || delay > 5000) return
|
||||||
|
|
||||||
|
const animations = currentSlideAnimations.value.map(item => {
|
||||||
|
if (item.id === element.id) return { ...item, delay }
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
slidesStore.updateSlide({ animations })
|
||||||
|
addHistorySnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用本页全部动画
|
||||||
|
const updateElementAnimationAll = (element: { duration: number; delay: number; implement: number }) => {
|
||||||
|
if (!handleElementAnimation.value) return
|
||||||
|
const animations: PPTAnimation[] = currentSlideAnimations.value ? JSON.parse(JSON.stringify(currentSlideAnimations.value)) : []
|
||||||
|
|
||||||
|
const handleElementAnimationMap = {
|
||||||
|
duration: element.duration,
|
||||||
|
delay: element.delay,
|
||||||
|
implement: element.implement
|
||||||
|
}
|
||||||
|
const animationsUpdate: PPTAnimation[] = animations.map(x => Object.assign({}, x, handleElementAnimationMap))
|
||||||
|
slidesStore.updateSlide({ animations: animationsUpdate })
|
||||||
|
addHistorySnapshot()
|
||||||
|
|
||||||
|
message.success({ content: '已应用本页其他动画', duration: 3 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改元素入场动画,并执行一次预览
|
||||||
|
const updateElementAnimation = (type: string, effect: string) => {
|
||||||
|
if (!currentSlideAnimations.value) return
|
||||||
|
|
||||||
|
const animations = currentSlideAnimations.value.map(item => {
|
||||||
|
if (item.id === handleAnimationId.value) return { ...item, type, effect }
|
||||||
return item
|
return item
|
||||||
})
|
})
|
||||||
slidesStore.updateSlide({ animations })
|
slidesStore.updateSlide({ animations })
|
||||||
@ -212,40 +349,34 @@ export default defineComponent({
|
|||||||
|
|
||||||
const animationItem = currentSlideAnimations.value.find(item => item.elId === handleElementId.value)
|
const animationItem = currentSlideAnimations.value.find(item => item.elId === handleElementId.value)
|
||||||
const duration = animationItem?.duration || defaultDuration
|
const duration = animationItem?.duration || defaultDuration
|
||||||
|
const delay = animationItem?.delay || defaultDelay
|
||||||
|
|
||||||
runAnimation(handleElementId.value, type, duration)
|
runAnimation(handleElementId.value, type, duration, delay)
|
||||||
}
|
|
||||||
|
|
||||||
// 修改元素入场动画持续时间
|
|
||||||
const updateElementAnimationDuration = (duration: number) => {
|
|
||||||
if (!currentSlideAnimations.value) return
|
|
||||||
if (duration < 100 || duration > 5000) return
|
|
||||||
|
|
||||||
const animations = currentSlideAnimations.value.map(item => {
|
|
||||||
if (item.elId === handleElementId.value) return { ...item, duration }
|
|
||||||
return item
|
|
||||||
})
|
|
||||||
slidesStore.updateSlide({ animations })
|
|
||||||
addHistorySnapshot()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleAnimationId = ref('')
|
||||||
// 添加元素入场动画,并执行一次预览
|
// 添加元素入场动画,并执行一次预览
|
||||||
const addAnimation = (type: string) => {
|
const addAnimation = (type: string, effect: string) => {
|
||||||
if (handleElementAnimationName.value) {
|
if (handleAnimationId.value) {
|
||||||
updateElementAnimation(type)
|
updateElementAnimation(type, effect)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const animations: PPTAnimation[] = currentSlideAnimations.value ? JSON.parse(JSON.stringify(currentSlideAnimations.value)) : []
|
const animations: PPTAnimation[] = currentSlideAnimations.value ? JSON.parse(JSON.stringify(currentSlideAnimations.value)) : []
|
||||||
animations.push({
|
animations.push({
|
||||||
|
id: nanoid(10),
|
||||||
elId: handleElementId.value,
|
elId: handleElementId.value,
|
||||||
type,
|
type,
|
||||||
|
effect,
|
||||||
duration: defaultDuration,
|
duration: defaultDuration,
|
||||||
|
delay: defaultDelay,
|
||||||
|
implement: defaultImplement,
|
||||||
})
|
})
|
||||||
slidesStore.updateSlide({ animations })
|
slidesStore.updateSlide({ animations })
|
||||||
animationPoolVisible.value = false
|
animationPoolVisible.value = false
|
||||||
addHistorySnapshot()
|
addHistorySnapshot()
|
||||||
|
|
||||||
runAnimation(handleElementId.value, type, defaultDuration)
|
runAnimation(handleElementId.value, type, defaultDuration, defaultDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动画选择面板打开500ms后再移除遮罩层,否则打开面板后迅速滑入鼠标预览会导致抖动
|
// 动画选择面板打开500ms后再移除遮罩层,否则打开面板后迅速滑入鼠标预览会导致抖动
|
||||||
@ -257,21 +388,41 @@ export default defineComponent({
|
|||||||
else popoverMaskHide.value = false
|
else popoverMaskHide.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const animationOutItem = ref({})
|
||||||
|
const animationOutTimer = ref(0)
|
||||||
|
const onAnimationOut = (item: { value: string }) => {
|
||||||
|
if (item.value === animationOutItem.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clearTimeout(animationOutTimer.value)
|
||||||
|
animationOutItem.value = item
|
||||||
|
hoverPreviewAnimation.value = item.value
|
||||||
|
animationOutTimer.value = window.setTimeout(() => {
|
||||||
|
hoverPreviewAnimation.value = ''
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
handleAnimationId,
|
||||||
handleElement,
|
handleElement,
|
||||||
animationPoolVisible,
|
animationPoolVisible,
|
||||||
animations,
|
animations,
|
||||||
|
animationsExits,
|
||||||
animationSequence,
|
animationSequence,
|
||||||
hoverPreviewAnimation,
|
hoverPreviewAnimation,
|
||||||
handleElementAnimation,
|
handleElementAnimation,
|
||||||
handleElementAnimationName,
|
|
||||||
popoverMaskHide,
|
popoverMaskHide,
|
||||||
addAnimation,
|
addAnimation,
|
||||||
deleteAnimation,
|
deleteAnimation,
|
||||||
handleDragEnd,
|
handleDragEnd,
|
||||||
runAnimation,
|
runAnimation,
|
||||||
updateElementAnimationDuration,
|
updateElementAnimationDuration,
|
||||||
|
updateElementAnimationImplement,
|
||||||
|
updateElementAnimationDelay,
|
||||||
handlePopoverVisibleChange,
|
handlePopoverVisibleChange,
|
||||||
|
updateElementAnimationAll,
|
||||||
|
tabsActiveKey,
|
||||||
|
onAnimationOut,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -292,9 +443,19 @@ export default defineComponent({
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.duration {
|
.duration {
|
||||||
|
margin-top: 5px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
word-break: keep-all;
|
||||||
|
.duration-r {
|
||||||
|
flex: 2;
|
||||||
|
text-indent: 8px;
|
||||||
|
}
|
||||||
|
&.duration-btn {
|
||||||
|
margin-top: 20px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.tip {
|
.tip {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
@ -310,7 +471,6 @@ export default defineComponent({
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-right: -12px;
|
margin-right: -12px;
|
||||||
padding-right: 12px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.mask {
|
.mask {
|
||||||
@ -353,13 +513,12 @@ export default defineComponent({
|
|||||||
@include overflow-overlay();
|
@include overflow-overlay();
|
||||||
}
|
}
|
||||||
.sequence-item {
|
.sequence-item {
|
||||||
height: 36px;
|
// height: 35px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
border: 1px solid $borderColor;
|
border: 1px solid $borderColor;
|
||||||
padding: 6px;
|
padding: 9px 6px;
|
||||||
border-radius: $borderRadius;
|
border-radius: $borderRadius;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
transition: all .5s;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
@ -368,22 +527,28 @@ export default defineComponent({
|
|||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
border-color: $themeColor;
|
border-color: $themeColor;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.index {
|
.sequence-content {
|
||||||
flex: 1;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
.text {
|
// height: 23px;
|
||||||
flex: 6;
|
.index {
|
||||||
}
|
flex: 1;
|
||||||
.handler {
|
}
|
||||||
flex: 2;
|
.text {
|
||||||
font-size: 15px;
|
flex: 6;
|
||||||
text-align: right;
|
}
|
||||||
}
|
.handler {
|
||||||
.handler-btn {
|
flex: 2;
|
||||||
margin-left: 8px;
|
font-size: 15px;
|
||||||
cursor: pointer;
|
text-align: right;
|
||||||
|
}
|
||||||
|
.handler-btn {
|
||||||
|
margin-left: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -81,8 +81,41 @@ export default defineComponent({
|
|||||||
const needWaitAnimation = computed(() => {
|
const needWaitAnimation = computed(() => {
|
||||||
const animations = currentSlide.value.animations || []
|
const animations = currentSlide.value.animations || []
|
||||||
const elementIndexInAnimation = animations.findIndex(animation => animation.elId === props.elementInfo.id)
|
const elementIndexInAnimation = animations.findIndex(animation => animation.elId === props.elementInfo.id)
|
||||||
if (elementIndexInAnimation !== -1 && elementIndexInAnimation >= props.animationIndex) return true
|
if (elementIndexInAnimation === -1) {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首先判断是否 effect 为 in 的元素(多动画的第一条)
|
||||||
|
const firstAnimation = animations.find(animation => animation.elId === props.elementInfo.id)
|
||||||
|
if (!firstAnimation) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先判断 animationIndex 到哪步 判断动画最后一条如果不是
|
||||||
|
const findAnimationsList = animations.filter((animation, index) => index < props.animationIndex && animation.elId === props.elementInfo.id)
|
||||||
|
if (findAnimationsList.length) {
|
||||||
|
const lastAnimation = findAnimationsList[findAnimationsList.length - 1]
|
||||||
|
const effect = lastAnimation.effect || 'in' // VERSION 2022/5/7 兼容旧数据: effect 为空则为进场动画
|
||||||
|
if (effect === 'out') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elementIndexInAnimation >= props.animationIndex) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后再判断effect
|
||||||
|
const firstEffectAnimation = animations.find(animation => animation.elId === props.elementInfo.id)
|
||||||
|
if (firstEffectAnimation) {
|
||||||
|
const effect = firstEffectAnimation.effect || 'in' // VERSION 2022/5/7 兼容旧数据: effect 为空则为进场动画
|
||||||
|
if (effect === 'out') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
// 打开元素绑定的超链接
|
// 打开元素绑定的超链接
|
||||||
|
@ -14,31 +14,80 @@ export default () => {
|
|||||||
const animations = computed(() => currentSlide.value.animations || [])
|
const animations = computed(() => currentSlide.value.animations || [])
|
||||||
const animationIndex = ref(0)
|
const animationIndex = ref(0)
|
||||||
|
|
||||||
|
const awaitRef = ref<number | null>(-1)
|
||||||
|
const awaitTimer = (delay: number) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
awaitRef.value = window.setTimeout(() => {
|
||||||
|
awaitRef.value = null
|
||||||
|
resolve(delay)
|
||||||
|
}, delay)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 执行元素的入场动画
|
// 执行元素的入场动画
|
||||||
const runAnimation = () => {
|
const runAnimation = async () => {
|
||||||
const prefix = 'animate__'
|
const prefix = 'animate__'
|
||||||
const animation = animations.value[animationIndex.value]
|
const animation = animations.value[animationIndex.value]
|
||||||
animationIndex.value += 1
|
|
||||||
|
|
||||||
const elRef = document.querySelector(`#screen-element-${animation.elId} [class^=base-element-]`)
|
const elRef = document.querySelector(`#screen-element-${animation.elId} [class^=base-element-]`)
|
||||||
if (elRef) {
|
if (elRef) {
|
||||||
|
if (awaitRef.value) {
|
||||||
|
clearTimeout(awaitRef.value)
|
||||||
|
awaitRef.value = null
|
||||||
|
}
|
||||||
|
else if (animation.implement !== 1) {
|
||||||
|
// 判断执行动画 与上一动画在一起则不延迟
|
||||||
|
await awaitTimer(animation.delay || 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line require-atomic-updates
|
||||||
|
animationIndex.value += 1
|
||||||
|
|
||||||
const animationName = `${prefix}${animation.type}`
|
const animationName = `${prefix}${animation.type}`
|
||||||
document.documentElement.style.setProperty('--animate-duration', `${animation.duration}ms`)
|
document.documentElement.style.setProperty('--animate-duration', `${animation.duration}ms`)
|
||||||
elRef.classList.add(`${prefix}animated`, animationName)
|
|
||||||
|
// 判断如果存在非进场动画保留,就去除原动画
|
||||||
|
elRef.classList.remove(`${prefix}animated`)
|
||||||
|
for (let i = 0; i < elRef.classList.length; i++) {
|
||||||
|
if (elRef.classList[i].indexOf(prefix) > -1) {
|
||||||
|
elRef.classList.remove(elRef.classList[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elRef.classList.add(animationName, `${prefix}animated`)
|
||||||
|
|
||||||
const handleAnimationEnd = () => {
|
const handleAnimationEnd = () => {
|
||||||
document.documentElement.style.removeProperty('--animate-duration')
|
document.documentElement.style.removeProperty('--animate-duration')
|
||||||
elRef.classList.remove(`${prefix}animated`, animationName)
|
if (animation.effect === 'in') { // 如果是进场动画就去除动画
|
||||||
|
elRef.classList.remove(`${prefix}animated`)
|
||||||
|
elRef.classList.remove(animationName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elRef.addEventListener('animationend', handleAnimationEnd, { once: true })
|
elRef.addEventListener('animationend', handleAnimationEnd, { once: true })
|
||||||
|
|
||||||
|
// 判断下个动画
|
||||||
|
if (animations.value.length && animationIndex.value < animations.value.length) {
|
||||||
|
const nextAnimation = animations.value[animationIndex.value]
|
||||||
|
|
||||||
|
if (nextAnimation.implement === 1) { // 一起
|
||||||
|
runAnimation()
|
||||||
|
}
|
||||||
|
else if (nextAnimation.implement === 2) { // 之后
|
||||||
|
await awaitTimer(animation.duration || 0)
|
||||||
|
runAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
animationIndex.value += 1
|
||||||
|
runAnimation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭自动播放
|
// 关闭自动播放
|
||||||
const autoPlayTimer = ref(0)
|
const autoPlayTimer = ref<number>(0)
|
||||||
const closeAutoPlay = () => {
|
const closeAutoPlay = () => {
|
||||||
if (autoPlayTimer.value) {
|
if (autoPlayTimer.value) {
|
||||||
clearInterval(autoPlayTimer.value)
|
window.clearInterval(autoPlayTimer.value)
|
||||||
autoPlayTimer.value = 0
|
autoPlayTimer.value = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,6 +103,21 @@ export default () => {
|
|||||||
const execPrev = () => {
|
const execPrev = () => {
|
||||||
if (animations.value.length && animationIndex.value > 0) {
|
if (animations.value.length && animationIndex.value > 0) {
|
||||||
animationIndex.value -= 1
|
animationIndex.value -= 1
|
||||||
|
|
||||||
|
// 判断当前动画是否保留动画效果,有则去除后再上翻
|
||||||
|
const prefix = 'animate__'
|
||||||
|
const animation = animations.value[animationIndex.value]
|
||||||
|
if (animation) {
|
||||||
|
const elRef = document.querySelector(`#screen-element-${animation.elId} [class^=base-element-]`)
|
||||||
|
if (elRef) {
|
||||||
|
elRef.classList.remove(`${prefix}animated`)
|
||||||
|
for (let i = 0; i < elRef.classList.length; i++) {
|
||||||
|
if (elRef.classList[i].indexOf(prefix) > -1) {
|
||||||
|
elRef.classList.remove(elRef.classList[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (slideIndex.value > 0) {
|
else if (slideIndex.value > 0) {
|
||||||
slidesStore.updateSlideIndex(slideIndex.value - 1)
|
slidesStore.updateSlideIndex(slideIndex.value - 1)
|
||||||
@ -82,7 +146,7 @@ export default () => {
|
|||||||
const autoPlay = () => {
|
const autoPlay = () => {
|
||||||
closeAutoPlay()
|
closeAutoPlay()
|
||||||
message.success('开始自动放映')
|
message.success('开始自动放映')
|
||||||
autoPlayTimer.value = setInterval(execNext, 2500)
|
autoPlayTimer.value = window.setInterval(execNext, 2500)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 鼠标滚动翻页
|
// 鼠标滚动翻页
|
||||||
|
Loading…
x
Reference in New Issue
Block a user