feat: 通用动画效果

Re-1.0
咬轮猫 3 years ago
parent 829a99a732
commit c958367c99

@ -145,6 +145,10 @@ vue2建议使用iframe集成目前没有vue2的版本如果您接受不了
[秀英童鞋](https://blog.csdn.net/qq_42862247)对此项目前进的耐心指导
[一个低代码(可视化拖拽)教学项目](https://github.com/woai3c/visual-drag-demo)
还在坚持用原生`svg`进行组态的开发者们
## 捐助
如果这个项目对您有所帮助,请扫下方二维码打赏一杯咖啡。

@ -29,6 +29,7 @@
},
"dependencies": {
"ace-builds": "^1.14.0",
"animate.css": "^4.1.1",
"echarts": "^5.4.1",
"element-plus": "^2.2.9",
"pinia": "^2.0.16",

@ -7,6 +7,7 @@ specifiers:
'@typescript-eslint/parser': ^5.30.5
'@vitejs/plugin-vue': ^2.3.3
ace-builds: ^1.14.0
animate.css: ^4.1.1
echarts: ^5.4.1
element-plus: ^2.2.9
eslint: ^8.19.0
@ -33,6 +34,7 @@ specifiers:
dependencies:
ace-builds: 1.14.0
animate.css: 4.1.1
echarts: 5.4.1
element-plus: 2.2.9_vue@3.2.37
pinia: 2.0.17_j6bzmzd4ujpabbp5objtwxyjp4
@ -815,6 +817,10 @@ packages:
uri-js: 4.4.1
dev: true
/animate.css/4.1.1:
resolution: {integrity: sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==}
dev: false
/ansi-regex/2.1.1:
resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==}
engines: {node: '>=0.10.0'}

@ -0,0 +1,6 @@
<svg viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<path
d="M919.6 405.6l-57.2-8c-12.7-1.8-23-10.4-28-22.1-11.3-26.7-25.7-51.7-42.9-74.5-7.7-10.2-10-23.5-5.2-35.3l21.7-53.5c6.7-16.4 0.2-35.3-15.2-44.1L669.1 96.6c-15.4-8.9-34.9-5.1-45.8 8.9l-35.4 45.3c-7.9 10.2-20.7 14.9-33.5 13.3-14-1.8-28.3-2.8-42.8-2.8-14.5 0-28.8 1-42.8 2.8-12.8 1.6-25.6-3.1-33.5-13.3l-35.4-45.3c-10.9-14-30.4-17.8-45.8-8.9L230.4 168c-15.4 8.9-21.8 27.7-15.2 44.1l21.7 53.5c4.8 11.9 2.5 25.1-5.2 35.3-17.2 22.8-31.7 47.8-42.9 74.5-5 11.8-15.3 20.4-28 22.1l-57.2 8C86 408 72.9 423 72.9 440.8v142.9c0 17.7 13.1 32.7 30.6 35.2l57.2 8c12.7 1.8 23 10.4 28 22.1 11.3 26.7 25.7 51.7 42.9 74.5 7.7 10.2 10 23.5 5.2 35.3l-21.7 53.5c-6.7 16.4-0.2 35.3 15.2 44.1L354 927.8c15.4 8.9 34.9 5.1 45.8-8.9l35.4-45.3c7.9-10.2 20.7-14.9 33.5-13.3 14 1.8 28.3 2.8 42.8 2.8 14.5 0 28.8-1 42.8-2.8 12.8-1.6 25.6 3.1 33.5 13.3l35.4 45.3c10.9 14 30.4 17.8 45.8 8.9l123.7-71.4c15.4-8.9 21.8-27.7 15.2-44.1l-21.7-53.5c-4.8-11.8-2.5-25.1 5.2-35.3 17.2-22.8 31.7-47.8 42.9-74.5 5-11.8 15.3-20.4 28-22.1l57.2-8c17.6-2.5 30.6-17.5 30.6-35.2V440.8c0.2-17.8-12.9-32.8-30.5-35.2z m-408 245.5c-76.7 0-138.9-62.2-138.9-138.9s62.2-138.9 138.9-138.9 138.9 62.2 138.9 138.9-62.2 138.9-138.9 138.9z"
></path>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -70,6 +70,7 @@
item.actual_bound.x +
item.actual_bound.width / 2
)},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
:class="`${getCommonClass(item)}`"
></use>
<component
v-else-if="item.type === EDoneJsonType.CustomSvg"
@ -84,11 +85,13 @@
item.actual_bound.x +
item.actual_bound.width / 2
)},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
:class="`${getCommonClass(item)}`"
></component>
<foreignObject
v-else-if="item.type === EDoneJsonType.Vue"
v-bind="getActualBoundScale(item.actual_bound, item.scale_x, item.scale_y)"
:id="`foreign-object${item.id}`"
:class="`${getCommonClass(item)}`"
>
<component
:is="item.tag"
@ -138,7 +141,7 @@
globalStore.handle_svg_info?.info.id == item.id
? 'svg-item-select'
: ''
}`"
} ${getCommonClass(item)}`"
></rect>
<handle-panel
v-if="
@ -197,7 +200,8 @@
getSvgNowPosition,
setSvgActualInfo,
prosToVBind,
objectDeepClone
objectDeepClone,
getCommonClass
} from '@/utils';
import {
calculateBottom,
@ -871,4 +875,7 @@
margin-top: 5px;
}
}
.common-ani {
transform-box: fill-box;
}
</style>

@ -0,0 +1,105 @@
<!-- eslint-disable vue/html-indent -->
<template>
<div style="height: 100%">
<el-tag
closable
v-if="select_val"
@close="addAnimation('')"
@click="drawer_visiable = true"
style="cursor: pointer"
>{{
common_animate_list
.map((m) => m.children)
.reduce((pre, curr) => {
return pre.concat(curr);
})
.find((f) => f.value == select_val)?.label
}}
<el-icon :size="10"> <svg-analysis name="setting"></svg-analysis> </el-icon
></el-tag>
<el-tag v-else type="success" style="cursor: pointer" @click="drawer_visiable = true"
>新增</el-tag
>
<el-drawer v-model="drawer_visiable" title="选择动画" direction="ltr">
<el-tabs v-model="activeName">
<el-tab-pane
:label="tab_item.label"
:name="tab_item.label"
v-for="tab_item in common_animate_list"
:key="tab_item.label"
>
<el-scrollbar height="500px">
<div class="flex flex-wrap">
<div
class="animate"
v-for="(animate, index) in tab_item.children"
:key="index"
@mouseenter="play_index = index"
@mouseleave="play_index = null"
@click="addAnimation(animate.value)"
>
<div
:class="`${
play_index == index
? `animate__animated animate__${animate.value} animate__slow animate__infinite`
: ''
}`"
>
{{ animate.label }}
</div>
</div>
</div>
</el-scrollbar></el-tab-pane
>
</el-tabs>
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { ElTag, ElDrawer, ElTabs, ElTabPane, ElScrollbar, ElIcon } from 'element-plus';
import { ref, watch } from 'vue';
import { common_animate_list } from '@/config-center/system/index';
import SvgAnalysis from '@/components/svg-analysis/index.vue';
const props = defineProps({
val: {
type: String,
default: ''
}
});
const emits = defineEmits(['updateCommonAniVal']);
const select_val = ref(props.val);
const drawer_visiable = ref(false);
const activeName = ref('进入');
const play_index = ref<null | number>(null);
const addAnimation = (val: string) => {
emits('updateCommonAniVal', val);
drawer_visiable.value = false;
};
watch(
() => props.val,
(new_val) => {
select_val.value = new_val;
}
);
</script>
<style scoped>
.animate {
cursor: pointer;
}
.animate > div {
width: 80px;
height: 60px;
background: #f5f8fb;
display: flex;
align-items: center;
justify-content: center;
margin: 0 12px;
margin-bottom: 10px;
font-size: 12px;
color: #333;
border-radius: 3px;
user-select: none;
cursor: pointer;
}
</style>

@ -78,6 +78,46 @@
:obj-info="globalStore.handle_svg_info.info.animations"
></dynamic-el-form-item>
</el-form>
<el-form
label-width="90px"
label-position="left"
v-else-if="globalStore.handle_svg_info.info.common_animations"
>
<el-form-item label="动画效果" size="small">
<common-animate
@update-common-ani-val="
(val) => updateCommonAniVal(globalStore.handle_svg_info?.info, val)
"
:val="globalStore.handle_svg_info.info.common_animations.val"
></common-animate>
</el-form-item>
<el-form-item label="延迟" size="small">
<el-select v-model="globalStore.handle_svg_info.info.common_animations.delay">
<el-option value="delay-0s" label="无"></el-option>
<el-option value="delay-1s" label="1秒"></el-option
><el-option value="delay-2s" label="2秒"></el-option
><el-option value="delay-3s" label="3秒"></el-option
><el-option value="delay-4s" label="4秒"></el-option
><el-option value="delay-5s" label="5秒"></el-option>
</el-select>
</el-form-item>
<el-form-item label="动画速度" size="small">
<el-select v-model="globalStore.handle_svg_info.info.common_animations.speed">
<el-option value="slow" label="慢"></el-option>
<el-option value="slower" label="最慢"></el-option>
<el-option value="fast" label="快"></el-option>
<el-option value="faster" label="最快"></el-option>
</el-select>
</el-form-item>
<el-form-item label="循环次数" size="small">
<el-select v-model="globalStore.handle_svg_info.info.common_animations.repeat">
<el-option value="repeat-1" label="一次"></el-option>
<el-option value="repeat-2" label="两次"></el-option>
<el-option value="repeat-3" label="三次"></el-option>
<el-option value="infinite" label="无限次"></el-option>
</el-select>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="事件" name="event"></el-tab-pane>
</el-tabs>
@ -95,13 +135,16 @@
ElTabPane,
TabsPaneContext,
ElInput,
ElSwitch
ElSwitch,
ElSelect,
ElOption
} from 'element-plus';
import { ref } from 'vue';
import { useConfigStore } from '@/store/config';
import { useGlobalStore } from '@/store/global';
import { EGlobalStoreIntention } from '@/store/global/types';
import { EGlobalStoreIntention, IDoneJson } from '@/store/global/types';
import DynamicElFormItem from './dynamic-el-form-item.vue';
import CommonAnimate from './common-animate.vue';
const configStore = useConfigStore();
const globalStore = useGlobalStore();
@ -109,4 +152,9 @@
const handleClick = (tab: TabsPaneContext, event: Event) => {
console.log(tab, event);
};
const updateCommonAniVal = (item: IDoneJson | undefined, val: string) => {
if (item?.common_animations) {
item.common_animations.val = val;
}
};
</script>

@ -86,6 +86,7 @@
import { useImportDataModel } from '@/hooks';
import { useGlobalStore } from '@/store/global';
import { IDoneJson } from '@/store/global/types';
import 'animate.css';
const props = defineProps({
customToolBar: {
type: Object as PropType<IConfigCenter>,

@ -50,6 +50,7 @@
item.actual_bound.x +
item.actual_bound.width / 2
)},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
:class="`${getCommonClass(item)}`"
></use>
<component
v-else-if="item.type === EDoneJsonType.CustomSvg"
@ -64,11 +65,13 @@
item.actual_bound.x +
item.actual_bound.width / 2
)},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
:class="`${getCommonClass(item)}`"
></component>
<foreignObject
v-else-if="item.type === EDoneJsonType.Vue"
v-bind="getActualBoundScale(item.actual_bound, item.scale_x, item.scale_y)"
:id="`foreign-object${item.id}`"
:class="`${getCommonClass(item)}`"
>
<component
:is="item.tag"
@ -93,7 +96,7 @@
import { getCurrentInstance, PropType, reactive } from 'vue';
import { useGlobalStore } from '@/store/global';
import { EGlobalStoreIntention, EMouseInfoState } from '@/store/global/types';
import { prosToVBind, setArrItemByID } from '@/utils';
import { prosToVBind, setArrItemByID, getCommonClass } from '@/utils';
import { EDoneJsonType } from '@/config-center/types';
import ConnectionLine from '@/components/webtopo-svg-edit/components/connection-line/index.vue';
@ -101,6 +104,7 @@
import { ComponentImport } from '@/config-center';
import { IDataModel } from '../webtopo-svg-edit/types';
import 'element-plus/dist/index.css';
import 'animate.css';
// import HandlePanel from '../handle-panel/index.vue';
//
const instance = getCurrentInstance();
@ -245,4 +249,7 @@
cursor: move;
outline: 1px solid rgb(23, 222, 30);
}
.common-ani {
transform-box: fill-box;
}
</style>

@ -42,5 +42,11 @@ export const custom_svg_text: IConfigItem = {
type: EConfigItemPropsType.Color,
val: '#FFF200FF'
}
},
common_animations: {
val: '',
delay: 'delay-0s',
speed: 'slow',
repeat: 'infinite'
}
};

@ -21,5 +21,11 @@ export const reservoir_svg_file: IConfigItem = {
type: EConfigItemPropsType.Color,
val: '#000'
}
},
common_animations: {
val: '',
delay: 'delay-0s',
speed: 'slow',
repeat: 'infinite'
}
};

@ -26,5 +26,11 @@ export const circuit_breaker_svg_file: IConfigItem = {
}
}
}
},
common_animations: {
val: '',
delay: 'delay-0s',
speed: 'slow',
repeat: 'infinite'
}
};

@ -16,5 +16,11 @@ export const alternator_svg_file: IConfigItem = {
type: EConfigItemPropsType.Color,
val: '#00ff00'
}
},
common_animations: {
val: '',
delay: 'delay-0s',
speed: 'slow',
repeat: 'infinite'
}
};

@ -16,5 +16,11 @@ export const traction_transformer_svg_file: IConfigItem = {
type: EConfigItemPropsType.Color,
val: '#ff0000'
}
},
common_animations: {
val: '',
delay: 'delay-0s',
speed: 'slow',
repeat: 'infinite'
}
};

@ -109,3 +109,97 @@ export const connection_line_system: ISystemStraightLine = Object.freeze({
end: null
}
});
export const common_animate_list = [
{
label: '进入',
children: [
{ label: '渐显', value: 'fadeIn' },
{ label: '向右进入', value: 'fadeInLeft' },
{ label: '向左进入', value: 'fadeInRight' },
{ label: '向上进入', value: 'fadeInUp' },
{ label: '向下进入', value: 'fadeInDown' },
{ label: '向右长距进入', value: 'fadeInLeftBig' },
{ label: '向左长距进入', value: 'fadeInRightBig' },
{ label: '向上长距进入', value: 'fadeInUpBig' },
{ label: '向下长距进入', value: 'fadeInDownBig' },
{ label: '旋转进入', value: 'rotateIn' },
{ label: '左顺时针旋转', value: 'rotateInDownLeft' },
{ label: '右逆时针旋转', value: 'rotateInDownRight' },
{ label: '左逆时针旋转', value: 'rotateInUpLeft' },
{ label: '右逆时针旋转', value: 'rotateInUpRight' },
{ label: '弹入', value: 'bounceIn' },
{ label: '向右弹入', value: 'bounceInLeft' },
{ label: '向左弹入', value: 'bounceInRight' },
{ label: '向上弹入', value: 'bounceInUp' },
{ label: '向下弹入', value: 'bounceInDown' },
{ label: '光速从右进入', value: 'lightSpeedInRight' },
{ label: '光速从左进入', value: 'lightSpeedInLeft' },
{ label: '光速从右退出', value: 'lightSpeedOutRight' },
{ label: '光速从左退出', value: 'lightSpeedOutLeft' },
{ label: 'Y轴旋转', value: 'flip' },
{ label: '中心X轴旋转', value: 'flipInX' },
{ label: '中心Y轴旋转', value: 'flipInY' },
{ label: '左长半径旋转', value: 'rollIn' },
{ label: '由小变大进入', value: 'zoomIn' },
{ label: '左变大进入', value: 'zoomInLeft' },
{ label: '右变大进入', value: 'zoomInRight' },
{ label: '向上变大进入', value: 'zoomInUp' },
{ label: '向下变大进入', value: 'zoomInDown' },
{ label: '向右滑动展开', value: 'slideInLeft' },
{ label: '向左滑动展开', value: 'slideInRight' },
{ label: '向上滑动展开', value: 'slideInUp' },
{ label: '向下滑动展开', value: 'slideInDown' }
]
},
{
label: '强调',
children: [
{ label: '弹跳', value: 'bounce' },
{ label: '闪烁', value: 'flash' },
{ label: '放大缩小', value: 'pulse' },
{ label: '放大缩小弹簧', value: 'rubberBand' },
{ label: '左右晃动', value: 'headShake' },
{ label: '左右扇形摇摆', value: 'swing' },
{ label: '放大晃动缩小', value: 'tada' },
{ label: '扇形摇摆', value: 'wobble' },
{ label: '左右上下晃动', value: 'jello' },
{ label: 'Y轴旋转', value: 'flip' }
]
},
{
label: '退出',
children: [
{ label: '渐隐', value: 'fadeOut' },
{ label: '向左退出', value: 'fadeOutLeft' },
{ label: '向右退出', value: 'fadeOutRight' },
{ label: '向上退出', value: 'fadeOutUp' },
{ label: '向下退出', value: 'fadeOutDown' },
{ label: '向左长距退出', value: 'fadeOutLeftBig' },
{ label: '向右长距退出', value: 'fadeOutRightBig' },
{ label: '向上长距退出', value: 'fadeOutUpBig' },
{ label: '向下长距退出', value: 'fadeOutDownBig' },
{ label: '旋转退出', value: 'rotateOut' },
{ label: '左顺时针旋转', value: 'rotateOutDownLeft' },
{ label: '右逆时针旋转', value: 'rotateOutDownRight' },
{ label: '左逆时针旋转', value: 'rotateOutUpLeft' },
{ label: '右逆时针旋转', value: 'rotateOutUpRight' },
{ label: '弹出', value: 'bounceOut' },
{ label: '向左弹出', value: 'bounceOutLeft' },
{ label: '向右弹出', value: 'bounceOutRight' },
{ label: '向上弹出', value: 'bounceOutUp' },
{ label: '向下弹出', value: 'bounceOutDown' },
{ label: '中心X轴旋转', value: 'flipOutX' },
{ label: '中心Y轴旋转', value: 'flipOutY' },
{ label: '左长半径旋转', value: 'rollOut' },
{ label: '由小变大退出', value: 'zoomOut' },
{ label: '左变大退出', value: 'zoomOutLeft' },
{ label: '右变大退出', value: 'zoomOutRight' },
{ label: '向上变大退出', value: 'zoomOutUp' },
{ label: '向下变大退出', value: 'zoomOutDown' },
{ label: '向左滑动收起', value: 'slideOutLeft' },
{ label: '向右滑动收起', value: 'slideOutRight' },
{ label: '向上滑动收起', value: 'slideOutUp' },
{ label: '向下滑动收起', value: 'slideOutDown' }
]
}
];

@ -22,11 +22,18 @@ export interface IConfigItem {
props: IConfigItemProps;
type: EDoneJsonType;
config: IDoneJsonConfig;
animations?: IConfigItemProps;
common_animations?: IConfigItemPropsCommonAnimations;
animations?: IConfigItemProps; //自定义动画效果
tag?: any;
state?: IConfigItemState; //通过一个属性去控制多个属性就是有状态组件
tag_slot?: string;
}
export interface IConfigItemPropsCommonAnimations {
val: string;
delay: string;
speed: string;
repeat: string;
}
export interface IConfigItemProps {
[key: string]: {
title: string;

@ -112,5 +112,11 @@ export const custom_vue_common_table: IConfigItem = {
type: EConfigItemPropsType.Switch,
val: false
}
},
common_animations: {
val: '',
delay: 'delay-0s',
speed: 'slow',
repeat: 'infinite'
}
};

@ -48,5 +48,11 @@ export const pie_charts: IConfigItem = {
}
]
}
},
common_animations: {
val: '',
delay: 'delay-0s',
speed: 'slow',
repeat: 'infinite'
}
};

@ -45,5 +45,11 @@ export const el_button_vue: IConfigItem = {
val: false
}
},
tag_slot: '按钮'
tag_slot: '按钮',
common_animations: {
val: '',
delay: 'delay-0s',
speed: 'slow',
repeat: 'infinite'
}
};

@ -34,5 +34,11 @@ export const el_tag_vue: IConfigItem = {
val: false
}
},
tag_slot: '标签'
tag_slot: '标签',
common_animations: {
val: '',
delay: 'delay-0s',
speed: 'slow',
repeat: 'infinite'
}
};

@ -315,3 +315,9 @@ export const setArrItemByID = (id: string, key: string, val: any, json_arr: IDon
});
});
};
export const getCommonClass = (item: IDoneJson) => {
if (!item.common_animations || !item.common_animations.val) {
return ``;
}
return `common-ani animate__animated animate__${item.common_animations.val} animate__${item.common_animations.speed} animate__${item.common_animations.repeat} animate__${item.common_animations.delay}`;
};

@ -79,6 +79,13 @@
params: {}
});
}
},
{
name: '集成到平台效果预览',
title: '集成到平台效果预览',
onClick: () => {
window.open('http://svgpro.yaolm.top/');
}
}
]);
interface IButtonList {

Loading…
Cancel
Save