项目是关于门店购物商品相关的H5 移动端, 技术栈 vue + ts 项目框架,通过refs 获取dom 会有报错问题,本模块还是通过原生的方式获取dom ,绑定类名最好是ID 标签名。
1 添加动画模块封装:
/** 购物车添加动画效果
* @param addBtn 开始-源添加按钮 dom对象
* @param shopCar 目标-购物车库 dom 对象
* @return controllPath
*/
import { Toast } from "vant";
export const addShopPath = (addBtn: Element | null, shopCar: Element | null, imgUrl?: string | null): void => {
let addCartDom = addBtn,
shopCarDom = shopCar;
// dom类型提示
if (addBtn instanceof HTMLElement) {
addCartDom = addBtn;
} else {
Toast("第一个参数应为添加按钮的dom元素或该元素的选择器。");
return;
}
if (shopCar instanceof HTMLElement) {
shopCarDom = shopCar;
} else {
Toast("第二个参数应为购物车的dom元素或该元素的选择器。");
return;
}
/*
* 绘制动画购物车
* */
const drawBall = (): any => {
const ballDom = document.createElement("div");
ballDom.id = "ballDom";
ballDom.style.width = "24px";
ballDom.style.height = "24px";
// ballDom.style.border = "2px solid pink";
// ballDom.style.background = "red";
ballDom.style.borderRadius = "50%";
// 自定义购物车背景图片
ballDom.innerHTML =
'<img width= "100%"' +
'src="' +
require("@/assets/images/template/template1/same-city-product-list/cart-animation@2x.png") +
'"/>';
// ballDom.style.backgroundSize = "cover";
// 用于隐藏
ballDom.style.display = "block";
ballDom.style.position = "absolute";
ballDom.style.zIndex = "2300";
console.log("imgUrl", imgUrl);
return ballDom;
};
// 获取两个dom的位置
const addCartDomPosition = addCartDom.getBoundingClientRect();
const shopCartDomPosition = shopCarDom.getBoundingClientRect();
// 25 目标元素距离边框距离优化 可自定义
const addBtnDomCenterX = (addCartDomPosition.left + addCartDomPosition.right) / 2 - 25;
const addBtnDomCenterY = (addCartDomPosition.top + addCartDomPosition.bottom) / 2;
const shopCarCenterX = (shopCartDomPosition.left + shopCartDomPosition.right) / 2 - 25;
const shopCarCenterY = (shopCartDomPosition.top + shopCartDomPosition.bottom) / 2;
//计算增加按钮 是在 相对于购物车的 左边还是右边(用于控制后面的移动方向)
const relativePosition = addBtnDomCenterX > shopCarCenterX ? -1 : 1;
// 获取连个dom之间的距离 取正距离
const xDistance = Math.abs(addBtnDomCenterX - shopCarCenterX);
const yDistance = Math.abs(addBtnDomCenterY - shopCarCenterY);
// 绘制小车并设置其位置
const ballDom = drawBall();
ballDom.style.top = addBtnDomCenterY + "px";
ballDom.style.left = addBtnDomCenterX + "px";
document.body.appendChild(ballDom);
/*
* 根据一元二次方程的轨迹求出对象的系数 y = ax^2 + bx + c
* var coefficientC = 0;
* var coefficientB = 0;
* var coefficientA = yDistance / Math.pow(xDistance, 2);
*/
// 小车的横竖坐标
let xAbscissa = 0,
yAbscissa = 0;
//设置移动路径
const ballTimer = setInterval(() => {
//每次重新坐标 pow() 方法表示的是 x 的 y 次幂的值
xAbscissa += 5 * relativePosition;
yAbscissa = (yDistance / Math.pow(xDistance, 2)) * Math.pow(xAbscissa, 2);
ballDom.style.top = addBtnDomCenterY + yAbscissa + "px";
ballDom.style.left = addBtnDomCenterX + xAbscissa + "px";
//检查是否到达终点
const surplusDistance = parseInt(ballDom.style.left) - shopCarCenterX;
if (Math.abs(surplusDistance) <= 10) {
clearInterval(ballTimer);
// 开启隐藏动画图标
ballDom.style.display = "none";
}
// 6 控制动画的响应快慢 ,具体根据实际动画效果修改
}, 6);
};
2 htm 动态添加dom的id名, 做唯一标识
// 开始位置
<div
v-if="row.spec_type === 0"
:id="`buy-cart${key}`"
class="add-cart"
@click.stop="addShopCart(row, key)"
>
</div>
<div @click="openCard">
// 目标位置
<div id="shop-cart" class="left"></div>
</div>
3 组件引入传参 方式:
// 请求成功后 动态添加购物车
addShopCartHandle(key: number | string): void {
if (key== "true") return; // 其他规格操作无需动画
// 获取目标对象 由于多个商品列表id名键名拼接 获取唯一对象
let buyCartName = `#buy-cart${key}`;
const addCartDom = document.querySelector(buyCartName);
const shopCartDom = document.querySelector("#shop-cart");
// 触发添加购物车动画
addShopPath(addCartDom , shopCartDom );
}