1、预期效果
2、实现思路
每次点击添加按钮时,往该按钮上方添加一个悬浮元素,通过位移动画将元素移到目标位置。
-
为每个点击元素设置不同的class,才能通过uni.createSelectorQuery来获取每个元素的节点信息;
-
添加一个与点击元素一模一样的动画元素;
-
获取点击元素的节点信息将动画元素放置到点击元素上方;
-
计算动画元素到目标位置的距离,获得xy坐标执行位移动画;
-
等待每个动画元素执行动画完毕后移除该元素。
3、核心代码
TypeScript
<template>
<view>
<!-- 商品列表 -->
<view v-for="item in goodsList" :key="item.id">
<view :class="[`add-cart-${item.id}`]" @click="addToCart(item)">加购</view>
</view>
<!-- 动画元素列表 -->
<view
v-for="item in anims" :key="item.key"
style="position: fixed; transition: transform 0.5s linear;"
:style="{
top: `${item.top}px`,
left: `${item.left}px`,
transform: `translate(${item.x}px, ${item.y}px)`,
}"
>
加购
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import uniqueId from 'lodash-es/uniqueId';
const sys = uni.getSystemInfoSync();
const anims = ref<any[]>([]);
const goodsList = ref([{ id: 1 }, { id: 2 }, { id: 3 }])
function addToCart(item) {
// 添加动画元素
const key = uniqueId();
anims.value.push({
key,
id: item.id,
left: 0,
top: 0,
y: 0,
x: 0,
});
// 获取点击元素的节点信息
uni.createSelectorQuery().select(`.add-cart-${item.id}`)
.boundingClientRect((e: any) => {
// 初始化起始位置
anims.value.some((citem) => {
if (citem.key === key) {
citem.top = e.top;
citem.left = e.left;
return true;
}
return false;
});
nextTick(() => {
// 设置目标位置
anims.value.some((citem) => {
if (citem.key === key) {
citem.y = sys.windowHeight - citem.top - 50;
citem.x = -sys.windowWidth * 0.30;
setTimeout(() => { // 等待动画执行完毕移除元素
anims.value.splice(anims.value.findIndex((v: any) => v.key === key), 1);
}, 500);
return true;
}
return false;
});
});
}).exec();
}
</script>