6 changed files with 104 additions and 1322 deletions
@ -1,373 +0,0 @@ |
|||
<template> |
|||
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> |
|||
</template> |
|||
|
|||
<script> |
|||
// #ifndef MP-WEIXIN |
|||
import { createAnimation } from './createAnimation' |
|||
// #endif |
|||
// #ifdef MP-WEIXIN |
|||
const createAnimation = require('./createAnimation.js').createAnimation |
|||
// #endif |
|||
|
|||
/** |
|||
* Transition 过渡动画 |
|||
* @description 简单过渡动画组件 |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=985 |
|||
* @property {Boolean} show = [false|true] 控制组件显示或隐藏 |
|||
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型 |
|||
* @value fade 渐隐渐出过渡 |
|||
* @value slide-top 由上至下过渡 |
|||
* @value slide-right 由右至左过渡 |
|||
* @value slide-bottom 由下至上过渡 |
|||
* @value slide-left 由左至右过渡 |
|||
* @value zoom-in 由小到大过渡 |
|||
* @value zoom-out 由大到小过渡 |
|||
* @property {Number} duration 过渡动画持续时间 |
|||
* @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red` |
|||
*/ |
|||
export default { |
|||
name: 'uniTransition', |
|||
emits:['click','change'], |
|||
props: { |
|||
show: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
modeClass: { |
|||
type: [Array, String], |
|||
default() { |
|||
return 'fade' |
|||
} |
|||
}, |
|||
duration: { |
|||
type: Number, |
|||
default: 300 |
|||
}, |
|||
styles: { |
|||
type: Object, |
|||
default() { |
|||
return {} |
|||
} |
|||
}, |
|||
customClass:{ |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
isShow: false, |
|||
transform: '', |
|||
opacity: 1, |
|||
animationData: {}, |
|||
durationTime: 300, |
|||
config: {} |
|||
} |
|||
}, |
|||
watch: { |
|||
show: { |
|||
handler(newVal) { |
|||
if (newVal) { |
|||
this.open() |
|||
} else { |
|||
// 避免上来就执行 close,导致动画错乱 |
|||
if (this.isShow) { |
|||
this.close() |
|||
} |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
computed: { |
|||
// 生成样式数据 |
|||
stylesObject() { |
|||
let styles = { |
|||
...this.styles, |
|||
'transition-duration': this.duration / 1000 + 's' |
|||
} |
|||
let transform = '' |
|||
for (let i in styles) { |
|||
let line = this.toLine(i) |
|||
transform += line + ':' + styles[i] + ';' |
|||
} |
|||
return transform |
|||
}, |
|||
// 初始化动画条件 |
|||
transformStyles() { |
|||
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject |
|||
} |
|||
}, |
|||
created() { |
|||
// 动画默认配置 |
|||
this.config = { |
|||
duration: this.duration, |
|||
timingFunction: 'ease', |
|||
transformOrigin: '50% 50%', |
|||
delay: 0 |
|||
} |
|||
this.durationTime = this.duration |
|||
}, |
|||
methods: { |
|||
/** |
|||
* ref 触发 初始化动画 |
|||
*/ |
|||
init(obj = {}) { |
|||
if (obj.duration) { |
|||
this.durationTime = obj.duration |
|||
} |
|||
this.animation = createAnimation(Object.assign(this.config, obj),this) |
|||
}, |
|||
/** |
|||
* 点击组件触发回调 |
|||
*/ |
|||
onClick() { |
|||
this.$emit('click', { |
|||
detail: this.isShow |
|||
}) |
|||
}, |
|||
/** |
|||
* ref 触发 动画分组 |
|||
* @param {Object} obj |
|||
*/ |
|||
step(obj, config = {}) { |
|||
<<<<<<< HEAD |
|||
if (!this.animation) return this |
|||
======= |
|||
if (!this.animation || typeof this.animation.step !== 'function') return this |
|||
>>>>>>> dev_pig |
|||
for (let i in obj) { |
|||
try { |
|||
if (this.animation && typeof this.animation[i] === 'function') { |
|||
if(typeof obj[i] === 'object'){ |
|||
this.animation[i](...obj[i]) |
|||
}else{ |
|||
this.animation[i](obj[i]) |
|||
} |
|||
} |
|||
} catch (e) { |
|||
console.error(`方法 ${i} 不存在:`, e) |
|||
} |
|||
} |
|||
try { |
|||
if (this.animation && typeof this.animation.step === 'function') { |
|||
this.animation.step(config) |
|||
} |
|||
} catch (e) { |
|||
console.error('动画step执行错误:', e) |
|||
} |
|||
return this |
|||
}, |
|||
/** |
|||
* ref 触发 执行动画 |
|||
*/ |
|||
run(fn) { |
|||
<<<<<<< HEAD |
|||
if (!this.animation) return |
|||
try { |
|||
if (this.animation && typeof this.animation.run === 'function') { |
|||
this.animation.run(fn) |
|||
} |
|||
} catch (e) { |
|||
console.error('动画执行错误:', e) |
|||
} |
|||
======= |
|||
if (!this.animation || typeof this.animation.run !== 'function') return |
|||
this.animation.run(fn) |
|||
>>>>>>> dev_pig |
|||
}, |
|||
// 开始过度动画 |
|||
open() { |
|||
clearTimeout(this.timer) |
|||
this.transform = '' |
|||
this.isShow = true |
|||
let { opacity, transform } = this.styleInit(false) |
|||
if (typeof opacity !== 'undefined') { |
|||
this.opacity = opacity |
|||
} |
|||
this.transform = transform |
|||
// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常 |
|||
this.$nextTick(() => { |
|||
// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器 |
|||
this.timer = setTimeout(() => { |
|||
try { |
|||
this.animation = createAnimation(this.config, this) |
|||
<<<<<<< HEAD |
|||
if (this.animation) { |
|||
let result = this.tranfromInit(false) |
|||
if (result && typeof result.step === 'function') { |
|||
result.step() |
|||
if (typeof result.run === 'function') { |
|||
result.run() |
|||
} |
|||
} |
|||
} |
|||
} catch (e) { |
|||
console.error('动画初始化错误:', e) |
|||
======= |
|||
if (this.animation && typeof this.animation.step === 'function' && typeof this.animation.run === 'function') { |
|||
const result = this.tranfromInit(false) |
|||
if (result && typeof result.step === 'function') { |
|||
result.step() |
|||
if (typeof this.animation.run === 'function') { |
|||
this.animation.run() |
|||
} |
|||
} |
|||
} |
|||
} catch (error) { |
|||
console.error('uni-transition animation error:', error) |
|||
>>>>>>> dev_pig |
|||
} |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
}, 20) |
|||
}) |
|||
}, |
|||
// 关闭过度动画 |
|||
close(type) { |
|||
<<<<<<< HEAD |
|||
if (!this.animation) return |
|||
try { |
|||
let result = this.tranfromInit(true) |
|||
======= |
|||
if (!this.animation || typeof this.animation.step !== 'function' || typeof this.animation.run !== 'function') return |
|||
try { |
|||
const result = this.tranfromInit(true) |
|||
>>>>>>> dev_pig |
|||
if (result && typeof result.step === 'function') { |
|||
result.step() |
|||
if (typeof result.run === 'function') { |
|||
result.run(() => { |
|||
this.isShow = false |
|||
this.animationData = null |
|||
this.animation = null |
|||
let { opacity, transform } = this.styleInit(false) |
|||
this.opacity = opacity || 1 |
|||
this.transform = transform |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
}) |
|||
} |
|||
} |
|||
<<<<<<< HEAD |
|||
} catch (e) { |
|||
console.error('动画关闭错误:', e) |
|||
======= |
|||
} catch (error) { |
|||
console.error('uni-transition close animation error:', error) |
|||
// 发生错误时直接关闭 |
|||
this.isShow = false |
|||
this.animationData = null |
|||
this.animation = null |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
>>>>>>> dev_pig |
|||
} |
|||
}, |
|||
// 处理动画开始前的默认样式 |
|||
styleInit(type) { |
|||
let styles = { |
|||
transform: '' |
|||
} |
|||
let buildStyle = (type, mode) => { |
|||
if (mode === 'fade') { |
|||
styles.opacity = this.animationType(type)[mode] |
|||
} else { |
|||
styles.transform += this.animationType(type)[mode] + ' ' |
|||
} |
|||
} |
|||
if (typeof this.modeClass === 'string') { |
|||
buildStyle(type, this.modeClass) |
|||
} else { |
|||
this.modeClass.forEach(mode => { |
|||
buildStyle(type, mode) |
|||
}) |
|||
} |
|||
return styles |
|||
}, |
|||
// 处理内置组合动画 |
|||
tranfromInit(type) { |
|||
<<<<<<< HEAD |
|||
if (!this.animation) return this |
|||
======= |
|||
if (!this.animation) { |
|||
console.warn('uni-transition: animation object is not initialized') |
|||
return this.animation |
|||
} |
|||
>>>>>>> dev_pig |
|||
let buildTranfrom = (type, mode) => { |
|||
let aniNum = null |
|||
if (mode === 'fade') { |
|||
aniNum = type ? 0 : 1 |
|||
} else { |
|||
aniNum = type ? '-100%' : '0' |
|||
if (mode === 'zoom-in') { |
|||
aniNum = type ? 0.8 : 1 |
|||
} |
|||
if (mode === 'zoom-out') { |
|||
aniNum = type ? 1.2 : 1 |
|||
} |
|||
if (mode === 'slide-right') { |
|||
aniNum = type ? '100%' : '0' |
|||
} |
|||
if (mode === 'slide-bottom') { |
|||
aniNum = type ? '100%' : '0' |
|||
} |
|||
} |
|||
<<<<<<< HEAD |
|||
let methodName = this.animationMode()[mode] |
|||
if (this.animation && typeof this.animation[methodName] === 'function') { |
|||
this.animation[methodName](aniNum) |
|||
======= |
|||
const animationMethod = this.animationMode()[mode] |
|||
if (this.animation && typeof this.animation[animationMethod] === 'function') { |
|||
this.animation[animationMethod](aniNum) |
|||
>>>>>>> dev_pig |
|||
} |
|||
} |
|||
if (typeof this.modeClass === 'string') { |
|||
buildTranfrom(type, this.modeClass) |
|||
} else { |
|||
this.modeClass.forEach(mode => { |
|||
buildTranfrom(type, mode) |
|||
}) |
|||
} |
|||
|
|||
return this.animation |
|||
}, |
|||
animationType(type) { |
|||
return { |
|||
fade: type ? 1 : 0, |
|||
'slide-top': `translateY(${type ? '0' : '-100%'})`, |
|||
'slide-right': `translateX(${type ? '0' : '100%'})`, |
|||
'slide-bottom': `translateY(${type ? '0' : '100%'})`, |
|||
'slide-left': `translateX(${type ? '0' : '-100%'})`, |
|||
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`, |
|||
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})` |
|||
} |
|||
}, |
|||
// 内置动画类型与实际动画对应字典 |
|||
animationMode() { |
|||
return { |
|||
fade: 'opacity', |
|||
'slide-top': 'translateY', |
|||
'slide-right': 'translateX', |
|||
'slide-bottom': 'translateY', |
|||
'slide-left': 'translateX', |
|||
'zoom-in': 'scale', |
|||
'zoom-out': 'scale' |
|||
} |
|||
}, |
|||
// 驼峰转中横线 |
|||
toLine(name) { |
|||
return name.replace(/([A-Z])/g, '-$1').toLowerCase() |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style></style> |
@ -1,277 +0,0 @@ |
|||
<template> |
|||
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { createAnimation } from './createAnimation' |
|||
|
|||
/** |
|||
* Transition 过渡动画 |
|||
* @description 简单过渡动画组件 |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=985 |
|||
* @property {Boolean} show = [false|true] 控制组件显示或隐藏 |
|||
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型 |
|||
* @value fade 渐隐渐出过渡 |
|||
* @value slide-top 由上至下过渡 |
|||
* @value slide-right 由右至左过渡 |
|||
* @value slide-bottom 由下至上过渡 |
|||
* @value slide-left 由左至右过渡 |
|||
* @value zoom-in 由小到大过渡 |
|||
* @value zoom-out 由大到小过渡 |
|||
* @property {Number} duration 过渡动画持续时间 |
|||
* @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red` |
|||
*/ |
|||
export default { |
|||
name: 'uniTransition', |
|||
emits:['click','change'], |
|||
props: { |
|||
show: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
modeClass: { |
|||
type: [Array, String], |
|||
default() { |
|||
return 'fade' |
|||
} |
|||
}, |
|||
duration: { |
|||
type: Number, |
|||
default: 300 |
|||
}, |
|||
styles: { |
|||
type: Object, |
|||
default() { |
|||
return {} |
|||
} |
|||
}, |
|||
customClass:{ |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
isShow: false, |
|||
transform: '', |
|||
opacity: 1, |
|||
animationData: {}, |
|||
durationTime: 300, |
|||
config: {} |
|||
} |
|||
}, |
|||
watch: { |
|||
show: { |
|||
handler(newVal) { |
|||
if (newVal) { |
|||
this.open() |
|||
} else { |
|||
// 避免上来就执行 close,导致动画错乱 |
|||
if (this.isShow) { |
|||
this.close() |
|||
} |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
computed: { |
|||
// 生成样式数据 |
|||
stylesObject() { |
|||
let styles = { |
|||
...this.styles, |
|||
'transition-duration': this.duration / 1000 + 's' |
|||
} |
|||
let transform = '' |
|||
for (let i in styles) { |
|||
let line = this.toLine(i) |
|||
transform += line + ':' + styles[i] + ';' |
|||
} |
|||
return transform |
|||
}, |
|||
// 初始化动画条件 |
|||
transformStyles() { |
|||
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject |
|||
} |
|||
}, |
|||
created() { |
|||
// 动画默认配置 |
|||
this.config = { |
|||
duration: this.duration, |
|||
timingFunction: 'ease', |
|||
transformOrigin: '50% 50%', |
|||
delay: 0 |
|||
} |
|||
this.durationTime = this.duration |
|||
}, |
|||
methods: { |
|||
/** |
|||
* ref 触发 初始化动画 |
|||
*/ |
|||
init(obj = {}) { |
|||
if (obj.duration) { |
|||
this.durationTime = obj.duration |
|||
} |
|||
this.animation = createAnimation(Object.assign(this.config, obj),this) |
|||
}, |
|||
/** |
|||
* 点击组件触发回调 |
|||
*/ |
|||
onClick() { |
|||
this.$emit('click', { |
|||
detail: this.isShow |
|||
}) |
|||
}, |
|||
/** |
|||
* ref 触发 动画分组 |
|||
* @param {Object} obj |
|||
*/ |
|||
step(obj, config = {}) { |
|||
if (!this.animation) return |
|||
for (let i in obj) { |
|||
try { |
|||
if(typeof obj[i] === 'object'){ |
|||
this.animation[i](...obj[i]) |
|||
}else{ |
|||
this.animation[i](obj[i]) |
|||
} |
|||
} catch (e) { |
|||
console.error(`方法 ${i} 不存在`) |
|||
} |
|||
} |
|||
this.animation.step(config) |
|||
return this |
|||
}, |
|||
/** |
|||
* ref 触发 执行动画 |
|||
*/ |
|||
run(fn) { |
|||
if (!this.animation) return |
|||
this.animation.run(fn) |
|||
}, |
|||
// 开始过度动画 |
|||
open() { |
|||
clearTimeout(this.timer) |
|||
this.transform = '' |
|||
this.isShow = true |
|||
let { opacity, transform } = this.styleInit(false) |
|||
if (typeof opacity !== 'undefined') { |
|||
this.opacity = opacity |
|||
} |
|||
this.transform = transform |
|||
// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常 |
|||
this.$nextTick(() => { |
|||
// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器 |
|||
this.timer = setTimeout(() => { |
|||
this.animation = createAnimation(this.config, this) |
|||
this.tranfromInit(false).step() |
|||
this.animation.run() |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
}, 20) |
|||
}) |
|||
}, |
|||
// 关闭过度动画 |
|||
close(type) { |
|||
if (!this.animation) return |
|||
this.tranfromInit(true) |
|||
.step() |
|||
.run(() => { |
|||
this.isShow = false |
|||
this.animationData = null |
|||
this.animation = null |
|||
let { opacity, transform } = this.styleInit(false) |
|||
this.opacity = opacity || 1 |
|||
this.transform = transform |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
}) |
|||
}, |
|||
// 处理动画开始前的默认样式 |
|||
styleInit(type) { |
|||
let styles = { |
|||
transform: '' |
|||
} |
|||
let buildStyle = (type, mode) => { |
|||
if (mode === 'fade') { |
|||
styles.opacity = this.animationType(type)[mode] |
|||
} else { |
|||
styles.transform += this.animationType(type)[mode] + ' ' |
|||
} |
|||
} |
|||
if (typeof this.modeClass === 'string') { |
|||
buildStyle(type, this.modeClass) |
|||
} else { |
|||
this.modeClass.forEach(mode => { |
|||
buildStyle(type, mode) |
|||
}) |
|||
} |
|||
return styles |
|||
}, |
|||
// 处理内置组合动画 |
|||
tranfromInit(type) { |
|||
let buildTranfrom = (type, mode) => { |
|||
let aniNum = null |
|||
if (mode === 'fade') { |
|||
aniNum = type ? 0 : 1 |
|||
} else { |
|||
aniNum = type ? '-100%' : '0' |
|||
if (mode === 'zoom-in') { |
|||
aniNum = type ? 0.8 : 1 |
|||
} |
|||
if (mode === 'zoom-out') { |
|||
aniNum = type ? 1.2 : 1 |
|||
} |
|||
if (mode === 'slide-right') { |
|||
aniNum = type ? '100%' : '0' |
|||
} |
|||
if (mode === 'slide-bottom') { |
|||
aniNum = type ? '100%' : '0' |
|||
} |
|||
} |
|||
this.animation[this.animationMode()[mode]](aniNum) |
|||
} |
|||
if (typeof this.modeClass === 'string') { |
|||
buildTranfrom(type, this.modeClass) |
|||
} else { |
|||
this.modeClass.forEach(mode => { |
|||
buildTranfrom(type, mode) |
|||
}) |
|||
} |
|||
|
|||
return this.animation |
|||
}, |
|||
animationType(type) { |
|||
return { |
|||
fade: type ? 1 : 0, |
|||
'slide-top': `translateY(${type ? '0' : '-100%'})`, |
|||
'slide-right': `translateX(${type ? '0' : '100%'})`, |
|||
'slide-bottom': `translateY(${type ? '0' : '100%'})`, |
|||
'slide-left': `translateX(${type ? '0' : '-100%'})`, |
|||
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`, |
|||
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})` |
|||
} |
|||
}, |
|||
// 内置动画类型与实际动画对应字典 |
|||
animationMode() { |
|||
return { |
|||
fade: 'opacity', |
|||
'slide-top': 'translateY', |
|||
'slide-right': 'translateX', |
|||
'slide-bottom': 'translateY', |
|||
'slide-left': 'translateX', |
|||
'zoom-in': 'scale', |
|||
'zoom-out': 'scale' |
|||
} |
|||
}, |
|||
// 驼峰转中横线 |
|||
toLine(name) { |
|||
return name.replace(/([A-Z])/g, '-$1').toLowerCase() |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style></style> |
@ -1,319 +0,0 @@ |
|||
<template> |
|||
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> |
|||
</template> |
|||
|
|||
<script> |
|||
// #ifndef MP-WEIXIN |
|||
import { createAnimation } from './createAnimation' |
|||
// #endif |
|||
// #ifdef MP-WEIXIN |
|||
const createAnimation = require('./createAnimation.js').createAnimation |
|||
// #endif |
|||
|
|||
/** |
|||
* Transition 过渡动画 |
|||
* @description 简单过渡动画组件 |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=985 |
|||
* @property {Boolean} show = [false|true] 控制组件显示或隐藏 |
|||
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型 |
|||
* @value fade 渐隐渐出过渡 |
|||
* @value slide-top 由上至下过渡 |
|||
* @value slide-right 由右至左过渡 |
|||
* @value slide-bottom 由下至上过渡 |
|||
* @value slide-left 由左至右过渡 |
|||
* @value zoom-in 由小到大过渡 |
|||
* @value zoom-out 由大到小过渡 |
|||
* @property {Number} duration 过渡动画持续时间 |
|||
* @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red` |
|||
*/ |
|||
export default { |
|||
name: 'uniTransition', |
|||
emits:['click','change'], |
|||
props: { |
|||
show: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
modeClass: { |
|||
type: [Array, String], |
|||
default() { |
|||
return 'fade' |
|||
} |
|||
}, |
|||
duration: { |
|||
type: Number, |
|||
default: 300 |
|||
}, |
|||
styles: { |
|||
type: Object, |
|||
default() { |
|||
return {} |
|||
} |
|||
}, |
|||
customClass:{ |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
isShow: false, |
|||
transform: '', |
|||
opacity: 1, |
|||
animationData: {}, |
|||
durationTime: 300, |
|||
config: {} |
|||
} |
|||
}, |
|||
watch: { |
|||
show: { |
|||
handler(newVal) { |
|||
if (newVal) { |
|||
this.open() |
|||
} else { |
|||
// 避免上来就执行 close,导致动画错乱 |
|||
if (this.isShow) { |
|||
this.close() |
|||
} |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
computed: { |
|||
// 生成样式数据 |
|||
stylesObject() { |
|||
let styles = { |
|||
...this.styles, |
|||
'transition-duration': this.duration / 1000 + 's' |
|||
} |
|||
let transform = '' |
|||
for (let i in styles) { |
|||
let line = this.toLine(i) |
|||
transform += line + ':' + styles[i] + ';' |
|||
} |
|||
return transform |
|||
}, |
|||
// 初始化动画条件 |
|||
transformStyles() { |
|||
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject |
|||
} |
|||
}, |
|||
created() { |
|||
// 动画默认配置 |
|||
this.config = { |
|||
duration: this.duration, |
|||
timingFunction: 'ease', |
|||
transformOrigin: '50% 50%', |
|||
delay: 0 |
|||
} |
|||
this.durationTime = this.duration |
|||
}, |
|||
methods: { |
|||
/** |
|||
* ref 触发 初始化动画 |
|||
*/ |
|||
init(obj = {}) { |
|||
if (obj.duration) { |
|||
this.durationTime = obj.duration |
|||
} |
|||
this.animation = createAnimation(Object.assign(this.config, obj),this) |
|||
}, |
|||
/** |
|||
* 点击组件触发回调 |
|||
*/ |
|||
onClick() { |
|||
this.$emit('click', { |
|||
detail: this.isShow |
|||
}) |
|||
}, |
|||
/** |
|||
* ref 触发 动画分组 |
|||
* @param {Object} obj |
|||
*/ |
|||
step(obj, config = {}) { |
|||
if (!this.animation) return this |
|||
for (let i in obj) { |
|||
try { |
|||
if (this.animation && typeof this.animation[i] === 'function') { |
|||
if(typeof obj[i] === 'object'){ |
|||
this.animation[i](...obj[i]) |
|||
}else{ |
|||
this.animation[i](obj[i]) |
|||
} |
|||
} |
|||
} catch (e) { |
|||
console.error(`方法 ${i} 不存在:`, e) |
|||
} |
|||
} |
|||
try { |
|||
if (this.animation && typeof this.animation.step === 'function') { |
|||
this.animation.step(config) |
|||
} |
|||
} catch (e) { |
|||
console.error('动画step执行错误:', e) |
|||
} |
|||
return this |
|||
}, |
|||
/** |
|||
* ref 触发 执行动画 |
|||
*/ |
|||
run(fn) { |
|||
if (!this.animation) return |
|||
try { |
|||
if (this.animation && typeof this.animation.run === 'function') { |
|||
this.animation.run(fn) |
|||
} |
|||
} catch (e) { |
|||
console.error('动画执行错误:', e) |
|||
} |
|||
}, |
|||
// 开始过度动画 |
|||
open() { |
|||
clearTimeout(this.timer) |
|||
this.transform = '' |
|||
this.isShow = true |
|||
let { opacity, transform } = this.styleInit(false) |
|||
if (typeof opacity !== 'undefined') { |
|||
this.opacity = opacity |
|||
} |
|||
this.transform = transform |
|||
// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常 |
|||
this.$nextTick(() => { |
|||
// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器 |
|||
this.timer = setTimeout(() => { |
|||
try { |
|||
this.animation = createAnimation(this.config, this) |
|||
if (this.animation) { |
|||
let result = this.tranfromInit(false) |
|||
if (result && typeof result.step === 'function') { |
|||
result.step() |
|||
if (typeof result.run === 'function') { |
|||
result.run() |
|||
} |
|||
} |
|||
} |
|||
} catch (e) { |
|||
console.error('动画初始化错误:', e) |
|||
} |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
}, 20) |
|||
}) |
|||
}, |
|||
// 关闭过度动画 |
|||
close(type) { |
|||
if (!this.animation) return |
|||
try { |
|||
let result = this.tranfromInit(true) |
|||
if (result && typeof result.step === 'function') { |
|||
result.step() |
|||
if (typeof result.run === 'function') { |
|||
result.run(() => { |
|||
this.isShow = false |
|||
this.animationData = null |
|||
this.animation = null |
|||
let { opacity, transform } = this.styleInit(false) |
|||
this.opacity = opacity || 1 |
|||
this.transform = transform |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
}) |
|||
} |
|||
} |
|||
} catch (e) { |
|||
console.error('动画关闭错误:', e) |
|||
} |
|||
}, |
|||
// 处理动画开始前的默认样式 |
|||
styleInit(type) { |
|||
let styles = { |
|||
transform: '' |
|||
} |
|||
let buildStyle = (type, mode) => { |
|||
if (mode === 'fade') { |
|||
styles.opacity = this.animationType(type)[mode] |
|||
} else { |
|||
styles.transform += this.animationType(type)[mode] + ' ' |
|||
} |
|||
} |
|||
if (typeof this.modeClass === 'string') { |
|||
buildStyle(type, this.modeClass) |
|||
} else { |
|||
this.modeClass.forEach(mode => { |
|||
buildStyle(type, mode) |
|||
}) |
|||
} |
|||
return styles |
|||
}, |
|||
// 处理内置组合动画 |
|||
tranfromInit(type) { |
|||
if (!this.animation) return this |
|||
let buildTranfrom = (type, mode) => { |
|||
let aniNum = null |
|||
if (mode === 'fade') { |
|||
aniNum = type ? 0 : 1 |
|||
} else { |
|||
aniNum = type ? '-100%' : '0' |
|||
if (mode === 'zoom-in') { |
|||
aniNum = type ? 0.8 : 1 |
|||
} |
|||
if (mode === 'zoom-out') { |
|||
aniNum = type ? 1.2 : 1 |
|||
} |
|||
if (mode === 'slide-right') { |
|||
aniNum = type ? '100%' : '0' |
|||
} |
|||
if (mode === 'slide-bottom') { |
|||
aniNum = type ? '100%' : '0' |
|||
} |
|||
} |
|||
let methodName = this.animationMode()[mode] |
|||
if (this.animation && typeof this.animation[methodName] === 'function') { |
|||
this.animation[methodName](aniNum) |
|||
} |
|||
} |
|||
if (typeof this.modeClass === 'string') { |
|||
buildTranfrom(type, this.modeClass) |
|||
} else { |
|||
this.modeClass.forEach(mode => { |
|||
buildTranfrom(type, mode) |
|||
}) |
|||
} |
|||
|
|||
return this.animation |
|||
}, |
|||
animationType(type) { |
|||
return { |
|||
fade: type ? 1 : 0, |
|||
'slide-top': `translateY(${type ? '0' : '-100%'})`, |
|||
'slide-right': `translateX(${type ? '0' : '100%'})`, |
|||
'slide-bottom': `translateY(${type ? '0' : '100%'})`, |
|||
'slide-left': `translateX(${type ? '0' : '-100%'})`, |
|||
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`, |
|||
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})` |
|||
} |
|||
}, |
|||
// 内置动画类型与实际动画对应字典 |
|||
animationMode() { |
|||
return { |
|||
fade: 'opacity', |
|||
'slide-top': 'translateY', |
|||
'slide-right': 'translateX', |
|||
'slide-bottom': 'translateY', |
|||
'slide-left': 'translateX', |
|||
'zoom-in': 'scale', |
|||
'zoom-out': 'scale' |
|||
} |
|||
}, |
|||
// 驼峰转中横线 |
|||
toLine(name) { |
|||
return name.replace(/([A-Z])/g, '-$1').toLowerCase() |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style></style> |
@ -1,310 +0,0 @@ |
|||
<template> |
|||
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { createAnimation } from './createAnimation' |
|||
|
|||
/** |
|||
* Transition 过渡动画 |
|||
* @description 简单过渡动画组件 |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=985 |
|||
* @property {Boolean} show = [false|true] 控制组件显示或隐藏 |
|||
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型 |
|||
* @value fade 渐隐渐出过渡 |
|||
* @value slide-top 由上至下过渡 |
|||
* @value slide-right 由右至左过渡 |
|||
* @value slide-bottom 由下至上过渡 |
|||
* @value slide-left 由左至右过渡 |
|||
* @value zoom-in 由小到大过渡 |
|||
* @value zoom-out 由大到小过渡 |
|||
* @property {Number} duration 过渡动画持续时间 |
|||
* @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red` |
|||
*/ |
|||
export default { |
|||
name: 'uniTransition', |
|||
emits:['click','change'], |
|||
props: { |
|||
show: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
modeClass: { |
|||
type: [Array, String], |
|||
default() { |
|||
return 'fade' |
|||
} |
|||
}, |
|||
duration: { |
|||
type: Number, |
|||
default: 300 |
|||
}, |
|||
styles: { |
|||
type: Object, |
|||
default() { |
|||
return {} |
|||
} |
|||
}, |
|||
customClass:{ |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
isShow: false, |
|||
transform: '', |
|||
opacity: 1, |
|||
animationData: {}, |
|||
durationTime: 300, |
|||
config: {} |
|||
} |
|||
}, |
|||
watch: { |
|||
show: { |
|||
handler(newVal) { |
|||
if (newVal) { |
|||
this.open() |
|||
} else { |
|||
// 避免上来就执行 close,导致动画错乱 |
|||
if (this.isShow) { |
|||
this.close() |
|||
} |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
computed: { |
|||
// 生成样式数据 |
|||
stylesObject() { |
|||
let styles = { |
|||
...this.styles, |
|||
'transition-duration': this.duration / 1000 + 's' |
|||
} |
|||
let transform = '' |
|||
for (let i in styles) { |
|||
let line = this.toLine(i) |
|||
transform += line + ':' + styles[i] + ';' |
|||
} |
|||
return transform |
|||
}, |
|||
// 初始化动画条件 |
|||
transformStyles() { |
|||
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject |
|||
} |
|||
}, |
|||
created() { |
|||
// 动画默认配置 |
|||
this.config = { |
|||
duration: this.duration, |
|||
timingFunction: 'ease', |
|||
transformOrigin: '50% 50%', |
|||
delay: 0 |
|||
} |
|||
this.durationTime = this.duration |
|||
}, |
|||
methods: { |
|||
/** |
|||
* ref 触发 初始化动画 |
|||
*/ |
|||
init(obj = {}) { |
|||
if (obj.duration) { |
|||
this.durationTime = obj.duration |
|||
} |
|||
this.animation = createAnimation(Object.assign(this.config, obj),this) |
|||
}, |
|||
/** |
|||
* 点击组件触发回调 |
|||
*/ |
|||
onClick() { |
|||
this.$emit('click', { |
|||
detail: this.isShow |
|||
}) |
|||
}, |
|||
/** |
|||
* ref 触发 动画分组 |
|||
* @param {Object} obj |
|||
*/ |
|||
step(obj, config = {}) { |
|||
if (!this.animation || typeof this.animation.step !== 'function') return this |
|||
for (let i in obj) { |
|||
try { |
|||
if(typeof obj[i] === 'object'){ |
|||
this.animation[i](...obj[i]) |
|||
}else{ |
|||
this.animation[i](obj[i]) |
|||
} |
|||
} catch (e) { |
|||
console.error(`方法 ${i} 不存在`) |
|||
} |
|||
} |
|||
this.animation.step(config) |
|||
return this |
|||
}, |
|||
/** |
|||
* ref 触发 执行动画 |
|||
*/ |
|||
run(fn) { |
|||
if (!this.animation || typeof this.animation.run !== 'function') return |
|||
this.animation.run(fn) |
|||
}, |
|||
// 开始过度动画 |
|||
open() { |
|||
clearTimeout(this.timer) |
|||
this.transform = '' |
|||
this.isShow = true |
|||
let { opacity, transform } = this.styleInit(false) |
|||
if (typeof opacity !== 'undefined') { |
|||
this.opacity = opacity |
|||
} |
|||
this.transform = transform |
|||
// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常 |
|||
this.$nextTick(() => { |
|||
// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器 |
|||
this.timer = setTimeout(() => { |
|||
try { |
|||
this.animation = createAnimation(this.config, this) |
|||
if (this.animation && typeof this.animation.step === 'function' && typeof this.animation.run === 'function') { |
|||
const result = this.tranfromInit(false) |
|||
if (result && typeof result.step === 'function') { |
|||
result.step() |
|||
if (typeof this.animation.run === 'function') { |
|||
this.animation.run() |
|||
} |
|||
} |
|||
} |
|||
} catch (error) { |
|||
console.error('uni-transition animation error:', error) |
|||
} |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
}, 20) |
|||
}) |
|||
}, |
|||
// 关闭过度动画 |
|||
close(type) { |
|||
if (!this.animation || typeof this.animation.step !== 'function' || typeof this.animation.run !== 'function') return |
|||
try { |
|||
const result = this.tranfromInit(true) |
|||
if (result && typeof result.step === 'function') { |
|||
result.step() |
|||
if (typeof result.run === 'function') { |
|||
result.run(() => { |
|||
this.isShow = false |
|||
this.animationData = null |
|||
this.animation = null |
|||
let { opacity, transform } = this.styleInit(false) |
|||
this.opacity = opacity || 1 |
|||
this.transform = transform |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
}) |
|||
} |
|||
} |
|||
} catch (error) { |
|||
console.error('uni-transition close animation error:', error) |
|||
// 发生错误时直接关闭 |
|||
this.isShow = false |
|||
this.animationData = null |
|||
this.animation = null |
|||
this.$emit('change', { |
|||
detail: this.isShow |
|||
}) |
|||
} |
|||
}, |
|||
// 处理动画开始前的默认样式 |
|||
styleInit(type) { |
|||
let styles = { |
|||
transform: '' |
|||
} |
|||
let buildStyle = (type, mode) => { |
|||
if (mode === 'fade') { |
|||
styles.opacity = this.animationType(type)[mode] |
|||
} else { |
|||
styles.transform += this.animationType(type)[mode] + ' ' |
|||
} |
|||
} |
|||
if (typeof this.modeClass === 'string') { |
|||
buildStyle(type, this.modeClass) |
|||
} else { |
|||
this.modeClass.forEach(mode => { |
|||
buildStyle(type, mode) |
|||
}) |
|||
} |
|||
return styles |
|||
}, |
|||
// 处理内置组合动画 |
|||
tranfromInit(type) { |
|||
if (!this.animation) { |
|||
console.warn('uni-transition: animation object is not initialized') |
|||
return this.animation |
|||
} |
|||
let buildTranfrom = (type, mode) => { |
|||
let aniNum = null |
|||
if (mode === 'fade') { |
|||
aniNum = type ? 0 : 1 |
|||
} else { |
|||
aniNum = type ? '-100%' : '0' |
|||
if (mode === 'zoom-in') { |
|||
aniNum = type ? 0.8 : 1 |
|||
} |
|||
if (mode === 'zoom-out') { |
|||
aniNum = type ? 1.2 : 1 |
|||
} |
|||
if (mode === 'slide-right') { |
|||
aniNum = type ? '100%' : '0' |
|||
} |
|||
if (mode === 'slide-bottom') { |
|||
aniNum = type ? '100%' : '0' |
|||
} |
|||
} |
|||
const animationMethod = this.animationMode()[mode] |
|||
if (this.animation && typeof this.animation[animationMethod] === 'function') { |
|||
this.animation[animationMethod](aniNum) |
|||
} |
|||
} |
|||
if (typeof this.modeClass === 'string') { |
|||
buildTranfrom(type, this.modeClass) |
|||
} else { |
|||
this.modeClass.forEach(mode => { |
|||
buildTranfrom(type, mode) |
|||
}) |
|||
} |
|||
|
|||
return this.animation |
|||
}, |
|||
animationType(type) { |
|||
return { |
|||
fade: type ? 1 : 0, |
|||
'slide-top': `translateY(${type ? '0' : '-100%'})`, |
|||
'slide-right': `translateX(${type ? '0' : '100%'})`, |
|||
'slide-bottom': `translateY(${type ? '0' : '100%'})`, |
|||
'slide-left': `translateX(${type ? '0' : '-100%'})`, |
|||
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`, |
|||
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})` |
|||
} |
|||
}, |
|||
// 内置动画类型与实际动画对应字典 |
|||
animationMode() { |
|||
return { |
|||
fade: 'opacity', |
|||
'slide-top': 'translateY', |
|||
'slide-right': 'translateX', |
|||
'slide-bottom': 'translateY', |
|||
'slide-left': 'translateX', |
|||
'zoom-in': 'scale', |
|||
'zoom-out': 'scale' |
|||
} |
|||
}, |
|||
// 驼峰转中横线 |
|||
toLine(name) { |
|||
return name.replace(/([A-Z])/g, '-$1').toLowerCase() |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style></style> |
Loading…
Reference in new issue