闭包在动画补间中用于封装起始值、目标值及状态,确保各动画实例隔离;可通过工厂函数、对象配置、class等方式实现,核心是函数捕获定义时的词法环境。

在动画补间函数中使用闭包,核心是让每次启动动画时,把初始值(如起始位置)和目标值(如结束位置)“封存”进函数内部,避免被后续调用覆盖或污染。闭包不是语法糖,而是函数与其词法环境的组合——只要补间函数能访问定义时的变量,就能稳定维持状态。
用闭包封装起始与目标值
最直接的方式是写一个工厂函数,接收 from 和 to,返回一个可被 requestAnimationFrame 反复调用的更新函数:
function createTween(from, to) {
const start = from;
const end = to;
let t = 0; // 当前进度(0~1)
<p>return function update() {
t += 0.02; // 简单线性推进
if (t > 1) t = 1;</p><pre class="brush:php;toolbar:false;">const current = start + (end - start) * t;
return current; // 返回当前插值结果}; }
// 使用示例:从 100px 补间到 300px const tweenX = createTween(100, 300); function animate() { const x = tweenX(); element.style.left = x + 'px'; if (x
这里 start 和 end 被闭包捕获,即使 createTween 执行完毕,update 仍能安全读取它们——每个动画实例彼此隔离,互不干扰。
支持多属性、带 easing 的闭包封装
实际项目中常需同时控制多个属性(如 x、y、opacity),且需要缓动效果。可扩展为对象式配置:
function createMultiTween(config) {
const { from, to, ease = t => t } = config;
const keys = Object.keys(from);
const state = { t: 0 };
<p>return function update() {
state.t = Math.min(state.t + 0.02, 1);
const progress = ease(state.t);</p><pre class="brush:php;toolbar:false;">const result = {};
for (const key of keys) {
result[key] = from[key] + (to[key] - from[key]) * progress;
}
return result;}; }
// 启动一个带 easeOutCubic 的位移+透明度动画 const tween = createMultiTween({ from: { x: 50, y: 50, opacity: 0 }, to: { x: 200, y: 150, opacity: 1 }, ease: t => 1 - Math.pow(1 - t, 3) });
所有 from、to、ease 和内部 state 都被闭包保护。不同动画调用 createMultiTween 会生成各自独立的状态空间。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
避免常见陷阱:不要在循环中直接创建闭包引用
下面写法看似简洁,实则危险:
// ❌ 错误:i 在循环结束后才被读取,所有闭包共享同一个 i for (let i = 0; i console.log(i)); // 全部输出 3 } <p>// ✅ 正确:用立即执行或 let 块级作用域(现代 JS 推荐) for (let i = 0; i console.log(i)); // 各自输出 0、1、2 }</p>
在批量初始化动画时,确保每个闭包捕获的是当时确定的数值,而非变量引用。使用 let 声明循环变量即可满足,无需额外 IIFE。
结合 class 封装更易复用
当逻辑变复杂(如支持暂停、重置、回调),用 class 显得更清晰,但本质仍是闭包驱动:
class Tween {
constructor({ from, to, duration = 300, easing = t => t }) {
this.from = { ...from };
this.to = { ...to };
this.duration = duration;
this.easing = easing;
this.startTime = null;
this.isRunning = false;
}
<p>start() {
this.startTime = performance.now();
this.isRunning = true;
this.tick();
}</p><p>tick = (now = performance.now()) => {
if (!this.isRunning) return;</p><pre class="brush:php;toolbar:false;">const elapsed = now - this.startTime;
const t = Math.min(elapsed / this.duration, 1);
const easedT = this.easing(t);
const result = {};
for (const key in this.from) {
result[key] = this.from[key] + (this.to[key] - this.from[key]) * easedT;
}
this.onUpdate?.(result);
if (t <p>}</p><p>onUpdate(callback) { this.onUpdate = callback; return this; }
onComplete(callback) { this.onComplete = callback; return this; }
}</p><p>// 使用
new Tween({ from: { x: 0 }, to: { x: 400 } })
.onUpdate(({ x }) => el.style.transform = <code>translateX(${x}px)</code>)
.onComplete(() => console.log('done'))
.start();</p>虽然用了 class,但 this.from、this.to、this.easing 等仍靠闭包(方法内自由变量)维持状态;tick 是箭头函数,自动绑定 this,也隐含了对实例属性的闭包引用。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










