https://www.zhangxinxu.com/wordpress/2013/12/javascript-js-%e5%85%83%e7%b4%a0-%e6%8a%9b%e7%89%a9%e7%ba%bf-%e8%bf%90%e5%8a%a8-%e5%8a%a8%e7%94%bb/
文件:parabola.js
借鉴网上的抛物线写了个添加购物车的动画
<style>
* {
margin: 0;
padding: 0;
}
.item {
display: flex;
}
.item img {
width: 200px;
height: 200px;
}
.shopping-cart {
position: fixed;
bottom: 0;
right: 0;
width: 50px;
height: 50px;
background: red;
}
</style>
<body>
<div class="item">
<img src="https://vncdn.mobi88.cn/20220107/ct/ah_srl/iamges/shareImg.png" alt="">
<div class="add-to-cart">添加购物车</div>
</div>
<!-- 购物车图标 -->
<div class="shopping-cart"></div>
</body>
<script>
/*
封装一个通过dom节点名称筛选出指定dom的方法
dom: 一个dom节点
nodeName: string,需要返回的节点名称
*/
function divFind(dom, nodeName) {
var arr = dom.children
var filterDom = ''
if (arr.length <= 0) return dom;
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].nodeName);
if (arr[i].nodeName === String(nodeName).toUpperCase()) {
filterDom = arr[i]
}
}
return filterDom
}
// 设置样式
function setCss(dom, cssName, cssStr) {
dom.style[cssName] = String(cssStr)
}
// 防抖标识
var disabled = false
function animaStar() {
// 设置防抖,动画中将不能触发
if (disabled) return;
disabled = true
var itemDom = document.querySelector('.item')
// 商品图片dom
var currentImg = divFind(itemDom, 'img')
// 购物车dom
var cardDom = document.querySelector('.shopping-cart')
if (!currentImg) return console.log('为获取到商品图片');
// 注意克隆是否克隆子节点问题
var imgCloneDom = currentImg.cloneNode()
// 获取商品图片和终点dom信息
var imgDomInfo = currentImg.getBoundingClientRect()
var cardDomInfo = cardDom.getBoundingClientRect()
// 设置样式
setCss(imgCloneDom, 'width', currentImg.width + 'px')
setCss(imgCloneDom, 'height', currentImg.height + 'px')
setCss(imgCloneDom, 'position', 'fixed')
setCss(imgCloneDom, 'top', imgDomInfo.top + 'px')
setCss(imgCloneDom, 'left', imgDomInfo.left + 'px')
setCss(imgCloneDom, 'z-index', '100')
setCss(imgCloneDom, 'overflow', 'hidden')
// 添加到页面
document.body.appendChild(imgCloneDom)
// 给动画的dom添加类标识
imgCloneDom.classList.add('clone')
// 开始动画
anime({
targets: imgCloneDom,
keyframes: [
{
'borderRadius': '50%',
'width': '50px',
'height': '50px',
'top': imgDomInfo.height / 2 - 25 + 'px',
'left': imgDomInfo.width / 2 - 25 + 'px',
},
// {
// translateX: 15,
// translateY: -15,
// },
// {
// top: cardDomInfo.top,
// left: cardDomInfo.left,
// translateX: 0,
// translateY: 0,
// }
],
changeComplete: function (anim) {
startAnit()
},
});
function startAnit() {
// 获取初始元素到目标元素的偏移总量
let diffX = cardDomInfo.left - imgDomInfo.left
let diffY = cardDomInfo.top - imgDomInfo.top
// 假设(elX,elY)为(0,0),则c = 0,求a、b
// 设a=0.001 => 实际指焦点到准线的距离,可以抽象成曲率,这里模拟扔物体的抛物线,因此是开口向下的
let a = 0.005
// 则 b = (y - a*x*x) / x
let b = (diffY - a * diffX * diffX) / diffX
// 定义一个定时器,用来执行抛物线动画
let timer = null;
// 执行的时间
let duration = 500
function start() {
// 执行的开始时间
beginTime = new Date()
// 结束的时间
endTime = +beginTime + duration
// 定时器,执行抛物线动画
timer = setInterval(() => {
let now = new Date()
step(now);
}, 13);
}
start()
// 抛物线动画的方法
function step(now) {
let x, y;
if (now > endTime) {
// 运行结束
x = diffX;
y = diffY;
clearInterval(timer);
} else {
// 计算每一步的X轴的位置
x = diffX * ((now - beginTime) / duration);
// 则每一步的Y轴的位置y = a*x*x + b*x + c; c==0;
y = a * x * x + b * x;
}
anime({
targets: imgCloneDom,
keyframes: [
{
left: imgDomInfo.left + x,
top: imgDomInfo.top + y,
},
{
scale: 0,
}
],
endDelay: 200,
// easing: 'easeInOutExpo',
complete: function (anim) {
// console.log(anim);
// 动画结束,检测document中是否存在动画的dom
var cloneDom = document.getElementsByClassName('clone');
if (cloneDom.length > 0) {
// 移除动画dom
document.body.removeChild(imgCloneDom)
setTimeout(function () {
// 解除防抖
disabled = false
}, 500)
}
},
})
}
}
}
var addBtn = document.querySelector('.add-to-cart')
addBtn.addEventListener('click', function () {
animaStar();
})
</script>
可以封装成一个添加方法哈,待更新…