前言
在电商类 App 中,加入购物车的动画效果是提升用户体验的重要细节。
本文将详细介绍如何使用原生 HTML、CSS 和 JavaScript 实现类似美团的购物车小球动画效果,包括商品展示、数量统计和抛物线动画等核心功能。
从其他地方看见类似的美团购物车小球动画效果实现。我也试着做一下(AI生成)。
以下是代码效果:


功能需求分析
我们需要实现的核心功能包括:
- 展示四种商品,每种商品包含图片 (用圆形代替)、名称和价格
- 右下角固定显示购物车图标及商品总数
- 购物车旁提供重置按钮,用于清空购物车数量
- 点击商品时,生成一个小球从点击位置沿抛物线飞入购物车
- 页面需适配移动端和 PC 端
技术实现方案
1. 页面结构设计
首先,我们需要构建基础的 HTML 结构,主要包含三个部分:
- 页面头部:展示标题
- 商品列表区:使用网格布局展示四种商品
- 购物车区域:固定在右下角,包含重置按钮、购物车图标和数量显示
<header>
<h1>美团购物车示例</h1>
</header>
<div class="container">
<div class="product-list">
<!-- 商品卡片将在这里 -->
</div>
</div>
<div class="cart-container">
<button class="reset-btn">重置</button>
<div class="cart-icon">
<i class="fas fa-shopping-cart"></i>
<span class="cart-count">0</span>
</div>
</div>
2. 样式设计与响应式布局
商品卡片样式
为了让商品展示美观且一致,我们为商品卡片设计统一的样式:
.product-card {
background-color: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
transition: transform 0.3s ease;
cursor: pointer;
}
.product-card:hover {
transform: translateY(-5px);
}
.product-img {
width: 100%;
height: 180px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f0f0;
}
.product-circle {
width: 120px;
height: 120px;
border-radius: 50%;
}
响应式网格布局
使用 CSS Grid 实现商品列表的响应式布局,在不同屏幕尺寸下自动调整列数:
.product-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}
/* 移动端适配 */
@media (max-width: 768px) {
.product-list {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
}
.product-img {
height: 140px;
}
.product-circle {
width: 90px;
height: 90px;
}
}
购物车样式
将购物车固定在右下角,并设计醒目的样式:
.cart-container {
position: fixed;
bottom: 20px;
right: 20px;
display: flex;
align-items: center;
gap: 15px;
}
.cart-icon {
width: 60px;
height: 60px;
background-color: #ff4d4f;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
position: relative;
box-shadow: 0 4px 12px rgba(255,77,79,0.4);
}
.cart-count {
position: absolute;
top: -10px;
right: -10px;
background-color: #ffad33;
color: white;
width: 26px;
height: 26px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: bold;
}
3. 核心功能实现:购物车动画
数量统计功能
首先实现最基本的购物车数量统计和重置功能:
const productCards = document.querySelectorAll('.product-card');
const cartCount = document.querySelector('.cart-count');
const resetBtn = document.querySelector('.reset-btn');
let count = 0;
// 点击商品添加到购物车
productCards.forEach(card => {
card.addEventListener('click', (e) => {
count++;
cartCount.textContent = count;
// 后续将添加动画调用
});
});
// 重置购物车
resetBtn.addEventListener('click', () => {
count = 0;
cartCount.textContent = count;
});
抛物线动画实现
这是整个效果的核心部分,实现步骤如下:
- 获取起点和终点位置
- 起点:用户点击商品的位置
- 终点:购物车图标的中心位置
// 获取点击位置
const startX = event.clientX;
const startY = event.clientY;
// 获取购物车位置
const cartRect = cartIcon.getBoundingClientRect();
const endX = cartRect.left + cartRect.width / 2;
const endY = cartRect.top + cartRect.height / 2;
- 创建动画小球
// 创建小球元素
const ball = document.createElement('div');
ball.classList.add('add-animation');
// 获取商品圆形的背景色,使小球颜色与商品一致
const circleColor = card.querySelector('.product-circle').style.backgroundColor;
ball.style.backgroundColor = circleColor;
// 设置小球起始位置
ball.style.left = `${startX}px`;
ball.style.top = `${startY}px`;
// 添加到页面
document.body.appendChild(ball);
- 计算贝塞尔曲线控制点
为了实现自然的抛物线效果,我们需要计算贝塞尔曲线的控制点:
// 计算控制点(贝塞尔曲线的控制点)
// 让小球先向上飞一点,再向下落到购物车,形成一个抛物线
const controlX = (startX + endX) / 2;
const controlY = startY - 100; // 向上100px作为控制点
- 执行动画
使用 Web Animations API 的 animate () 方法执行动画:
const animation = ball.animate(
[
// 起始位置
{
transform: 'translate(0, 0) scale(1)',
opacity: 1
},
// 结束位置
{
transform: `translate(${endX - startX}px, ${endY - startY}px) scale(0.3)`,
opacity: 0.6
}
],
{
duration: 800,
easing: `cubic-bezier(0.2, 0.8, 0.2, 1)`, // 使用贝塞尔曲线
fill: 'forwards'
}
);
- 动画结束处理
动画结束后,移除小球元素,并给购物车添加一个缩放反馈:
animation.onfinish = () => {
ball.remove();
// 给购物车添加一个缩放动画,表示添加成功
cartIcon.animate(
[
{ transform: 'scale(1)' },
{ transform: 'scale(1.3)' },
{ transform: 'scale(1)' }
],
{
duration: 300,
easing: 'ease-out'
}
);
};
4. 完整代码实现(直接复制可用)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>美团购物车效果</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial', sans-serif;
}
body {
background-color: #f5f5f5;
padding-bottom: 80px;
}
header {
background-color: #ff4d4f;
color: white;
padding: 16px;
text-align: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.product-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}
.product-card {
background-color: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
transition: transform 0.3s ease;
cursor: pointer;
}
.product-card:hover {
transform: translateY(-5px);
}
.product-img {
width: 100%;
height: 180px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f0f0;
}
.product-circle {
width: 120px;
height: 120px;
border-radius: 50%;
}
.product-info {
padding: 16px;
}
.product-name {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
color: #333;
}
.product-price {
color: #ff4d4f;
font-size: 18px;
font-weight: bold;
}
.cart-container {
position: fixed;
bottom: 20px;
right: 20px;
display: flex;
align-items: center;
gap: 15px;
}
.cart-icon {
width: 60px;
height: 60px;
background-color: #ff4d4f;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
position: relative;
box-shadow: 0 4px 12px rgba(255,77,79,0.4);
}
.cart-count {
position: absolute;
top: -10px;
right: -10px;
background-color: #ffad33;
color: white;
width: 26px;
height: 26px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: bold;
}
.reset-btn {
background-color: white;
border: 1px solid #ddd;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.reset-btn:hover {
background-color: #f5f5f5;
}
.add-animation {
position: fixed;
width: 30px;
height: 30px;
border-radius: 50%;
z-index: 9999;
pointer-events: none;
}
@media (max-width: 768px) {
.product-list {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
}
.product-img {
height: 140px;
}
.product-circle {
width: 90px;
height: 90px;
}
.cart-icon {
width: 50px;
height: 50px;
font-size: 20px;
}
.cart-count {
width: 22px;
height: 22px;
font-size: 12px;
}
}
</style>
</head>
<body>
<header>
<h1>美团购物车示例</h1>
</header>
<div class="container">
<div class="product-list">
<!-- 商品1 -->
<div class="product-card" data-price="19.9">
<div class="product-img">
<div class="product-circle" style="background-color: #4CAF50;"></div>
</div>
<div class="product-info">
<div class="product-name">新鲜有机蔬菜</div>
<div class="product-price">¥19.9</div>
</div>
</div>
<!-- 商品2 -->
<div class="product-card" data-price="29.9">
<div class="product-img">
<div class="product-circle" style="background-color: #2196F3;"></div>
</div>
<div class="product-info">
<div class="product-name">精选水果拼盘</div>
<div class="product-price">¥29.9</div>
</div>
</div>
<!-- 商品3 -->
<div class="product-card" data-price="39.9">
<div class="product-img">
<div class="product-circle" style="background-color: #FF9800;"></div>
</div>
<div class="product-info">
<div class="product-name">进口牛奶</div>
<div class="product-price">¥39.9</div>
</div>
</div>
<!-- 商品4 -->
<div class="product-card" data-price="49.9">
<div class="product-img">
<div class="product-circle" style="background-color: #E91E63;"></div>
</div>
<div class="product-info">
<div class="product-name">全麦面包</div>
<div class="product-price">¥49.9</div>
</div>
</div>
</div>
</div>
<div class="cart-container">
<button class="reset-btn">重置</button>
<div class="cart-icon">
<i class="fas fa-shopping-cart"></i>
<span class="cart-count">0</span>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const productCards = document.querySelectorAll('.product-card');
const cartCount = document.querySelector('.cart-count');
const resetBtn = document.querySelector('.reset-btn');
const cartIcon = document.querySelector('.cart-icon');
let count = 0;
// 点击商品添加到购物车
productCards.forEach(card => {
card.addEventListener('click', (e) => {
count++;
cartCount.textContent = count;
// 创建动画小球
createAddToCartAnimation(e, card);
});
});
// 重置购物车
resetBtn.addEventListener('click', () => {
count = 0;
cartCount.textContent = count;
});
// 创建加入购物车的动画
function createAddToCartAnimation(event, card) {
// 获取点击位置
const startX = event.clientX;
const startY = event.clientY;
// 获取购物车位置
const cartRect = cartIcon.getBoundingClientRect();
const endX = cartRect.left + cartRect.width / 2;
const endY = cartRect.top + cartRect.height / 2;
// 创建小球元素
const ball = document.createElement('div');
ball.classList.add('add-animation');
// 获取商品圆形的背景色
const circleColor = card.querySelector('.product-circle').style.backgroundColor;
ball.style.backgroundColor = circleColor;
// 设置小球起始位置
ball.style.left = `${startX}px`;
ball.style.top = `${startY}px`;
// 添加到页面
document.body.appendChild(ball);
// 计算控制点(贝塞尔曲线的控制点)
// 我们让小球先向上飞一点,再向下落到购物车,形成一个抛物线
const controlX = (startX + endX) / 2;
const controlY = startY - 100; // 向上100px作为控制点
// 创建动画
const animation = ball.animate(
[
// 起始位置
{
transform: 'translate(0, 0) scale(1)',
opacity: 1
},
// 结束位置
{
transform: `translate(${endX - startX}px, ${endY - startY}px) scale(0.3)`,
opacity: 0.6
}
],
{
duration: 800,
easing: `cubic-bezier(0.2, 0.8, 0.2, 1)`, // 使用贝塞尔曲线
fill: 'forwards'
}
);
// 动画结束后移除小球元素
animation.onfinish = () => {
ball.remove();
// 给购物车添加一个缩放动画,表示添加成功
cartIcon.animate(
[
{ transform: 'scale(1)' },
{ transform: 'scale(1.3)' },
{ transform: 'scale(1)' }
],
{
duration: 300,
easing: 'ease-out'
}
);
};
}
});
</script>
</body>
</html>
技术要点总结
- 响应式设计:使用 CSS Grid 和媒体查询实现不同屏幕尺寸的适配
- Web Animations API:利用原生 animate () 方法实现流畅的动画效果
- 贝塞尔曲线:通过 cubic-bezier 函数创建自然的抛物线运动轨迹
- 事件处理:监听点击事件实现交互功能
- DOM 操作:动态创建和移除小球元素,实现动画效果
这个购物车动画效果虽然看似简单,但涉及了多个前端知识点的综合应用。通过这种细节优化,可以显著提升用户体验,让整个购物流程更加生动有趣。
可以根据实际需求进一步扩展这个基础版本,比如添加商品总价计算、购物车详情展示等功能。

451





