面向对象动画的基本封装思路
面向对象动画的核心是将动画相关的属性(如元素、目标位置、速度)和方法(如开始、暂停、更新状态)封装到一个类中,通过实例化类来创建独立的动画实例。这种方式可以让多个动画实例互不干扰,方便管理不同的动画逻辑。

基础的动画类需要包含以下核心部分:
- 构造函数:初始化动画元素、起始位置、目标位置、动画速度等属性
- 更新方法:每帧计算元素当前位置,修改元素的样式实现移动效果
- 开始方法:启动动画循环,通常使用requestAnimationFrame实现帧更新
this上下文在动画中的常见问题
在动画类中,我们通常会把更新方法作为requestAnimationFrame的回调传入,这时候就会出现this指向异常的问题。因为requestAnimationFrame的回调执行时,this默认指向window对象,而不是动画类的实例,导致无法访问实例的属性。
比如下面这段代码就会出现问题:
class Animate {
constructor(element, target) {
this.element = element;
this.target = target;
this.current = parseInt(getComputedStyle(element).left) || 0;
}
update() {
// 这里this会指向window,无法访问this.current和this.element
if (this.current < this.target) {
this.current += 2;
this.element.style.left = this.current + 'px';
requestAnimationFrame(this.update);
}
}
start() {
requestAnimationFrame(this.update);
}
}
const box = document.querySelector('.box');
const animate = new Animate(box, 300);
animate.start(); // 执行后会报错,提示this.current是undefined
正确处理this上下文的几种方式
1. 使用箭头函数绑定this
箭头函数没有自己的this,它的this会继承外层作用域的this,在类的方法中使用箭头函数作为回调,就可以保证this指向类的实例。
修改上面的代码,把start方法中的回调改成箭头函数:
class Animate {
constructor(element, target) {
this.element = element;
this.target = target;
this.current = parseInt(getComputedStyle(element).left) || 0;
}
update() {
if (this.current < this.target) {
this.current += 2;
this.element.style.left = this.current + 'px';
// 这里使用箭头函数,this指向animate实例
requestAnimationFrame(() => this.update());
}
}
start() {
// 这里的this是animate实例,箭头函数继承这个this
requestAnimationFrame(() => this.update());
}
}
const box = document.querySelector('.box');
const animate = new Animate(box, 300);
animate.start(); // 动画正常执行
2. 使用bind方法显式绑定this
可以在构造函数中使用bind把update方法的this绑定到实例上,这样不管update以什么方式调用,this都会指向实例。
class Animate {
constructor(element, target) {
this.element = element;
this.target = target;
this.current = parseInt(getComputedStyle(element).left) || 0;
// 绑定this到当前实例
this.update = this.update.bind(this);
}
update() {
if (this.current < this.target) {
this.current += 2;
this.element.style.left = this.current + 'px';
requestAnimationFrame(this.update);
}
}
start() {
requestAnimationFrame(this.update);
}
}
const box = document.querySelector('.box');
const animate = new Animate(box, 300);
animate.start(); // 动画正常执行
3. 在调用时临时绑定this
也可以在把update作为回调传入的时候,使用bind临时绑定this,这种方式不需要在构造函数中提前处理。
class Animate {
constructor(element, target) {
this.element = element;
this.target = target;
this.current = parseInt(getComputedStyle(element).left) || 0;
}
update() {
if (this.current < this.target) {
this.current += 2;
this.element.style.left = this.current + 'px';
requestAnimationFrame(this.update.bind(this));
}
}
start() {
requestAnimationFrame(this.update.bind(this));
}
}
const box = document.querySelector('.box');
const animate = new Animate(box, 300);
animate.start(); // 动画正常执行
完整可用的面向对象动画类示例
下面是一个支持配置速度、方向,并且正确处理this上下文的完整动画类:
class Animation {
constructor(options) {
this.element = options.element;
this.target = options.target;
this.speed = options.speed || 2;
this.axis = options.axis || 'left'; // 支持left、top等方向
this.current = parseInt(getComputedStyle(this.element)[this.axis]) || 0;
// 绑定update的this到实例
this.update = this.update.bind(this);
}
update() {
const diff = this.target - this.current;
// 判断是否到达目标位置
if (Math.abs(diff) < this.speed) {
this.element.style[this.axis] = this.target + 'px';
return;
}
// 根据方向调整当前位置
this.current += diff > 0 ? this.speed : -this.speed;
this.element.style[this.axis] = this.current + 'px';
requestAnimationFrame(this.update);
}
start() {
requestAnimationFrame(this.update);
}
stop() {
// 取消动画帧,停止动画
cancelAnimationFrame(this.animationId);
}
}
// 使用示例
const box1 = document.querySelector('.box1');
const box2 = document.querySelector('.box2');
const animate1 = new Animation({
element: box1,
target: 300,
speed: 3,
axis: 'left'
});
const animate2 = new Animation({
element: box2,
target: 200,
speed: 2,
axis: 'top'
});
animate1.start();
animate2.start();
注意事项
在使用面向对象动画时,还需要注意以下几点:
- 如果动画元素被移除,需要及时停止动画,避免不必要的性能消耗
- 多个属性动画可以扩展类的方法,比如同时修改left和opacity,只需在update方法中同时处理两个属性的计算
- 如果需要兼容低版本浏览器,可以用setTimeout模拟requestAnimationFrame,但需要注意this绑定的逻辑同样适用
处理this上下文的核心是明确回调函数的执行环境,根据场景选择合适的绑定方式,就可以避免大部分上下文异常的问题。
JavaScript面向对象动画this上下文动画封装修改时间:2026-06-27 03:33:33