diff --git a/src/core/service/api/create-animation.js b/src/core/service/api/create-animation.js new file mode 100644 index 0000000000000000000000000000000000000000..49a599c0640bde01b4f7b635037a8b16b80d0f43 --- /dev/null +++ b/src/core/service/api/create-animation.js @@ -0,0 +1,77 @@ +const defaultOption = { + duration: 400, + timingFunction: 'linear', + delay: 0, + transformOrigin: '50% 50% 0' +} + +class MPAnimation { + constructor (option) { + this.actions = [] + this.currentTransform = {} + this.currentStepAnimates = [] + this.option = Object.assign({}, defaultOption, option) + } + _getOption (option) { + let _option = { + transition: Object.assign({}, this.option, option) + } + _option.transformOrigin = _option.transition.transformOrigin + delete _option.transition.transformOrigin + return _option + } + _pushAnimates (type, args) { + this.currentStepAnimates.push({ + type: type, + args: args + }) + } + _converType (type) { + return type.replace(/[A-Z]/g, text => { + return `-${text.toLowerCase()}` + }) + } + _getValue (value) { + return typeof value === 'number' ? `${value}px` : value + } + export () { + const actions = this.actions + this.actions = [] + return { + actions + } + } + step (option) { + this.currentStepAnimates.forEach(animate => { + if (animate.type !== 'style') { + this.currentTransform[animate.type] = animate + } else { + this.currentTransform[`${animate.type}.${animate.args[0]}`] = animate + } + }) + this.actions.push({ + animates: Object.values(this.currentTransform), + option: this._getOption(option) + }) + this.currentStepAnimates = [] + return this + } +} + +const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', 'translateZ'] +const animateTypes2 = ['opacity', 'backgroundColor'] +const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom'] +animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => { + MPAnimation.prototype[type] = function (...args) { + if (animateTypes2.concat(animateTypes3).includes(type)) { + this._pushAnimates('style', [this._converType(type), animateTypes3.includes(type) ? this._getValue(args[0]) : args[0]]) + } else { + this._pushAnimates(type, args) + } + return this + } +}) + +export function createAnimation (option) { + return new MPAnimation(option) +} diff --git a/src/core/view/components/index.js b/src/core/view/components/index.js index bbbd6a069bae9c1a416e2a4b4df5f7f6dc9c0e35..c275aad62836167721a33b20e95b41a16969cf42 100644 --- a/src/core/view/components/index.js +++ b/src/core/view/components/index.js @@ -1,6 +1,7 @@ import Vue from 'vue' import baseMixin from 'uni-mixins/base' +import animation from 'uni-mixins/animation' const requireComponents = [ // baseComponents @@ -17,9 +18,11 @@ requireComponents.forEach((components, index) => { componentConfig.mixins = componentConfig.mixins ? [].concat(baseMixin, componentConfig.mixins) : [baseMixin] + componentConfig.mixins.push(animation) + componentConfig.name = 'VUni' + componentConfig.name // 全局注册组件 Vue.component(componentConfig.name, componentConfig) }) -}) +}) diff --git a/src/core/view/mixins/animation.js b/src/core/view/mixins/animation.js new file mode 100644 index 0000000000000000000000000000000000000000..17a28f8a524add16bd4a6989b5a27525b81f3404 --- /dev/null +++ b/src/core/view/mixins/animation.js @@ -0,0 +1,88 @@ +function converPx (value) { + if (/\d+[ur]px$/i.test(value)) { + value.replace(/\d+[ur]px$/i, text => { + return `${uni.upx2px(parseFloat(text))}px` + }) + // eslint-disable-next-line no-useless-escape + } else if (/^-?[\d\.]+$/.test(value)) { + return `${value}px` + } + return value || '' +} + +function converType (type) { + return type.replace(/[A-Z]/g, text => { + return `-${text.toLowerCase()}` + }).replace('webkit', '-webkit') +} + +function getStyle (action) { + const animateTypes1 = ['matrix', 'matrix3d', 'scale', 'scale3d', 'rotate3d', 'skew', 'translate', 'translate3d'] + const animateTypes2 = ['scaleX', 'scaleY', 'scaleZ', 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'skewX', 'skewY', 'translateX', 'translateY', 'translateZ'] + const animateTypes3 = ['opacity', 'backgroundColor'] + const animateTypes4 = ['width', 'height', 'left', 'right', 'top', 'bottom'] + const animates = action.animates + const option = action.option + const transition = option.transition + const style = {} + let transform = [] + animates.forEach(animate => { + const type = animate.type + let args = [...animate.args] + if (animateTypes1.concat(animateTypes2).includes(type)) { + if (type.startsWith('rotate') || type.startsWith('skew')) { + args = args.map(value => parseFloat(value) + 'deg') + } else if (type.startsWith('translate')) { + args = args.map(converPx) + } + if (animateTypes2.indexOf(type)) { + args.length = 1 + } + transform.push(`${type}(${args.join(',')})`) + } else if (animateTypes3.concat(animateTypes4).includes(type)) { + const value = args[0] + style[type] = animateTypes4.includes(type) ? converPx(value) : value + } + }) + style.transform = style.webkitTransform = transform.join(' ') + style.transition = style.webkitTransition = Object.keys(style).map(type => `${converType(type)} ${transition.duration}ms ${transition.timingFunction} ${transition.delay}ms`).join(',') + style.transformOrigin = style.webkitTransformOrigin = option.transformOrigin + return style +} + +function startAnimation (context) { + const animation = context.animation + if (!animation || !animation.actions || !animation.actions.length) { + return + } + let index = 0 + const actions = animation.actions + const length = animation.actions.length + function animate () { + const action = actions[index] + const transition = action.option.transition + const style = getStyle(action) + Object.keys(style).forEach(key => { + context.$el.style[key] = style[key] + }) + + index += 1 + if (index < length) { + setTimeout(animate, transition.duration + transition.delay) + } + } + + animate() +} + +export default { + props: ['animation'], + watch: { + animation () { + startAnimation(this) + } + }, + mounted () { + startAnimation(this) + } +} diff --git a/src/platforms/h5/helpers/todo-api.js b/src/platforms/h5/helpers/todo-api.js index 1bdb9d792a0033cdfb66fc228134484c788a23fe..b2c26b7ff92ee2ee4aff7a518ce1d768b0d71d55 100644 --- a/src/platforms/h5/helpers/todo-api.js +++ b/src/platforms/h5/helpers/todo-api.js @@ -47,7 +47,6 @@ export default [ 'stopBeaconDiscovery', 'setBackgroundColor', 'setBackgroundTextStyle', - 'createAnimation', 'loadFontFace', 'getProvider', 'login',