购物车添加商品弹跳小球实现
效果图
演示地址
简述
实现原理是,通过监听+按钮的位置,以及购物车的位置,作为小球的起始和运动的最终位置,但是怎么移动小球呢,我们可以看到,小球的运动轨迹类似抛物线,起始拆分来看就是x,y轴同时进行移动,这里我们可以运用一个技巧,父元素的移动会带动子元素,其中向上抛的部分,可以用贝塞尔曲线实现,下面看看代码。
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
<script src="https://kit.fontawesome.com/4f492bdd9a.js" crossorigin="anonymous"></script>
<link href="style.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="main">
<div class="item">
<img class="cover" src="https://source.unsplash.com/random/800x600?food" alt="">
<div class="item-right">
<div>
<p>山楂</p>
</div>
<div class="item-right-bottom">
<div class="price">18</div>
<div class="step-counter">
<button class="select">选规格</button>
<button class="subtract"><i class="fa-solid fa-minus"></i></button>
<div class="count">0</div>
<button class="plus"><i class="fa-solid fa-plus"></i></button>
</div>
</div>
</div>
</div>
<div class="item">
<img class="cover" src="https://source.unsplash.com/random/800x600?fruit" alt="">
<div class="item-right">
<div>
<p>山楂</p>
</div>
<div class="item-right-bottom">
<div class="price">18</div>
<div class="step-counter">
<button class="select">选规格</button>
<button class="subtract"><i class="fa-solid fa-minus"></i></button>
<div class="count">0</div>
<button class="plus"><i class="fa-solid fa-plus"></i></button>
</div>
</div>
</div>
</div>
<div class="item">
<img class="cover" src="https://source.unsplash.com/random/800x600?snack" alt="">
<div class="item-right">
<div>
<p>山楂</p>
</div>
<div class="item-right-bottom">
<div class="price">18</div>
<div class="step-counter">
<button class="select">选规格</button>
<button class="subtract"><i class="fa-solid fa-minus"></i></button>
<div class="count">0</div>
<button class="plus"><i class="fa-solid fa-plus"></i></button>
</div>
</div>
</div>
</div>
</div>
<div class="buy-car">
<div class="left">
<div class="car-pic"><i class="fa-solid fa-shop"></i><span>0</span></div>
<div class="price">¥18</div>
</div>
</div>
</div>
<script>
let carPic = document.querySelector('.buy-car .left .car-pic')
let {x, y} = carPic.getBoundingClientRect()
let plusList = document.querySelectorAll('.plus')
let subtractList = document.querySelectorAll('.subtract')
let selectList = document.querySelectorAll('.select')
let countSum = document.querySelector('.buy-car .left .car-pic span')
selectList.forEach(select => {
select.addEventListener('click', function (e) {
setTimeout(() => {
select.style.display = 'none';
let stepCounter = select.parentElement;
stepCounter.querySelectorAll('.subtract,.plus,.count').forEach(e => {
e.style.display = 'block'
})
}, 500)
})
})
plusList.forEach(plus => {
plus.addEventListener('click', function (e) {
let parentElement = findStpCounter(e.target);
let ballWarp = document.createElement('div')
ballWarp.classList.add('ball-warp')
ballWarp.innerHTML = `<div class="ball"></div>`
document.body.appendChild(ballWarp)
let clientRect = e.target.getBoundingClientRect()
let moveY = y - clientRect.y
let moveX = x - clientRect.x
ballWarp.style.setProperty('--left', clientRect.x + 'px');
ballWarp.style.setProperty('--top', clientRect.y + 'px');
ballWarp.style.setProperty('--y', moveY + 'px');
ballWarp.style.setProperty('--x', moveX + 'px');
ballWarp.addEventListener('animationend', (e) => {
if (e.animationName === 'moveX') {
let countStr = countSum.innerText
let countNum = parseInt(countStr) + 1;
countSum.innerText = `${countNum}`
}
ballWarp.remove()
})
let count = parentElement.querySelector('.count')
let countStr = count.innerText
let countNum = parseInt(countStr) + 1;
count.innerText = `${countNum}`
})
})
subtractList.forEach(subtract => {
subtract.addEventListener('click', function (e) {
let parentElement = findStpCounter(e.target);
let count = parentElement.querySelector('.count')
let countNum = parseInt(count.innerText)
if (countNum !== 0) {
countNum--;
let countSumNum = parseInt(countSum.innerHTML)
if (countSumNum !==0){
countSum.innerText = `${countSumNum - 1}`
}
}
count.innerText = `${countNum}`
})
})
function findStpCounter(e) {
if (e.classList.contains('step-counter')) {
return e
} else {
return findStpCounter(e.parentElement)
}
}
</script>
</body>
</html>
样式部分
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
}
.container {
overflow-y: hidden;
display: flex;
flex-direction: column;
max-width: 390px;
margin: 0 auto;
border: 1px solid rgba(18, 17, 42, .07);
border-radius: 5px;
box-shadow: 0 4px 24px rgba(128, 128, 128, .1);
height: 100vh;
position: relative;
}
.container .main{
flex: 1;
overflow-y: auto;
padding-bottom: 60px;
}
.item {
display: flex;
gap: 20px;
padding: 20px 10px;
}
.item .cover {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 5px;
flex-shrink: 0;
}
.item-right {
display: flex;
width: 100%;
flex-direction: column;
justify-content: space-between;
}
.item-right p {
font-weight: bold;
}
.item-right-bottom {
display: flex;
justify-content: space-between;
}
.item-right-bottom .price {
color: red;
}
.step-counter {
position: relative;
width: 90px;
}
.step-counter button {
background-color: #2b88f0;
color: white;
outline: none;
border: none;
}
.step-counter .select {
position: absolute;
right: 0;
height: 22px;
padding: 0 15px;
border-radius: 999px;
font-size: 12px;
line-height: 22px;
}
.step-counter .plus, .subtract {
width: 22px;
height: 22px;
border-radius: 50%;
cursor: pointer;
display: none;
}
.step-counter .subtract {
position: absolute;
right: 0;
top: 0;
background-color: #fff;
color: #2b88f0;
border: 1px solid #2b88f0;
animation: sub-left .8s forwards;
opacity: 0;
}
.step-counter .plus {
position: absolute;
right: 0;
top: 0;
}
@keyframes sub-left {
to {
right: calc(100% - 22px);
transform: rotate(360deg);
opacity: 1;
}
}
.step-counter .count {
width: 100%;
display: none;
text-align: center;
}
.buy-car {
background-color: #fff;
z-index: 1000;
position: absolute;
left: 0;
bottom: 0;
max-width: 390px;
margin: 0 auto;
border-top: 1px solid rgba(18, 17, 42, .07);
height: 60px;
width: 100%;
display: flex;
align-items: center;
padding: 0 20px;
justify-content: space-between;
}
.buy-car .left {
flex: 1;
display: flex;
gap: 10px;
}
.buy-car .left .car-pic {
color: #2b88f0;
position: relative;
}
.buy-car .left .car-pic span {
padding: 1px 5px;
border-radius: 999px;
background-color: red;
display: block;
position: absolute;
right: -24px;
top: -12px;
font-size: 10px;
text-align: center;
line-height: 12px;
color: #fff;
}
.buy-car .left .price {
color: red;
}
@keyframes moveY {
to {
transform: translateY(var(--y));
}
}
@keyframes moveX {
to {
transform: translateX(var(--x));
}
}
.ball {
width: 15px;
height: 15px;
border-radius: 50%;
background-color: #2b88f0;
animation: moveY 1s cubic-bezier(0.49, -0.4, 0.75, 0.41);
}
.ball-warp {
position: fixed;
top: var(--top);
left: var(--left);
animation: moveX 1s;
}
分析
在点击+号的时候,会创建一个.ball-warp的元素,这个元素有个ball的子元素,我们给.ball-warp设置moveX的动画,这个动画就是横向的移动,这会带动子元素ball也横向移动。我们通过css变量的方式设置,移动的位置和初始位置。
购物车添加商品弹跳小球实现
https://www.zhaojun.inkhttps://www.zhaojun.ink/archives/buy-car-ball