頁面代碼
<template>
<view class="main">
<scroll-view :scroll-top="0" scroll-y="true" style="height:90vh">
<view class="box-right">
<view>
<block v-for="item in 15">
<view @click="addCar($event)" class="add-btn">
加入購物車
</view>
</block>
</view>
</view>
</scroll-view>
<cartsBall ref="cartBtn" :endPos="{
x: 80, y:height
}"></cartsBall>
</view>
</template>
<script setup>
import {
onMounted,
ref
} from "vue";
import cartsBall from './cartsBall.vue'
const height = ref(0)
onMounted(() => {
height.value = uni.getWindowInfo().windowHeight
})
const cartBtn = ref()
const addCar = (e) => {
cartBtn.value.drop({
x: e.detail.x,
y: e.detail.y
})
}
</script>
<style>
.main {
height: 90vh;
background: #cdcdcd;
}
.box-right {
display: flex;
justify-content: end;
}
.add-btn {
display: flex;
width: 80px;
height: 50px;
line-height: 50px;
background: #a0cfff;
margin-bottom: 40px;
}
</style>
cartsBall.vue 組件代碼
<template>
<view>
<view class="ball-wrap" v-for="(item, k) of balls" :key="k"
:style="{
opacity: item.show,
top: item.start.y + 'px',
left: item.start.x + 'px',
transitionDuration: (item.show?(duration/1000):0)+'s',
'transition-timing-function': xTimeFunction[!item.ani?0:1],
transform: 'translate3d('+item.offset.x+'px,0,0)',
zIndex
}"
>
<view class="ball" :class="{ball3d:is3dSheet}"
:style="{
marginLeft: -size/2 + 'px',
marginTop: -size/2 + 'px',
padding: size + 'px',
backgroundImage: ballImage,
backgroundColor: ballColor,
transitionDuration: (item.show?(duration/1000):0)+'s',
transform: 'translate3d(0,'+item.offset.y+'px,0)',
'transition-timing-function': yTimeFunction[item.ani?0:1]
}"
></view>
</view>
</view>
</template>
<script>
// 節(jié)流函數
function debounce(func, delay, context) {
return function () {
func.id && clearTimeout(func.id);
func.id = setTimeout(() => {
func.apply(context,arguments)
}, delay)
}
}
export default {
props: {
size: {
type: Number,
default: 8
},
//3D
is3dSheet: {
type: Boolean,
default: true
},
endPos: {
type: Object,
default(){
return {
x: 150,
y: 150
}
}
},
//持續(xù)時間
duration: {
type: Number,
default: 700
},
zIndex: {
type: Number,
default: 9999
},
ballImage: {
type: String,
default: ''
},
ballColor: {
type: String,
default: 'red'
}
},
data() {
return {
balls: [],
xTimeFunction: ['ease-in', 'ease-out'],
yTimeFunction: ['cubic-bezier(0.23,-0.1, 0.84,-0.01)', 'cubic-bezier(0, 0.59, 0.46, 2.15)']
};
},
mounted() {
this.initBalls()
},
methods: {
drop(pos){
let ball
let duration=this.duration
for (var i = 0; i < this.balls.length; i++) {
if(this.balls[i].show){continue}
ball = this.balls[i]
}
ball.start.x = pos.x
ball.start.y = pos.y
ball.offset.x = this.endPos.x - pos.x
ball.offset.y = this.endPos.y - pos.y
if(ball.offset.y>0){
ball.ani = 1
}else{
ball.ani = 0
}
ball.show = 1
// 無限加載
this.balls.push({
show: 0,
start: {
x: 0,
y: 0
},
offset: {
x: 0,
y: 0
},
ani: 0
})
if(ball.offset.y<120){
//y低于120時加速的代碼
}
setTimeout(()=>{
ball.show = 0
}, duration)
debounce(this.initBalls, duration+200, this)()
},
initBalls(){
const balls = []
// 解決微信自帶瀏覽器渲染慢問題死遭,先加載幾個
for (var i = 0; i < 3; i++) {
const ball = {
show: 0,
start: {
x: 0,
y: 0
},
offset: {
x: 0,
y: 0
},
ani: 0
}
balls.push(ball)
}
this.balls = balls
}
}
}
</script>
<style lang="scss" scoped>
.ball-wrap {
position: fixed;
transform: translateZ(0);
z-index: 999;
transition-property: transform;
pointer-events: none;
.ball {
width: 0;
height: 0;
padding: 40rpx;
border-radius: 40rpx;
overflow: hidden;
background-repeat: no-repeat;
background-size: cover;
transform: translateZ(0);
transition-property: transform opacity;
&.ball3d {
position: relative;
&::after {
content: "";
display: block;
position: absolute;
left: -15rpx;
right: -15rpx;
top: -15rpx;
bottom: -15rpx;
opacity: 0.35;
background-image: radial-gradient(30% 30%, white, transparent, black);
background-size: 100%;
background-repeat: no-repeat;
background-position: -5rpx -5rpx;
}
}
}
}
</style>