# Ticker (/start/canvas-tickers)



Pixi’VN allows you to animate canvas components using tickers.

<Accordions>
  <Accordion title="What is a Ticker?" id="what-is-ticker">
    A ticker is a class that runs on every frame and executes a function. Tickers can be used to animate components, perform transitions, or run any logic that needs to update regularly.
  </Accordion>

  <Accordion title="Differences between Pixi’VN tickers and PixiJS tickers" id="pixijs-ticker-differences">
    Compared to `PixiJS.tickers`, Pixi’VN tickers are classes with a [`fn`](/jsdoc/pixi-vn/index/classes/TickerBase#fn) method that is called every frame. This method is used to animate canvas components. Pixi’VN manages all running tickers, detects when they are no longer needed, and lets you pause, resume, or delete them using various methods.
  </Accordion>
</Accordions>

## Functions [#functions]

To play, pause, or stop a ticker, you must use the functions of [`tickers`](/jsdoc/pixi-vn/index/variables/tickers).
It is important to keep the following behaviors in mind:

* If a ticker does not have any canvas components associated with it, it will be deleted.
* If you <DynamicLink href="/start/canvas-functions#remove">remove a canvas component</DynamicLink>, your alias will be unlinked from the ticker.
* If you add a canvas component with an alias that already exists, the new component will replace the old one. The new component will inherit the tickers of the old component.

<Accordions>
  <Accordion title="Get" id="get">
    To find a ticker, you must use the [`tickers.find`](/jsdoc/pixi-vn/index/interfaces/TickersInterface#find) function.

    <CodeBlockTabs defaultValue="content/labels/start.label.ts">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="content/labels/start.label.ts">
          content/labels/start.label.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="utils/defineAssets.ts">
          utils/defineAssets.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="content/labels/start.label.ts">
        ```ts
        import { newLabel, RotateTicker, showImage, tickers } from "@drincs/pixi-vn";

        export const startLabel = newLabel("start", [
            async () => {
                await showImage("egg_head", "egg_head", {
                    yAlign: 0.5,
                    xAlign: 0.25,
                    anchor: 0.5,
                });
                await showImage("flower_top", "flower_top", {
                    yAlign: 0.5,
                    xAlign: 0.75,
                    anchor: 0.5,
                });
                let tikerId = tickers.add(
                    ["egg_head", "flower_top"],
                    new RotateTicker({}),
                );
                let ticker = tickers.find(tikerId); // [!code focus]
                console.log(ticker);
            },
        ]);
        ```
      </CodeBlockTab>

      <CodeBlockTab value="utils/defineAssets.ts">
        ```ts
        import { Assets } from "@drincs/pixi-vn";

        export async function defineAssets() {
            Assets.add({
                alias: "egg_head",
                src: "https://pixijs.com/assets/eggHead.png",
            });
            Assets.add({
                alias: "flower_top",
                src: "https://pixijs.com/assets/flowerTop.png",
            });
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Accordion>

  <Accordion title="Remove" id="remove">
    To remove a ticker, you must use the [`tickers.remove`](/jsdoc/pixi-vn/index/interfaces/TickersInterface#remove) function.

    <CodeBlockTabs defaultValue="content/labels/start.label.ts">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="content/labels/start.label.ts">
          content/labels/start.label.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="utils/defineAssets.ts">
          utils/defineAssets.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="content/labels/start.label.ts">
        ```ts
        import {
            newLabel,
            RotateTicker,
            showImage,
            storage,
            tickers,
        } from "@drincs/pixi-vn";

        export const startLabel = newLabel("start", [
            async () => {
                await showImage("egg_head", "egg_head", {
                    yAlign: 0.5,
                    xAlign: 0.25,
                    anchor: 0.5,
                });
                await showImage("flower_top", "flower_top", {
                    yAlign: 0.5,
                    xAlign: 0.75,
                    anchor: 0.5,
                });
                let tikerId = tickers.add(
                    ["egg_head", "flower_top"],
                    new RotateTicker({}),
                );
                storage.set("tiker_id", tikerId);
            },
            () => {
                let tikerId = storage.get<string>("tiker_id");
                tikerId && tickers.remove(tikerId); // [!code focus]
            },
        ]);
        ```
      </CodeBlockTab>

      <CodeBlockTab value="utils/defineAssets.ts">
        ```ts
        import { Assets } from "@drincs/pixi-vn";

        export async function defineAssets() {
            Assets.add({
                alias: "egg_head",
                src: "https://pixijs.com/assets/eggHead.png",
            });
            Assets.add({
                alias: "flower_top",
                src: "https://pixijs.com/assets/flowerTop.png",
            });
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <Sandbox template="zglj8q" entry="/src/labels/startLabel.ts,/src/utils/assets-utility.ts" />
  </Accordion>

  <Accordion title="Pause and resume" id="pause-and-resume">
    To pause a ticker, you must use the [`tickers.pause`](/jsdoc/pixi-vn/index/interfaces/TickersInterface#pause) function.

    To resume a paused ticker, you must use the [`tickers.resume`](/jsdoc/pixi-vn/index/interfaces/TickersInterface#resume) function.

    <CodeBlockTabs defaultValue="content/labels/start.label.ts">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="content/labels/start.label.ts">
          content/labels/start.label.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="utils/defineAssets.ts">
          utils/defineAssets.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="content/labels/start.label.ts">
        ```ts
        import {
            narration,
            newLabel,
            RotateTicker,
            showImage,
            tickers,
        } from "@drincs/pixi-vn";

        export const startLabel = newLabel("start", [
            async () => {
                await showImage("egg_head", "egg_head", { align: 0.5, anchor: 0.5 });
                let tikerId = tickers.add(["egg_head"], new RotateTicker({}));
                narration.dialogue = "start";
            },
            () => {
                tickers.pause("egg_head"); // [!code focus]
                narration.dialogue = "pause";
            },
            () => {
                tickers.resume("egg_head"); // [!code focus]
                narration.dialogue = "resume";
            },
        ]);
        ```
      </CodeBlockTab>

      <CodeBlockTab value="utils/defineAssets.ts">
        ```ts
        import { Assets } from "@drincs/pixi-vn";

        export async function defineAssets() {
            Assets.add({
                alias: "egg_head",
                src: "https://pixijs.com/assets/eggHead.png",
            });
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <Sandbox template="ns726n" entry="/src/labels/startLabel.ts,/src/utils/assets-utility.ts" />
  </Accordion>

  <Accordion title="Force completion at step end" id="force-completion-at-step-end">
    When the animation has a goal to reach, such as a destination, we sometimes need the animation to reach the goal before the current `step` ends.

    To do this, you can use the [`tickers.completeOnStepEnd`](/jsdoc/pixi-vn/index/interfaces/TickersInterface#completeonstepend) function.

    <CodeBlockTabs defaultValue="content/labels/start.label.ts">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="content/labels/start.label.ts">
          content/labels/start.label.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="utils/defineAssets.ts">
          utils/defineAssets.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="content/labels/start.label.ts">
        ```ts
        import { narration, newLabel, showImage, tickers } from "@drincs/pixi-vn";

        export const startLabel = newLabel("start", [
            async () => {
                await showImage("egg_head", "egg_head", {
                    yAlign: 0,
                    xAlign: 0,
                    anchor: 0,
                });
                let tikerId = tickers.add(
                    ["egg_head"],
                    new MoveTicker({
                        destination: { x: 1, y: 0, type: "align" },
                        speed: 1,
                    }),
                );
                tikerId && tickers.completeOnStepEnd({ id: tikerId }); // [!code focus]
            },
            () => {
                narration.dialogue = "complete";
            },
        ]);
        ```
      </CodeBlockTab>

      <CodeBlockTab value="utils/defineAssets.ts">
        ```ts
        import { Assets } from "@drincs/pixi-vn";

        export async function defineAssets() {
            Assets.add({
                alias: "egg_head",
                src: "https://pixijs.com/assets/eggHead.png",
            });
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <Sandbox template="7zgqr6" entry="/src/labels/startLabel.ts,/src/utils/assets-utility.ts" />
  </Accordion>

  <Accordion title="Sequence" id="sequence">
    If you want to run a sequence of tickers, you can use the [`tickers.addSequence`](/jsdoc/pixi-vn/index/interfaces/TickersInterface#addsequence) function.

    <CodeBlockTabs defaultValue="content/labels/start.label.ts">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="content/labels/start.label.ts">
          content/labels/start.label.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="utils/defineAssets.ts">
          utils/defineAssets.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="content/labels/start.label.ts">
        ```ts
        import { newLabel, RotateTicker, showImage, tickers } from "@drincs/pixi-vn";

        export const startLabel = newLabel("start", [
            async () => {
                await showImage("egg_head", "egg_head", { anchor: 0.5 });
                let tikerId = tickers.addSequence("egg_head", [
                    new MoveTicker({
                        destination: { x: 0.5, y: 0.5, type: "align" },
                    }),
                    new RotateTicker({ speed: 2, clockwise: false }, 2),
                ]);
            },
        ]);
        ```
      </CodeBlockTab>

      <CodeBlockTab value="utils/defineAssets.ts">
        ```ts
        import { Assets } from "@drincs/pixi-vn";

        export async function defineAssets() {
            Assets.add({
                alias: "egg_head",
                src: "https://pixijs.com/assets/eggHead.png",
            });
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <Sandbox template="k3wj6d" entry="/src/labels/startLabel.ts,/src/utils/assets-utility.ts" />
  </Accordion>
</Accordions>

## Other features [#other-features]

<Accordions>
  <Accordion title="Create your own ticker" id="create-your-own-ticker">
    Creating your own ticker is simple: extend the [`TickerBase`](/jsdoc/pixi-vn/index/classes/TickerBase) class, override the [`fn`](/jsdoc/pixi-vn/index/classes/TickerBase#fn) method, and implement your logic.

    Then, decorate the class with the [`@tickerDecorator`](/jsdoc/pixi-vn/index/functions/tickerDecorator) decorator. The decorator can take a string as the ticker's alias; if not provided, the class name is used.

    For example:

    ```typescript title="canvas/tickers/RotateTicker.ts"
    import {
        canvas,
        Container,
        TickerBase,
        tickerDecorator,
        TickerValue,
    } from "@drincs/pixi-vn";

    @tickerDecorator() // or @tickerDecorator('RotateTicker')
    export default class RotateTicker extends TickerBase<{
        speed?: number;
        clockwise?: boolean;
    }> {
        fn(
            t: TickerValue,
            args: {
                speed?: number;
                clockwise?: boolean;
            },
            aliases: string[],
        ): void {
            let speed = args.speed === undefined ? 0.1 : args.speed;
            let clockwise = args.clockwise === undefined ? true : args.clockwise;
            aliases.forEach((alias) => {
                let component = canvas.find(alias);
                if (component && component instanceof Container) {
                    if (clockwise) component.rotation += speed * t.deltaTime;
                    else component.rotation -= speed * t.deltaTime;
                }
            });
        }
    }
    ```
  </Accordion>

  <Accordion title="Run a ticker" id="run-a-ticker">
    To add a ticker you must use the [`tickers.add`](/jsdoc/pixi-vn/index/interfaces/TickersInterface#add) function.

    <CodeBlockTabs defaultValue="content/labels/start.label.ts">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="content/labels/start.label.ts">
          content/labels/start.label.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="utils/defineAssets.ts">
          utils/defineAssets.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="content/labels/start.label.ts">
        ```ts
        import { newLabel, RotateTicker, showImage, tickers } from "@drincs/pixi-vn";

        export const startLabel = newLabel("start", [
            async () => {
                await showImage("egg_head", "egg_head", {
                    yAlign: 0.5,
                    xAlign: 0.25,
                    anchor: 0.5,
                });
                await showImage("flower_top", "flower_top", {
                    yAlign: 0.5,
                    xAlign: 0.75,
                    anchor: 0.5,
                });
                let tikerId = tickers.add(
                    ["egg_head", "flower_top"],
                    new RotateTicker({}),
                );
            },
        ]);
        ```
      </CodeBlockTab>

      <CodeBlockTab value="utils/defineAssets.ts">
        ```ts
        import { Assets } from "@drincs/pixi-vn";

        export async function defineAssets() {
            Assets.add({
                alias: "egg_head",
                src: "https://pixijs.com/assets/eggHead.png",
            });
            Assets.add({
                alias: "flower_top",
                src: "https://pixijs.com/assets/flowerTop.png",
            });
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <Sandbox template="vfqzch" entry="/src/labels/startLabel.ts,/src/utils/assets-utility.ts" />
  </Accordion>
</Accordions>
