222 lines
8.9 KiB
Vue
222 lines
8.9 KiB
Vue
<template>
|
|
<view class="like-button">
|
|
<!-- #ifdef APP-NVUE -->
|
|
<list class="animate-wrap">
|
|
<cell class="a-img" v-for="(item,index) in viewList" :key="item.elId" :ref="item.elId" :style="{ 'right': site.x || site[0] + 'rpx', 'bottom': site.y || site[1] + 'rpx'}">
|
|
<image :style="{ 'width': imgWidth + 'rpx', 'height': imgHeight + 'rpx'}" mode="widthFix" :src="item.src" :animation="item.animation"></image>
|
|
</cell>
|
|
</list>
|
|
<!-- #endif -->
|
|
<!-- #ifndef APP-NVUE -->
|
|
<view class="animate-wrap">
|
|
<view class="a-img" v-for="(item,index) in viewList" :key="item.elId" :ref="item.elId" :style="{ 'right': site.x || site[0] + 'rpx', 'bottom': site.y || site[1] + 'rpx'}">
|
|
<image :style="{'width': imgWidth + 'rpx', 'height': imgHeight + 'rpx'}" mode="widthFix" :src="item.src" :animation="item.animation"></image>
|
|
</view>
|
|
</view>
|
|
<!-- #endif -->
|
|
<view class="on-button">
|
|
<view ref="likeBtn" id="like-btn" class="el-like-btn" @click="handleClick">
|
|
<slot></slot>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
props: {
|
|
src: {
|
|
type: String,
|
|
default: '/static/images/green/tabbar/cart.png'
|
|
},
|
|
showImgs: { // 显示图标路径
|
|
type: Array,
|
|
default: () => {
|
|
return [
|
|
'https://new.shopxo.vip/static/plugins/live/images/like/like1.png',
|
|
'https://new.shopxo.vip/static/plugins/live/images/like/like2.png',
|
|
'https://new.shopxo.vip/static/plugins/live/images/like/like3.png',
|
|
'https://new.shopxo.vip/static/plugins/live/images/like/like4.png',
|
|
'https://new.shopxo.vip/static/plugins/live/images/like/like5.png',
|
|
]
|
|
}
|
|
},
|
|
duration: { // 动画效果时间
|
|
type: Number,
|
|
default: 5000
|
|
},
|
|
range: { // x 间隔幅度
|
|
type: Number,
|
|
default: 50
|
|
},
|
|
high: {
|
|
type: Number,
|
|
default: 360
|
|
},
|
|
width: { // 图标宽度
|
|
type: Number || String,
|
|
default: 52
|
|
},
|
|
height: { // 图标高度
|
|
type: Number || String,
|
|
default: 52
|
|
},
|
|
imgWidth: { // 图标宽度
|
|
type: Number || String,
|
|
default: 52
|
|
},
|
|
imgHeight: { // 图标高度
|
|
type: Number || String,
|
|
default: 52
|
|
},
|
|
throttle: { // 点击节流 ms
|
|
type: Number,
|
|
default: 200
|
|
},
|
|
site: { // x y 坐标 [x<Number>, y<Number>]
|
|
type: Array || Object,
|
|
default: () => {
|
|
return [122, 120] || {
|
|
x: 122,
|
|
y: 100
|
|
}
|
|
}
|
|
},
|
|
large: { // 是否缩放冒泡
|
|
type: [Number, Boolean],
|
|
default: false
|
|
},
|
|
alone: {
|
|
type: Boolean,
|
|
default: true
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
viewList: [], // 渲染元素
|
|
elId: 0, // 元素渲染id
|
|
oldTime: 0, // 全局时间用于函数节流
|
|
timer: null, // 定时器
|
|
waitDeleteIndex: 0
|
|
}
|
|
},
|
|
watch: {},
|
|
methods: {
|
|
handleClick(e) {
|
|
// 函数节流
|
|
let interval = e.timeStamp - this.oldTime
|
|
if (interval < this.throttle) return null;
|
|
this.oldTime = e.timeStamp
|
|
let animation = {}
|
|
// 创建animate配置
|
|
// #ifdef APP-NVUE
|
|
animation = weex.requireModule('animation')
|
|
// #endif
|
|
let randomImg = Math.floor(Math.random() * this.showImgs.length)
|
|
let _item = {
|
|
elId: 'el_likeicon_' + this.elId, // 生成元素ref
|
|
src: this.showImgs[randomImg], // 随机图标
|
|
animation: animation, // 每个盒子动画
|
|
x: Math.ceil(Math.random() * this.range), // 方向间距
|
|
q: Math.floor(Math.random() * 2), // 随机方向
|
|
}
|
|
// 动画
|
|
let _abs = ['-', '']
|
|
let _dirX = Number(_abs[_item.q] + _item.x) // 随机的方向和间距
|
|
let _dirY = this.high - Math.random() * 10
|
|
// 生成DOM
|
|
this.elId++
|
|
this.viewList.push(_item)
|
|
// #ifndef APP-NVUE
|
|
_item.animation = uni.createAnimation({
|
|
duration: this.duration,
|
|
timingFunction: 'ease-out',
|
|
})
|
|
setTimeout(() => {
|
|
console.log('animation finished.')
|
|
// 完成后事件回调
|
|
this.$emit('finished')
|
|
// 逐渐消失
|
|
if (this.alone) {
|
|
this.waitDeleteIndex++
|
|
this.onThrottle(this.deleteView, this.duration)
|
|
return null;
|
|
} else {
|
|
// 完成动画后在n秒后清空
|
|
clearTimeout(this.timer)
|
|
this.timer = setTimeout(() => {
|
|
this.viewList = []
|
|
}, this.duration)
|
|
return null;
|
|
}
|
|
}, this.duration)
|
|
// #endif
|
|
// 执行动画
|
|
setTimeout(() => {
|
|
this.$nextTick(() => {
|
|
let _n = 1
|
|
if (this.large) _n = typeof(this.large) === 'number' ? this.large : 2;
|
|
// #ifndef APP-NVUE
|
|
_item.animation.translateY(-_dirY).translateX(_dirX).scale(_n, _n).opacity(0).step()
|
|
_item.animation = _item.animation.export()
|
|
// #endif
|
|
// #ifdef APP-NVUE
|
|
let el = this.$refs[_item.elId][0];
|
|
clearTimeout(this.timer)
|
|
_item.animation.transition(el, {
|
|
styles: {
|
|
transform: `translate(${_dirX}rpx, -${_dirY}rpx) scale(${_n}, ${_n}])`,
|
|
transformOrigin: 'center center',
|
|
opacity: 0
|
|
},
|
|
duration: this.duration, // ms
|
|
timingFunction: 'ease-out',
|
|
delay: 0 // ms
|
|
}, () => {
|
|
console.log('animation finished.')
|
|
// 完成后事件回调
|
|
this.$emit('finished')
|
|
// 逐渐消失
|
|
if (this.alone) {
|
|
this.waitDeleteIndex++
|
|
this.onThrottle(this.deleteView, this.duration)
|
|
return null
|
|
} else {
|
|
// 完成动画后在n秒后清空
|
|
clearTimeout(this.timer)
|
|
this.timer = setTimeout(() => {
|
|
this.viewList = []
|
|
}, this.duration)
|
|
}
|
|
})
|
|
// #endif
|
|
})
|
|
}, 0)
|
|
// 点击立即触发组件事件
|
|
this.$emit('handleClick', this.elId)
|
|
},
|
|
deleteView() {
|
|
this.viewList.splice(0, this.waitDeleteIndex)
|
|
this.waitDeleteIndex = 0
|
|
},
|
|
onThrottle(fn, delay) {
|
|
let verifi = true
|
|
return (function() {
|
|
if (!verifi) return false;
|
|
verifi = false
|
|
setTimeout(() => {
|
|
fn()
|
|
verifi = true
|
|
}, delay)
|
|
})()
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.a-img {
|
|
position: fixed;
|
|
}
|
|
</style> |