京东购物车动效实现:贝塞尔曲线的妙用
前言大家好,我是奈德丽。前两天在逛京东想买 Pocket 3 的时候,注意到了它的购物车动效,当点击 "加入购物车" 按钮时,一个小红球从商品飞入购物车,我觉得很有意思,于是花了点时间来研究。
实现效果看了图才知道我在讲什么,那么先看 Gif 吧!
代码演示代码已经上传到了码上掘金,感兴趣的可以自行查看,文章中没有贴全部代码了,主要讲讲思路,code.juejin.cn/pen/7503150…[1]
实现思路下面这个思路,小白也能会,我们将通过以下几个步骤来实现这个效果:
画页面—— 写逻辑实现动画效果
好了,废话不多说,开始进入正题
第一步:先让 AI 帮我们写出来 UI 结构像我们这种工作 1 坤年以上的切图仔,能偷懒当然偷懒啦,这种画页面的活可以丢给 AI 来干了,下面是 Taro 帮我生成的页面部分,没什么难点,就是一些普普通通的页面元素。
template
div class="rolling-ball-container"
!-- 商品列表 --
div class="item-list"
div class="item" v-for="item in 10" :key="item"
div class="product-card"
div class="product-tag"/div
div class="product-image"
img src="/product.jpg" alt="商品图片" /
/div
div class="product-info"
div class="product-title"大疆 DJI Osmo Pocket 3 一英寸口袋云台相机/div
div class="product-features"
span class="feature-tag"三轴防抖/span
span class="feature-tag"防抖稳定/span
span class="feature-tag"高清画质/span
/div
div class="product-price"
span class="price-symbol"/span
span class="price-value"4788/span
span class="price-original"¥4899/span
/div
div class="product-meta"
span class="delivery-time"24分钟达/span
span class="rating"好评率96%/span
/div
div class="product-shop"京东之家-凯德汇新店/div
/div
div class="add-to-cart" @click="startRolling($event)"/div
/div
/div
/div
!-- 购物车图标 --
div class="point end-point"
div style="position: relative;"
img src="/cart.png" /
div class="cart-count"{{ totalCount }}/div
/div
/div
!-- 小球容器 --
div
v-for="(ball, index) in balls"
:key="index"
class="ball"
v-show="ball.show"
:style="getBallStyle(ball)"
/div
/div
/template
第二步:设计小球数据模型有了页面元素了,我们需要创建小球数组和计数器
import { reactive, ref } from 'vue';
// 购物车商品计数
const totalCount = ref(0);
// 创建小球数组(预先创建3个小球以应对连续点击)
const balls = reactive(Array(3).fill(0).map(() = ({
show: false, // 是否显示
startX: 0, // 起点X坐标
startY: 0, // 起点Y坐标
endX: 0, // 终点X坐标
endY: 0, // 终点Y坐标
pathX: 0, // 路径X偏移量
pathY: 0, // 路径Y偏移量
progress: 0 // 动画进度
})));
为什么小球要用一个数组来存储呢?因为我看到京东上用户是可以连续点击 + 号将商品加入购入车的,页面上可以同时存在很多个飞行的小球。
第三步:实现动画触发函数当用户点击 "+" 按钮时,我们需要计算起点和终点坐标,然后启动动画,这儿有一个细节,为了让小球刚好落到在购物车中间,对终点坐标进行了微调。
// 开始滚动动画
const startRolling = (event: MouseEvent) = {
// 获取起点和终点元素
const startPoint = event.currentTarget as HTMLElement;
const endPoint = document.querySelector('.end-point') as HTMLElement;
if (startPoint && endPoint) {
// 找到一个可用的小球
const ball = balls.find(ball = !ball.show);
if (ball) {
// 获取起点位置
const startRect = startPoint.getBoundingClientRect();
ball.startX = startRect.left + startRect.width / 2;
ball.startY = startRect.top + startRect.height / 2;
// 获取终点位置
const endRect = endPoint.getBoundingClientRect();
const endX = endRect.left + endRect.width / 2;
const endY = endRect.top + endRect.height / 2;
// 微调终点位置
ball.endX = endX - 4;
ball.endY = endY - 7;
// 设置路径偏移量
ball.pathX = 0;
ball.pathY = 100;
// 显示小球并重置进度
ball.show = true;
ball.progress = 0;
// 使用requestAnimationFrame实现动画
let startTime = Date.now();
const duration = 400; // 动画持续时间(毫秒)
function animate() {
const currentTime = Date.now();
const elapsed = currentTime - startTime;
ball.progress = Math.min(elapsed / duration, 1);
if (ball.progress 1) {
requestAnimationFrame(animate);
} else {
// 动画结束后隐藏小球
setTimeout(() = {
ball.show = false;
}, 100);
}
}
requestAnimationFrame(animate);
// 增加购物车商品数量
totalCount.value++;
}
}
};
第四步:使用贝塞尔曲线计算小球轨迹点击 "+" 按钮,不能让小球做自由落体运动吧,那是伽利略研究的,你看这自由落体好看嘛,指定不行,要是长这样,那东哥的商城还能卖出去东西吗?Hah
为了不让它自由落体,给它一个向左的偏移量 100px
// 获取小球样式
const getBallStyle = (ball: any) = {
if (!ball.show) return {};
// 使用二次贝塞尔曲线计算路径
const t = ball.progress;
const mt = 1 - t;
// 判断起点和终点是否在同一垂直线上
const isVertical = Math.abs(ball.startX - ball.endX)
// 计算控制点(确保有弧度)
let controlX, controlY;
if (isVertical) {
// 如果在同一垂直线上,向左偏移一定距离
controlX = ball.startX - 100;
controlY = (ball.startY + ball.endY) / 2;
} else {
// 否则使用向左偏移
controlX = (ball.startX + ball.endX) / 2 - 100;
controlY = (ball.startY + ball.endY) / 2 + (ball.pathY || 100);
}
// 二次贝塞尔曲线公式
const x = mt * mt * ball.startX + 2 * mt * t * controlX + t * t * ball.endX;
const y = mt * mt * ball.startY + 2 * mt * t * controlY + t * t * ball.endY;
return {
left: `${x}px`,
top: `${y}px`,
transform: `rotate(${ball.progress * 360}deg)` // 添加旋转效果
};
技术要点解析1. 贝塞尔曲线原理贝塞尔曲线是一种参数化曲线,广泛应用于计算机图形学。二次贝塞尔曲线由三个点定义:起点 P?、控制点 P?和终点 P?。
曲线上任意点的坐标可以通过以下公式计算:
B(t) = (1-t)2P? + 2(1-t)tP? + t2P? (0 ≤ t ≤ 1)
在我们的实现中,通过调整控制点的位置,可以控制曲线的形状,从而实现小球的抛物线运动效果。
2. requestAnimationFrame 的优势与 setTimeout 或 setInterval 相比,requestAnimationFrame 有以下优势:
「性能更好」:浏览器会在最合适的时间(通常是下一次重绘之前)执行回调函数,避免不必要的重绘「节能」:当页面不可见或最小化时,动画会自动暂停,节省 CPU 资源「更流畅」:与显示器刷新率同步,动画更平滑3. 动态计算元素位置我们使用getBoundingClientRect()方法获取元素在视口中的精确位置,这确保了无论页面如何滚动或调整大小,动画始终能准确地从起点到达终点。
总结通过这个小球飞入购物车的动画效果,我们不仅提升了用户体验,还学习了:
如何使用贝塞尔曲线创建平滑动画如何用 requestAnimationFrame 实现高性能动画如何动态计算元素位置如何使用 rem 单位实现移动端适配这个小小的交互设计虽然看起来简单,但能大大提升用户体验,让你的电商网站更加生动有趣。从京东商城的灵感到实际代码实现,我们完成了一个专业级别的交互效果。
恩恩…… 懦夫的味道
AI编程资讯AI Coding专区指南:https://aicoding.juejin.cn/aicoding
点击"阅读原文"了解详情~
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线