在页面交互设计中,卡片悬浮时的弹性回弹效果能大幅提升用户的操作感知,让界面更有活力。传统的CSS transition或animation很难模拟真实的弹簧物理运动,而spring动画函数可以基于物理参数生成自然的弹性过渡曲线,完美适配这类需求。
spring动画函数核心原理
spring动画函数本质是基于胡克定律模拟弹簧运动的工具函数,核心参数包含质量、刚度、阻尼、初始速度等,不同参数组合会产生不同的回弹效果。常见的前端spring函数实现会返回随时间变化的进度值,我们可以将这个值应用到CSS属性的过渡中。
核心参数说明
- mass:弹簧质量,值越大回弹越迟缓
- stiffness:弹簧刚度,值越大回弹力度越强
- damping:阻尼系数,值越大回弹次数越少,越容易快速稳定
- initialVelocity:初始速度,决定动画起始的运动快慢
基础卡片样式搭建
首先我们先构建一个基础的卡片结构,设置好初始样式,为后续的悬浮动效做准备。
<div class="card"> <h3>示例卡片</h3> <p>这是一个带有弹性回弹效果的悬浮卡片</p> </div>
对应的基础CSS样式如下,设置卡片的尺寸、阴影和圆角,让卡片有基础的立体感:
.card {
width: 300px;
height: 200px;
padding: 20px;
border-radius: 12px;
background-color: #ffffff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
cursor: pointer;
/* 初始变换状态,后续动画会修改这个值 */
transform: translateY(0) scale(1);
}
实现spring动画函数
我们可以自己实现一个简单的spring动画函数,用于生成弹性过渡的进度值,也可以直接使用成熟的动画库提供的函数,这里先展示自定义实现的逻辑:
/**
* spring动画函数
* @param {number} mass 质量
* @param {number} stiffness 刚度
* @param {number} damping 阻尼
* @param {number} initialVelocity 初始速度
* @returns {function} 接收时间参数,返回当前进度值
*/
function spring(mass = 1, stiffness = 100, damping = 10, initialVelocity = 0) {
let velocity = initialVelocity;
let position = 0;
const springForce = (pos) => -stiffness * pos;
const dampingForce = (vel) => -damping * vel;
return function(time) {
// 每帧时间步长为16ms,模拟60fps的帧率
const dt = 0.016;
const steps = Math.floor(time / dt);
for (let i = 0; i < steps; i++) {
const acceleration = (springForce(position) + dampingForce(velocity)) / mass;
velocity += acceleration * dt;
position += velocity * dt;
}
return position;
};
}
绑定卡片悬浮交互
接下来我们将spring动画和卡片的鼠标悬浮事件绑定,当鼠标移入卡片时,卡片向上移动并轻微放大,移出时弹性回弹到初始状态。
首先修改CSS,添加过渡相关的变量,方便JS动态控制:
.card {
/* 之前的样式保持不变 */
--translate-y: 0px;
--scale: 1;
transform: translateY(var(--translate-y)) scale(var(--scale));
}
然后是JS交互逻辑,通过requestAnimationFrame执行动画帧,调用spring函数获取每一帧的进度值,更新卡片的CSS变量:
// 获取卡片元素
const card = document.querySelector('.card');
// 创建spring动画实例,参数可以按需调整
const springAnimation = spring(1, 120, 12, 2);
// 记录动画开始时间
let startTime = null;
// 动画是否正在执行
let isAnimating = false;
// 目标偏移值,移入时向上8px,放大1.05倍
let targetTranslateY = 0;
let targetScale = 1;
// 鼠标移入事件
card.addEventListener('mouseenter', () => {
targetTranslateY = -8;
targetScale = 1.05;
startAnimation();
});
// 鼠标移出事件
card.addEventListener('mouseleave', () => {
targetTranslateY = 0;
targetScale = 1;
startAnimation();
});
function startAnimation() {
if (isAnimating) return;
isAnimating = true;
startTime = performance.now();
// 记录初始的CSS变量值
const startTranslateY = parseFloat(getComputedStyle(card).getPropertyValue('--translate-y'));
const startScale = parseFloat(getComputedStyle(card).getPropertyValue('--scale'));
// 计算需要变化的差值
const translateYDiff = targetTranslateY - startTranslateY;
const scaleDiff = targetScale - startScale;
function animate(currentTime) {
// 计算已经过去的时间,限制最大时长为500ms避免无限执行
const elapsed = Math.min(currentTime - startTime, 500);
// 获取spring函数返回的进度值,范围大致在-1到1之间
const progress = springAnimation(elapsed);
// 将进度值映射到实际的属性变化范围
// 因为spring函数会超出目标值再回弹,所以直接用进度乘以差值即可
const currentTranslateY = startTranslateY + translateYDiff * progress;
const currentScale = startScale + scaleDiff * progress;
// 更新CSS变量
card.style.setProperty('--translate-y', `${currentTranslateY}px`);
card.style.setProperty('--scale', currentScale);
// 当动画接近稳定状态时停止动画
if (elapsed >= 500 || Math.abs(progress) < 0.001) {
isAnimating = false;
// 最终设置为目标值,避免精度问题
card.style.setProperty('--translate-y', `${targetTranslateY}px`);
card.style.setProperty('--scale', targetScale);
return;
}
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
}
参数调优建议
如果想要不同的回弹效果,可以调整spring函数的参数:
- 想要更明显的回弹效果,可以减小damping值,比如设置为8
- 想要更快的稳定速度,可以增大damping值,比如设置为15
- 想要更轻柔的回弹,可以减小stiffness值,比如设置为80
通过合理调整参数,就能做出符合设计需求的卡片悬浮弹性回弹效果,让页面交互更有质感。
CSS动画spring动画函数卡片悬浮效果弹性回弹修改时间:2026-06-22 07:06:57