Commit 58d0048461f10dad45e5b9268b83ac191264334d
1 parent
911e0e61
feat(animation): run animations in edit mode
Showing
5 changed files
with
76 additions
and
7 deletions
front-end/h5/src/components/core/editor/edit-panel/animation.js
| @@ -3,29 +3,36 @@ import { animationOptions, animationValue2Name, firstLevelAnimationOptions } fro | @@ -3,29 +3,36 @@ import { animationOptions, animationValue2Name, firstLevelAnimationOptions } fro | ||
| 3 | 3 | ||
| 4 | export default { | 4 | export default { |
| 5 | computed: { | 5 | computed: { |
| 6 | - ...mapState('editor', ['editingElement']) | 6 | + ...mapState('editor', ['editingElement']), |
| 7 | + animationQueue () { | ||
| 8 | + return (this.editingElement && this.editingElement.animations) || [] | ||
| 9 | + } | ||
| 7 | }, | 10 | }, |
| 8 | data: () => ({ | 11 | data: () => ({ |
| 9 | - animationQueue: [], | 12 | + // animationQueue: [], |
| 10 | activeCollapsePanel: 0, | 13 | activeCollapsePanel: 0, |
| 11 | activePreviewAnimation: '', | 14 | activePreviewAnimation: '', |
| 12 | drawerVisible: false | 15 | drawerVisible: false |
| 13 | }), | 16 | }), |
| 14 | methods: { | 17 | methods: { |
| 15 | addAnimation () { | 18 | addAnimation () { |
| 19 | + // TODO move this to vuex | ||
| 16 | this.animationQueue.push({ | 20 | this.animationQueue.push({ |
| 17 | type: '', | 21 | type: '', |
| 18 | - duration: 0, | 22 | + duration: 2, |
| 19 | delay: 0, | 23 | delay: 0, |
| 20 | - countNum: 1, | 24 | + interationCount: 1, |
| 21 | infinite: false | 25 | infinite: false |
| 22 | }) | 26 | }) |
| 23 | this.activeCollapsePanel = this.animationQueue.length - 1 | 27 | this.activeCollapsePanel = this.animationQueue.length - 1 |
| 24 | }, | 28 | }, |
| 25 | deleteAnimate (index) { | 29 | deleteAnimate (index) { |
| 30 | + // TODO move this to vuex | ||
| 26 | this.animationQueue.splice(index, 1) | 31 | this.animationQueue.splice(index, 1) |
| 27 | }, | 32 | }, |
| 28 | runAnimate () { | 33 | runAnimate () { |
| 34 | + // front-end/h5/src/components/core/editor/index.js created() | ||
| 35 | + window.getEditorApp.$emit('RUN_ANIMATIONS') | ||
| 29 | }, | 36 | }, |
| 30 | renderSecondAnimationTabs (animations) { | 37 | renderSecondAnimationTabs (animations) { |
| 31 | return ( | 38 | return ( |
| @@ -50,11 +57,11 @@ export default { | @@ -50,11 +57,11 @@ export default { | ||
| 50 | // https://www.quirksmode.org/js/events_mouse.html#mouseenter | 57 | // https://www.quirksmode.org/js/events_mouse.html#mouseenter |
| 51 | <a-list-item> | 58 | <a-list-item> |
| 52 | <div | 59 | <div |
| 53 | - class={[this.activePreviewAnimation === item.value && item.value + ' animated', 'shortcut-button']} | ||
| 54 | onClick={(e) => { | 60 | onClick={(e) => { |
| 55 | // TODO move this to vuex mutation | 61 | // TODO move this to vuex mutation |
| 56 | this.editingElement.animations[this.activeCollapsePanel].type = item.value | 62 | this.editingElement.animations[this.activeCollapsePanel].type = item.value |
| 57 | }} | 63 | }} |
| 64 | + class={[this.activePreviewAnimation === item.value && item.value + ' animated', 'shortcut-button']} | ||
| 58 | onMouseenter={(e) => { | 65 | onMouseenter={(e) => { |
| 59 | this.activePreviewAnimation = item.value | 66 | this.activePreviewAnimation = item.value |
| 60 | }} | 67 | }} |
front-end/h5/src/components/core/editor/index.js
| @@ -3,6 +3,7 @@ import undoRedoHistory from '../../../store/plugins/undo-redo/History' | @@ -3,6 +3,7 @@ import undoRedoHistory from '../../../store/plugins/undo-redo/History' | ||
| 3 | import { getEditorConfigForEditingElement } from '../../../utils/element' | 3 | import { getEditorConfigForEditingElement } from '../../../utils/element' |
| 4 | 4 | ||
| 5 | import '../styles/index.scss' | 5 | import '../styles/index.scss' |
| 6 | +import 'animate.css' | ||
| 6 | 7 | ||
| 7 | import RenderEditCanvas from './canvas/edit' | 8 | import RenderEditCanvas from './canvas/edit' |
| 8 | import RenderPreviewCanvas from './canvas/preview' | 9 | import RenderPreviewCanvas from './canvas/preview' |
| @@ -296,6 +297,8 @@ export default { | @@ -296,6 +297,8 @@ export default { | ||
| 296 | ) | 297 | ) |
| 297 | }, | 298 | }, |
| 298 | created () { | 299 | created () { |
| 300 | + // event bus for editor | ||
| 301 | + window.getEditorApp = this | ||
| 299 | let workId = this.$route.params.workId | 302 | let workId = this.$route.params.workId |
| 300 | console.log(workId) | 303 | console.log(workId) |
| 301 | if (workId) { | 304 | if (workId) { |
front-end/h5/src/components/core/models/element.js
| @@ -34,6 +34,7 @@ class Element { | @@ -34,6 +34,7 @@ class Element { | ||
| 34 | this.pluginProps = (typeof ele.pluginProps === 'object' && clone(ele.pluginProps)) || this.getDefaultPluginProps(ele.editorConfig || {}) | 34 | this.pluginProps = (typeof ele.pluginProps === 'object' && clone(ele.pluginProps)) || this.getDefaultPluginProps(ele.editorConfig || {}) |
| 35 | this.commonStyle = (typeof ele.commonStyle === 'object' && clone(ele.commonStyle)) || { ...defaultStyle } | 35 | this.commonStyle = (typeof ele.commonStyle === 'object' && clone(ele.commonStyle)) || { ...defaultStyle } |
| 36 | this.events = [] | 36 | this.events = [] |
| 37 | + this.animations = [] | ||
| 37 | } | 38 | } |
| 38 | 39 | ||
| 39 | getDefaultPluginProps (editorConfig) { | 40 | getDefaultPluginProps (editorConfig) { |
| @@ -51,7 +52,7 @@ class Element { | @@ -51,7 +52,7 @@ class Element { | ||
| 51 | return pluginProps | 52 | return pluginProps |
| 52 | } | 53 | } |
| 53 | 54 | ||
| 54 | - getStyle ({ position = 'static', isRem = false }) { | 55 | + getStyle ({ position = 'static', isRem = false } = {}) { |
| 55 | const pluginProps = this.pluginProps | 56 | const pluginProps = this.pluginProps |
| 56 | const commonStyle = this.commonStyle | 57 | const commonStyle = this.commonStyle |
| 57 | let style = { | 58 | let style = { |
front-end/h5/src/components/core/support/shape.js
| 1 | +import animationMixin from '@/mixins/animation.js' | ||
| 2 | + | ||
| 1 | /** | 3 | /** |
| 2 | * #!zh: 上下左右 对应的 东南西北 | 4 | * #!zh: 上下左右 对应的 东南西北 |
| 3 | * #!en: top(north)、bottom(south)、left(west)、right(east) | 5 | * #!en: top(north)、bottom(south)、left(west)、right(east) |
| @@ -13,7 +15,8 @@ const directionKey = { | @@ -13,7 +15,8 @@ const directionKey = { | ||
| 13 | const points = ['lt', 'rt', 'lb', 'rb', 'l', 'r', 't', 'b'] | 15 | const points = ['lt', 'rt', 'lb', 'rb', 'l', 'r', 't', 'b'] |
| 14 | 16 | ||
| 15 | export default { | 17 | export default { |
| 16 | - props: ['defaultPosition', 'active', 'handleMousedownProp', 'handleElementMoveProp', 'handlePointMoveProp', 'handleElementMouseUpProp', 'handlePointMouseUpProp'], | 18 | + mixins: [animationMixin], |
| 19 | + props: ['defaultPosition', 'active', 'handleMousedownProp', 'handleElementMoveProp', 'handlePointMoveProp', 'handleElementMouseUpProp', 'handlePointMouseUpProp', 'element'], | ||
| 17 | computed: { | 20 | computed: { |
| 18 | position () { | 21 | position () { |
| 19 | return { ...this.defaultPosition } | 22 | return { ...this.defaultPosition } |
front-end/h5/src/mixins/animation.js
0 → 100644
| 1 | +// https://stackoverflow.com/questions/26874769/getcomputedstyle-and-csstext-in-ie-and-firefox | ||
| 2 | +function getComputedCSSText (style) { | ||
| 3 | + let cssText = '' | ||
| 4 | + for (let attr in style) { | ||
| 5 | + // m <?> matched | ||
| 6 | + // #!en: hump to line | ||
| 7 | + // #!zh: 驼峰转下划线 | ||
| 8 | + cssText += `${attr.replace(/[A-Z]+/g, m => `-${m.toLowerCase()}`)}:${style[attr]};` | ||
| 9 | + } | ||
| 10 | + return cssText | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +export default { | ||
| 14 | + methods: { | ||
| 15 | + async runAnimations () { | ||
| 16 | + const animationQueue = this.animations || this.element.animations || [] | ||
| 17 | + let len = animationQueue.length | ||
| 18 | + if (len === 0) return | ||
| 19 | + | ||
| 20 | + let that = this | ||
| 21 | + let parentNode = this.$el | ||
| 22 | + let animIdx = 0 | ||
| 23 | + const oldStyle = that.element.getStyle({ position: 'absolute' }) | ||
| 24 | + runAnimation() | ||
| 25 | + | ||
| 26 | + function runAnimation () { | ||
| 27 | + if (animIdx < len) { | ||
| 28 | + const animation = animationQueue[animIdx] | ||
| 29 | + let animationStyle = { | ||
| 30 | + animationName: animation.type, | ||
| 31 | + animationDuration: `${animation.duration}s`, | ||
| 32 | + animationIterationCount: animation.infinite ? 'infinite' : animation.interationCount, | ||
| 33 | + animationDelay: `${animation.delay}s`, | ||
| 34 | + animationFillMode: 'both' | ||
| 35 | + } | ||
| 36 | + parentNode.style.cssText = getComputedCSSText(animationStyle) + getComputedCSSText(oldStyle) | ||
| 37 | + animIdx++ | ||
| 38 | + } else { | ||
| 39 | + // reset to the initial state after the animation ended | ||
| 40 | + parentNode.style.cssText = getComputedCSSText(oldStyle) | ||
| 41 | + } | ||
| 42 | + } | ||
| 43 | + parentNode.addEventListener('animationend', runAnimation, false) | ||
| 44 | + } | ||
| 45 | + }, | ||
| 46 | + created () { | ||
| 47 | + const that = this | ||
| 48 | + window.getEditorApp.$on('RUN_ANIMATIONS', () => { | ||
| 49 | + if (that.active) { | ||
| 50 | + that.runAnimations() | ||
| 51 | + } | ||
| 52 | + }) | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | +} |