抛物线动画不能只靠 translate() 线性过渡,因水平匀速与垂直变速需协同;纯 css 需用 cubic-bezier() 统一控制 xy 位移,js 方案则通过 t(1−t) 实现拱形轨迹,并注意坐标对齐与渲染优化。

抛物线动画为什么不能只靠 transform: translate() 线性过渡
因为抛物线是二维运动:水平匀速 + 垂直先慢后快(或先快后慢),而单纯用 translateX 和 translateY 分别加 ease-in-out 无法协同出真实抛物轨迹——两个方向的缓动节奏不同步,视觉上会“甩出去”或“塌腰”。必须用贝塞尔曲线统一控制整个位移路径,或者用 JavaScript 动态计算每帧坐标。
用 @keyframes + cubic-bezier() 模拟近似抛物线(最简方案)
不需要 JS,适合按钮点击后从商品卡片飞向右上角购物车图标。关键是把 XY 位移合并进一个 transform: translate(x, y),再用自定义贝塞尔函数控制整体节奏:
```css
@keyframes cart-fly {
0% {
opacity: 1;
transform: translate(0, 0);
}
100% {
opacity: 0;
transform: translate(200px, -150px); /* 实际值按布局调整 */
}
}
.fly-item {
animation: cart-fly 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
```
这里 cubic-bezier(0.34, 1.56, 0.64, 1) 是关键:第二个参数 1.56 > 1 让动画在中间加速明显,模拟下坠感;第四个参数为 1 保证终点不回弹。注意:forwards 必须加,否则元素会闪回原位。
- 购物车图标位置要固定(比如
position: fixed; top: 20px; right: 20px),才能让translate目标可预期 - 动画元素需脱离文档流(
position: absolute或fixed),否则会影响其他商品布局 - 如果页面滚动,用
getBoundingClientRect()算起点时得转成视口坐标,纯 CSS 方案此时会偏移
用 JavaScript 精确控制抛物线(适配滚动/响应式场景)
当商品在可视区任意位置、购物车图标也随滚动变化时,必须用 JS 实时计算轨迹。核心是实现物理抛物线公式 y = a·x² + b·x + c,但更稳妥的是用「三阶贝塞尔锚点」插值,或直接分帧更新 transform:
```js
function launchToCart(el, cartIcon) {
const rect1 = el.getBoundingClientRect();
const rect2 = cartIcon.getBoundingClientRect();
const dx = rect2.left - rect1.left;
const dy = rect2.top - rect1.top;
const duration = 600;
<p>const flyEl = el.cloneNode(true);
flyEl.style.cssText = <code> position: fixed; left: ${rect1.left}px; top: ${rect1.top}px; pointer-events: none; z-index: 1000; </code>;
document.body.appendChild(flyEl);</p><p>const startTime = performance.now();
function animate(now) {
const elapsed = now - startTime;
const t = Math.min(elapsed / duration, 1);
// 抛物线参数:t=0→1,y 轴加个弧度(t <em> (1-t) 是钟形权重)
const x = dx </em> t;
const y = dy <em> t - 80 </em> t * (1 - t); // 80 是弧度强度,可调
flyEl.style.transform = <code>translate(${x}px, ${y}px)</code>;
if (t </p><pre class="brush:language-;toolbar:false;">
这个方案里,t * (1 - t) 项就是抛物线拱形的核心——它在 t=0.5 时最大,让飞行路径自然上扬再下压。比用 sin() 或 quadratic 更易控弧度。
- 务必用
requestAnimationFrame,不用setTimeout,否则动画卡顿 -
cloneNode后要移除pointer-events,避免遮挡下方操作 - 如果购物车图标有缩放/旋转,记得在终点对
flyEl补上对应transform
动画卡顿、闪动或定位偏移的三个高频原因
这类动画失败,八成不是逻辑错,而是渲染层没对齐:
- 起点/终点用了
offsetTop/Left(含边框、滚动条影响),应统一改用getBoundingClientRect() - 父容器有
transform(如scale(0.9)),会导致子元素position: fixed错位,此时起点要用getClientRects()[0]并手动补偿 - 动画中修改了
top/left(触发重排),必须只动transform和opacity(仅重绘)
真正难的不是写出抛物线,而是让每个像素都落在用户滚动、缩放、暗色模式切换后的正确位置上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











