[Pixi.js] Simple fade-in and fade-out effects for Sprites
Tags: pixijs, gamedev, javascript
Published: 2022-11-12 09:34:55

In my recent Pixi.js project, I developed a custom Sprite class that supports simple fade-in and fade-out effects using the shared ticker. This is helpful when you want your Sprites to enter or exit the scene more smoothly. 

Here is how I implemented it:

## 1. Extend the original PIXI.Sprite
First, let's create a custom Sprite class that contains two new methods:`fadeIn` and `fadeOut`.

```js
import { Sprite } from 'pixi.js';

class CustomSprite extends Sprite {
  fadeIn() {
    ...
  }

  fadeOut() {
    ...
  }
}

export default CustomSprite
```

## 2. Transitoning alpha value using shared Ticker
Smooth transition (and all kinds of animation) is often done by repeatedly increase or decrease some values in fast enough frequency (e.g. 24 frames per seconds or FPS). 

Of course, you can achieve this by `setInterval`. But there's a more native way to do this in Pixi.js. That is, the PIXI.Ticker.

```js
fadeIn() {
  this.alpha = 0;
  
  const ticker = PIXI.Ticker.shared;
  
  // declare a ticker listener
  const onTick = () => {
    // updates the alpha value little by little
    this.alpha += 0.01;
    
    // remove this listener once alpha reaches 1
    if (this.alpha > 1) {
      ticker.remove(onTick);
    }
  }
  
  // register the ticker listener
  ticker.add(onTick)
}
```

As you see, if `fadeIn` is called, the Sprite will increase its alpha by 0.01 in every tick untill alpha is greater than 1. But this is not good enough as we can't specify the transition duration.


## 3. Understand how ticker works
Before continuing improving our code, we first need to know **how often** does the ticker ticks, so that we can calculate how much to tweak the value in each tick. Right?

For example, if the ticker runs 10 times per second evenly and you wish the transion duration to be 2 seconds, then you should increase the alpha value by 0.05 in each tick.

However, there's no guarantee that Pixi Ticker would tick exactly at the same speed or in a specific frequency (even if you have set `PIXI.settings.TARGET_FPMS` somewhere). Some ticks just take longer than another when the browser is doing heavier work during these periods.

Hopefully, a ticker's listener is passed with a `deltaTime` paramater telling you how much time has passed since the last tick. This is how `deltaTime` is calculated before passing to a ticker listener:

```js
// https://github.com/pixijs/pixijs/blob/dev/packages/ticker/src/Ticker.ts#L411

const deltaTime = deltaMS * PIXI.settings.TARGET_FPMS;
```
Therefor, `deltaTime` roughly means "how many frames have passed" since last tick. This is useful if you prefer to think of timeline in the sense of "frames".

In my case, I like to think in milliseconds rather than frames, so I would revert the conversion like this and calculate the value.

```js
const onTick = (deltaTime) => {
  // how many milliseconds have passed since last tick
  const deltaMS = deltaTime / PIXI.settings.TARGET_FPMS;
  ...
}
```
## 4. Specify a duration and update alpha proportionally
Now that we have gathered the important information about our ticker, we can continue finish our code.

```js
// add a duration (MS) paramater
fadeIn(duration) {
  this.alpha = 0;

  const ticker = PIXI.Ticker.shared;

  const onTick = (deltaTime) => {
    const deltaMS = deltaTime / PIXI.settings.TARGET_FPMS;
    
    // increase the alpha proportionally
    this.alpha += deltaMS / duration;

    if (this.alpha > 1) {
      ticker.remove(onTick);
    }
  }

  ticker.add(onTick)
}
```

As for `fadeOut`, everything stays the same except for the direction you change the alpha value.

## 5. Put it into use
Now you can create sprites with the customized Sprite class to enjoy fade-in and fade-out effects easily.

```js
const bunny = new MySprite(bunnyTexture);

bunny.alpha = 0;
app.stage.addChild(bunny);

...

// fade in to the stage with a 3-second transition duration
bunny.fadeIn(3000);
```

[Check the example code here.](https://codesandbox.io/s/affectionate-mcnulty-kyhpvz?file=/src/index.js)