# 动画案例
整理一些日常用到的一些动画,以便想用的时候随时可以取用。当然更多的可以参考animate.css
# 1、数字变化的动画
如下所示
0
1000
$1234567.89
/*
* 数字变化动画函数
* @param {Object} options - 配置选项
* @param {number} options.from - 起始值
* @param {number} options.to - 目标值
* @param {number} [options.duration=1000] - 动画时长(毫秒)
* @param {function} options.onUpdate - 每帧更新回调 (formattedValue, rawValue) => {}
* @param {function} [options.formatter] - 数字格式化函数
* @param {string} [options.easing='easeInOutQuad'] - 缓动函数
* @returns {function} cancel - 取消动画函数
*/
function animateNumber({
from,
to,
duration = 1000,
onUpdate,
formatter = (n) => n.toLocaleString('zh-CN', {
maximumFractionDigits: 2
}),
easing = 'easeInOutQuad'
}) {
const startValue = Number(from);
const endValue = Number(to);
const startTime = performance.now();
let rafId = null;
// 缓动函数库
const easingFunctions = {
linear: t => t,
easeIn: t => t * t,
easeOut: t => t * (2 - t),
easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
easeOutCubic: t => 1 + Math.pow(t - 1, 3),
};
const ease = easingFunctions[easing] || easingFunctions.easeInOutQuad;
function update(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const easedProgress = ease(progress);
// 计算当前值(保留精度)
const current = startValue + (endValue - startValue) * easedProgress;
// 格式化并回调
if (onUpdate) {
onUpdate(formatter(current), current);
}
if (progress < 1) {
rafId = requestAnimationFrame(update);
} else {
// 确保最终值准确
if (onUpdate) {
onUpdate(formatter(endValue), endValue);
}
}
}
rafId = requestAnimationFrame(update);
// 返回取消函数
return () => {
if (rafId) {
cancelAnimationFrame(rafId);
rafId = null;
}
};
}
// 使用示例
const cancel = animateNumber({
from: 0,
to: 1000,
duration: 2000,
onUpdate: (formatted, raw) => {
document.getElementById('num').textContent = formatted;
console.log(raw); // 原始值,可用于其他逻辑
},
// formatter: (n) => n.toLocaleString('zh-CN', {
// minimumFractionDigits: 2,
// maximumFractionDigits: 2
// }),
formatter: n => n.toFixed(0),
});
// 2秒后自动取消(如果动画未结束)
// setTimeout(cancel, 2000);
// 负数变化示例
animateNumber({
from: 1000,
to: -1000,
duration: 1500,
onUpdate: (formatted) => {
document.getElementById('counter-negative').textContent = formatted;
},
formatter: (n) => {
const abs = Math.abs(n);
return (n < 0 ? '-' : '') + abs.toLocaleString('zh-CN');
}
});
// 大数字示例(带货币格式)
animateNumber({
from: 1234567.89,
to: 9876543.21,
duration: 3000,
easing: 'easeOutCubic',
onUpdate: (formatted) => {
document.getElementById('money').textContent = '$' + formatted;
},
formatter: (n) => n.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
});
下面是自己瞎写的, 可能不是很好用
function changeNum(config, cb = () => {}) {
let { from, to, duration = 1000 } = config;
if (from === to) {
cb({
data: to,
done: true
});
return;
}
const step = Math.floor((to - from) / 20);
const direction = to - from >= 0; // true 表示正向
let start = undefined;
cb({
data: from,
done: false
});
function callback(timestamp) {
if (start === undefined) {
start = timestamp;
}
let elapsed = timestamp - start;
if (elapsed >= duration) {
cb({
data: to,
done: true
});
return;
}
let num;
if (direction) {
num = Math.max(Math.floor(Math.random() * step), 1);
} else {
num = Math.min(Math.floor(Math.random() * step), -1);
}
from = from + num;
if ((direction && from >= to) || (!direction && from <= to)) {
from = to;
}
if (from === to) {
console.log(elapsed);
cb({
data: from,
done: true
});
return;
}
cb({
data: from,
done: false
});
window.requestAnimationFrame(callback);
}
window.requestAnimationFrame(callback);
}
用法
changeNum(
{
from: 0,
to: 1000
},
param => {
el.textContent = param.data.toString();
}
);
# 2、shake 摇动
主要用于点击某些按钮需要摇晃一下。
.shake {
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
transform: translate3d(0, 0, 0);
}
@keyframes shake {
10%,
90% {
transform: translate3d(-1px, 0, 0);
}
20%,
80% {
transform: translate3d(2px, 0, 0);
}
30%,
50%,
70% {
transform: translate3d(-4px, 0, 0);
}
40%,
60% {
transform: translate3d(4px, 0, 0);
}
}
# 3、点击效果
尤其是移动端,可以稍微用一下。不过大部分时候还是用button的伪类来做好一点
.clickable:active {
transform: translateY(2px);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
background-color: #f8f9fa;
}