Phaser 补间动画 #

补间概述 #

补间动画(Tween)是 Phaser 中实现平滑动画效果的核心工具,通过插值计算实现属性的变化。

text
┌─────────────────────────────────────────────────────────────┐
│                    补间动画流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   起始值 ──────────────────────────────────► 目标值        │
│                                                             │
│   x: 0   ──────── 插值计算 ────────►  x: 400              │
│                                                             │
│   时间轴:                                                  │
│   ├── delay: 延迟开始                                      │
│   ├── duration: 动画时长                                   │
│   ├── repeat: 重复次数                                     │
│   ├── yoyo: 往返播放                                       │
│   └── hold: 完成后保持                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

基本补间 #

创建补间 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 1000
});

const tween = this.tweens.add({
  targets: this.player,
  x: 400,
  y: 300,
  alpha: 0.5,
  scaleX: 2,
  scaleY: 2,
  angle: 360,
  duration: 2000,
  ease: 'Power2'
});

补间配置 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 2000,
  ease: 'Power2',
  delay: 0,
  repeat: 0,
  repeatDelay: 0,
  yoyo: false,
  hold: 0,
  paused: false,
  props: {
    x: { value: 400, duration: 1000, ease: 'Power2' },
    y: { value: 300, duration: 2000, ease: 'Bounce.easeOut' }
  }
});

缓动函数 #

内置缓动 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Linear',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Power0',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Power1',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Power2',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Power3',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Power4',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Quad.easeIn',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Quad.easeOut',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Quad.easeInOut',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Cubic.easeOut',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Bounce.easeOut',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Elastic.easeOut',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Back.easeOut',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Sine.easeInOut',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Expo.easeOut',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Circular.easeInOut',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: 'Stepped',
  duration: 1000
});

自定义缓动 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  ease: (t) => t * t,
  duration: 1000
});

const customEase = (t) => {
  return t < 0.5 
    ? 4 * t * t * t 
    : 1 - Math.pow(-2 * t + 2, 3) / 2;
};

this.tweens.add({
  targets: this.player,
  x: 400,
  ease: customEase,
  duration: 1000
});

补间控制 #

播放控制 #

javascript
const tween = this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 2000
});

tween.pause();

tween.resume();

tween.stop();

tween.stop(0);

tween.stop(1);

tween.restart();

tween.complete();

tween.complete(1);

进度控制 #

javascript
const tween = this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 2000
});

tween.seek(0.5);

tween.setProgress(0.5);

const progress = tween.progress;

tween.setElapsed(1000);

const elapsed = tween.elapsed;

时间缩放 #

javascript
const tween = this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 2000
});

tween.setTimeScale(2);

const timeScale = tween.timeScale;

this.tweens.setTimeScale(0.5);

const globalTimeScale = this.tweens.timeScale;

补间属性 #

常用属性 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  y: 300,
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  alpha: 0,
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  scaleX: 2,
  scaleY: 2,
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  angle: 360,
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  rotation: Math.PI * 2,
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  tint: 0xff0000,
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  displayWidth: 100,
  displayHeight: 100,
  duration: 1000
});

相对值 #

javascript
this.tweens.add({
  targets: this.player,
  x: '+=100',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  x: '-=50',
  y: '+=100',
  duration: 1000
});

this.tweens.add({
  targets: this.player,
  scaleX: '*=1.5',
  scaleY: '*=1.5',
  duration: 1000
});

自定义属性 #

javascript
const obj = { value: 0 };

this.tweens.add({
  targets: obj,
  value: 100,
  duration: 1000,
  onUpdate: () => {
    console.log('Value:', obj.value);
  }
});

this.tweens.add({
  targets: this.player,
  props: {
    x: {
      value: 400,
      duration: 1000,
      ease: 'Power2'
    },
    y: {
      value: 300,
      duration: 2000,
      ease: 'Bounce.easeOut',
      delay: 500
    }
  }
});

多目标补间 #

数组目标 #

javascript
this.tweens.add({
  targets: [this.player, this.enemy, this.npc],
  x: 400,
  y: 300,
  duration: 2000
});

const enemies = this.enemies.getChildren();
this.tweens.add({
  targets: enemies,
  alpha: 0,
  duration: 1000
});

交错动画 #

javascript
const items = this.add.group();
for (let i = 0; i < 10; i++) {
  items.create(100 + i * 60, 300, 'item');
}

this.tweens.add({
  targets: items.getChildren(),
  y: 200,
  duration: 500,
  delay: (target, key, value, index) => index * 100
});

this.tweens.add({
  targets: items.getChildren(),
  alpha: 0,
  scale: 0,
  duration: 300,
  delay: this.tweens.stagger(100)
});

循环与重复 #

无限循环 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 1000,
  repeat: -1
});

有限重复 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 1000,
  repeat: 3
});

往返动画 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 1000,
  yoyo: true
});

this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 1000,
  repeat: -1,
  yoyo: true
});

重复延迟 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 1000,
  repeat: 3,
  repeatDelay: 500
});

this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 1000,
  repeat: -1,
  yoyo: true,
  hold: 500
});

补间事件 #

事件回调 #

javascript
this.tweens.add({
  targets: this.player,
  x: 400,
  duration: 2000,
  onStart: (tween, targets) => {
    console.log('Tween started');
  },
  onUpdate: (tween, targets) => {
    console.log('Progress:', tween.progress);
  },
  onComplete: (tween, targets) => {
    console.log('Tween completed');
  },
  onRepeat: (tween, targets) => {
    console.log('Tween repeated');
  },
  onYoyo: (tween, targets) => {
    console.log('Tween yoyo');
  },
  onPause: (tween, targets) => {
    console.log('Tween paused');
  },
  onResume: (tween, targets) => {
    console.log('Tween resumed');
  },
  onLoop: (tween, targets) => {
    console.log('Tween looped');
  },
  onActive: (tween, targets) => {
    console.log('Tween active');
  }
});

时间线 #

创建时间线 #

javascript
const timeline = this.tweens.createTimeline();

timeline.add({
  targets: this.player,
  x: 400,
  duration: 1000
});

timeline.add({
  targets: this.player,
  y: 300,
  duration: 1000
});

timeline.add({
  targets: this.player,
  alpha: 0,
  duration: 500
});

timeline.play();

时间线配置 #

javascript
const timeline = this.tweens.timeline({
  targets: this.player,
  tweens: [
    {
      x: 400,
      duration: 1000
    },
    {
      y: 300,
      duration: 1000
    },
    {
      alpha: 0,
      duration: 500
    }
  ],
  onComplete: () => {
    console.log('Timeline complete');
  }
});

并行补间 #

javascript
const timeline = this.tweens.timeline();

timeline.add({
  targets: this.player,
  x: 400,
  duration: 1000
});

timeline.add({
  targets: this.enemy,
  x: 200,
  duration: 1000,
  offset: 0
});

timeline.play();

时间线控制 #

javascript
timeline.play();

timeline.pause();

timeline.resume();

timeline.stop();

timeline.restart();

timeline.seek(0.5);

const progress = timeline.progress;

补间管理 #

获取补间 #

javascript
const tweens = this.tweens.getAllTweens();

const tweens = this.tweens.getTweensOf(this.player);

const isTweening = this.tweens.isTweening(this.player);

全局控制 #

javascript
this.tweens.pauseAll();

this.tweens.resumeAll();

this.tweens.stopAll();

this.tweens.killTweensOf(this.player);

this.tweens.killAll();

实用动画 #

UI 动画 #

javascript
class UIAnimations {
  static fadeIn(scene, target, duration = 500) {
    target.setAlpha(0);
    scene.tweens.add({
      targets: target,
      alpha: 1,
      duration: duration,
      ease: 'Linear'
    });
  }
  
  static fadeOut(scene, target, duration = 500, destroy = false) {
    scene.tweens.add({
      targets: target,
      alpha: 0,
      duration: duration,
      ease: 'Linear',
      onComplete: () => {
        if (destroy) target.destroy();
      }
    });
  }
  
  static slideIn(scene, target, from, duration = 500) {
    const originalX = target.x;
    const originalY = target.y;
    
    if (from === 'left') target.x = -target.width;
    if (from === 'right') target.x = scene.scale.width + target.width;
    if (from === 'top') target.y = -target.height;
    if (from === 'bottom') target.y = scene.scale.height + target.height;
    
    scene.tweens.add({
      targets: target,
      x: originalX,
      y: originalY,
      duration: duration,
      ease: 'Power2'
    });
  }
  
  static popIn(scene, target, duration = 300) {
    target.setScale(0);
    scene.tweens.add({
      targets: target,
      scaleX: 1,
      scaleY: 1,
      duration: duration,
      ease: 'Back.easeOut'
    });
  }
  
  static shake(scene, target, intensity = 10, duration = 100) {
    const originalX = target.x;
    const originalY = target.y;
    
    scene.tweens.add({
      targets: target,
      x: originalX + intensity,
      y: originalY + intensity,
      duration: duration,
      yoyo: true,
      repeat: 3,
      ease: 'Linear',
      onComplete: () => {
        target.setPosition(originalX, originalY);
      }
    });
  }
}

游戏动画 #

javascript
class GameAnimations {
  static jump(scene, target, height = 100, duration = 500) {
    const originalY = target.y;
    
    scene.tweens.add({
      targets: target,
      y: originalY - height,
      duration: duration / 2,
      ease: 'Quad.easeOut',
      yoyo: true,
      ease: 'Quad.easeIn'
    });
  }
  
  static collect(scene, target, destination, duration = 500) {
    scene.tweens.add({
      targets: target,
      x: destination.x,
      y: destination.y,
      scaleX: 0,
      scaleY: 0,
      duration: duration,
      ease: 'Power2',
      onComplete: () => target.destroy()
    });
  }
  
  static float(scene, target, distance = 10, duration = 1000) {
    scene.tweens.add({
      targets: target,
      y: target.y - distance,
      duration: duration,
      yoyo: true,
      repeat: -1,
      ease: 'Sine.easeInOut'
    });
  }
  
  static pulse(scene, target, minScale = 0.9, maxScale = 1.1, duration = 500) {
    scene.tweens.add({
      targets: target,
      scaleX: maxScale,
      scaleY: maxScale,
      duration: duration,
      yoyo: true,
      repeat: -1,
      ease: 'Sine.easeInOut'
    });
  }
  
  static rotate(scene, target, duration = 2000) {
    scene.tweens.add({
      targets: target,
      angle: 360,
      duration: duration,
      repeat: -1,
      ease: 'Linear'
    });
  }
}

下一步 #

现在你已经掌握了补间动画,接下来学习 粒子系统,了解如何创建炫酷的粒子效果!

最后更新:2026-03-29