Skip to content

Latest commit

 

History

History
1897 lines (1674 loc) · 66.3 KB

README.md

File metadata and controls

1897 lines (1674 loc) · 66.3 KB

AnimationVideo

AnimationVideo is a javascript library to animate objects inside a canvas. The animation can be perfectly synced to music which can even be sought. Everything works without WebGl.

Examples

or in the index.html.

Table of Contents

Installation

Install it for example with

npm i animationvideo

or include the dist/animationvideo.umd.js directly into your project. It's recommended to import each needed component separately. Then you will need to install eases. You can install both together:

npm i animationvideo eases

General overview

The Engine of AnimationVideo will run Scenes that consists of Sprites that can be manipulated with Animations.

How to import

The best way to import components of AnimationVideo is the direct import of the modules.

import Engine from "animationvideo/Engine.mjs";
import Norm from "animationvideo/Scenes/Norm.mjs";
import FastBlur from "animationvideo/Sprites/FastBlur.mjs";
import Image from "animationvideo/Sprites/Image.mjs";
import Forever from "animationvideo/Animations/Forever.mjs";
import ChangeTo from "animationvideo/Animations/ChangeTo.mjs";
import QuadInOut from "eases/quad-in-out";

This is possible with Webpack >= 4 and other packers. For older packers you can use the main package and extract the needed components.

import AnimationVideo from "lib/animationvideo";

const {
    Engine,
    Scenes: {Norm},
    Animations: {Forever, ChangeTo},
    Sprites: {Image, FastBlur},
    Easing: {QuadInOut}
} = AnimationVideo;

For simple web projects you can include the js-file directly and use the global AnimationVideo object.

<script src="dist/animationvideo.umd.js"></script>
<script>
  new AnimationVideo.Engine(document.querySelector("canvas"))
    .switchScene(
      new AnimationVideo.Scenes.Norm({
        reset: () => [
          [
            new AnimationVideo.Sprites.Rect({ clear: true })
          ],
          [
            new AnimationVideo.Sprites.Circle({
              scaleX: 0.5,
              scaleY: 0.5,
              color: "#F00",
              animation: new AnimationVideo.Animations.Forever([
                new AnimationVideo.Animations.ChangeTo(
                  {
                    color: "#00F"
                  },
                  1000
                ),
                new AnimationVideo.Animations.ChangeTo(
                  {
                    color: "#F00"
                  },
                  1000
                )
              ])
            })
          ]
        ]
      })
    )
    .run();
</script>

Other examples are in the index.html.

Example

Here is how the code looks like in an example:

import Engine from "animationvideo/Engine.mjs";
import Norm from "animationvideo/Scenes/Norm.mjs";
import FastBlur from "animationvideo/Sprites/FastBlur.mjs";
import Image from "animationvideo/Sprites/Image.mjs";
import Forever from "animationvideo/Animations/Forever.mjs";
import ChangeTo from "animationvideo/Animations/ChangeTo.mjs";
import QuadInOut from "eases/quad-in-out";

// The Engine runs the scene "Norm"
new Engine({
  // automaticly adjust the size of the canvas
  autoSize: true,
  // set the target canvas
  canvas: document.querySelector("canvas"),
  // The Engine uses the scene "Norm"
  scene: new Norm({
    // load images beforehand
    images() {
      return { imageFile: "https://placekitten.com/400/400" };
    },
    // initialisation of the scene with sprites
    reset() {
      // the scene resets with two layers
      return [
        // first layer consits of a Image
        [
          new Image({
            image: "imageFile", // show image "imageFile" that was loaded before
            norm: true, // scale to full size
            animation: new Forever([
              // start animation
              // scale larger for 10 seconds with a easing
              new ChangeTo(
                {
                  scaleX: 1.3,
                  scaleY: 1.3
                },
                10000,
                QuadInOut
              ),
              // scale back smaller for 10 seconds with a easing
              new ChangeTo(
                {
                  scaleX: 1,
                  scaleY: 1
                },
                10000,
                QuadInOut
              )
            ])
          })
        ],
        // second layer consits of a blur effect
        [
          new FastBlur({
            compositeOperation: "lighter", // make a glow
            gridSize: 10, // the glow has the size of 10 times 10
            // pixel: true,
            darker: 0.5, // turn down the glow
            alpha: 0, // not visible
            animation: [
              // blend in the glow for half a second with easing
              new ChangeTo({ alpha: 1 }, 500, QuadInOut)
            ]
          })
        ]
      ];
    }
  })
}).run(); // start the engine

Test code at codesandbox.io

Engine

The Engine is the foundation of AnimationVideo that runs the system. It's a class that needs to be instantiated with new. The parameter is an object or a canvas.

import Engine from "animationvideo/Engine.mjs";

// general setup
const engine = new Engine(canvasOrOptions);

// init with canvas
const engine = new Engine(document.querySelector("canvas"));

// init with object
const engine = new Engine({
  // automatic scaling of the canvas - default false
  autoSize: false,
  // the canvas that is used by AnimationVideo
  canvas: null,
  // click event will be added to the canvas and send to an audio-scene
  clickToPlayAudio: false,
  // render only every second frame
  // this is intended for mobile to save energy and prevent heating
  // you could combine this with https://github.com/juliangruber/is-mobile
  reduceFramerate: false,
  // the current scene
  scene: null
});

Constructor-option "autoSize"

This feature will auto scale the canvas. This dynamically creates a balance between quality and performance. You can fine tune the parameter. Setting this to false disable the auto-sizing (this is the default setting). Setting this to true enable the auto-sizing with the default values.

import Engine from "animationvideo/Engine.mjs";

const engine = new Engine({
  // automatic scaling of the canvas - default false
  // can be true to set default values
  autoSize: {
    // enable/disable this feature
    enabled: true,
    // Best scaling factor (1 = size of drawable canvas is the size of the visible canvas)
    scaleLimitMin: 1,
    // Worst possible scaling factor
    scaleLimitMax: 8,
    // a value > 1. Larger values change the scale faster.
    scaleFactor: 1.1,
    // function that gets the visible width of the canvas in pixel
    referenceWidth: () => canvas.clientWidth,
    // function that gets the visible height of the canvas in pixel
    referenceHeight: () => canvas.clientHeight,
    // the current scale / the start scale
    currentScale: 1,
    // the time that the system stays at least in the current scale in ms
    waitTime: 800,
    // how many ms the system must be too slow till the scale gets worse (higher)
    offsetTimeLimitUp: 300,
    // how many ms the system must be too fast till the scale gets better (lower)
    offsetTimeLimitDown: 300,
    // the target time - how much time a frame of the main loop should take
    offsetTimeTarget: 1000 / 60,
    // if the time of a frame of the main loop is different than the "offsetTimeTarget",
    // it must be greater than "offsetTimeDelta" to be registered from the system
    offsetTimeDelta: 3,
    // adds events to recalculate the canvas size when the window
    // resizes/orientation changes
    registerResizeEvents: true,
    // adds events to disable the auto-size-system if the window/tab losses focus
    registerVisibilityEvents: true,
    // sets canvas width and height by setting the style attribute of the canvas
    setCanvasStyle: false,
    // start values for the waitTime and the offsetTime
    currentWaitedTime: 0,
    currentOffsetTime: 0
  },
  // don't forget to set canvas, scene...
  canvas: null //...
});

Commands

The instance of Engine has some functions to change the scenes.

import Engine from "animationvideo/Engine.mjs";

// init with canvas
const engine = new Engine(document.querySelector("canvas"));

// "recalculateCanvas" will trigger the scaling system of the auto-size-system
// (this will resize the canvas itself)
// after that it will trigger engine.resize()
engine.recalculateCanvas();

// "resize" will propagade a resize event to the sprite-objects of the current scene
engine.resize();

// "switchScene" will change the scene-object
engine.switchScene(scene);

// "run" starts the engine - returns a promise
/* await */ engine.run(/* optional: object with parameter that are given to the init-function */);

// "destroy" clean up the events, stops the main loop
// that was started with "run" - returns a promise
/* await */ engine.destroy();

Scenes

A scene controls what happens on the screen and in the engine. It uses Sprites and Animations.

Default

Default descries a Animation without sound on a canvas with a fixed size. In this canvas the coordinates of the top left corner is 0, 0 and the coordinate in the bottom right is the width, height of the canvas in pixels. This is the basic scene. All other scenes are based on this and add something special (f.e. add audio or the coordinates are special).

A scenes main task is to use the given layerManager to first move the objects of the layers and then to draw them.

There are a number of functions that are given to the constructor that are explained in this example. The functions can be combined in a object or a class:

import Engine from 'animationvideo/Engine.mjs'
import SceneDefault from 'animationvideo/Scenes/Default.mjs'
import TimingDefault from 'animationvideo/Timing/Default.mjs'

const engine = new Engine(document.querySelector('canvas'))

const classScene = new SceneDefault(class myScene { /*...same as in object...*/ });

const objectScene = new SceneDefault({
  // "images" is an optional object that returns a list of urls (images) with handlers.
  // The images will be loaded in parallel to calling "init". The scene will start
  // after every image is loaded and init is done.
  // can be a function that returns a object or a fixed object
  images: {
    'handlerName': 'http://image......' // list of images that will be loaded
  }

  // "init" is an optional function or async function and can return a promise.
  // It will be run when the engine sets this scene. The scene will start
  // after every image is loaded and init is done.
  // - engine is the engine object this scene is running in
  // - output is a object with canvas information
  //   output = {
  //     canvas: [],    // the canvas objects
  //     context: [],   // the context2d of the canvases
  //     width: 0,      // the width of the canvas
  //     height: 0,     // the height of the canvas
  //     ratio: 1       // the ratio between width and height
  //   }
  // - scene is the scene object this object is running in
  // - parameter are the parameter that are given from the
  //   last scene or the start parameter. The setup is always {
  //     run: ..., // the parameter given with the run command of the engine
  //     scene: ..., // the parameter given with the scene switch
  //     destroy: ..., // the parameter given from the last scene as return from the destroy function
  //   }
  // - imageManager is the object that loads the images
  async init({ engine, output, scene, parameter, imageManager }) {
    // optional: wait till all images are loaded
    // await imageManager.isLoadedPromise()

    // set events or do precalculation...
    // ...
  }

  // "destroy" is an optional function.
  // It will run when the engine's destroy is called or
  // when the engine switches a scene.
  async destroy({ engine, scene, output }) {
    // clean up code

    // return parameter for the next scene
    return {};
  }

  // "loading" is an optional function that replaces the loading animation
  // can be empty to disable any loading animation. F.e. loading() {}
  loading({ engine, scene, output, timePassed, totalTimePassed, progress, imageManager }) {
    // replace the loading screen
    const ctx = output.context[0];
    const loadedHeight =
      typeof progress === "number"
        ? Math.max(1, progress * output.height)
        : output.height;
    ctx.globalCompositeOperation = "source-over";
    ctx.globalAlpha = 1;
    ctx.clearRect(0, 0, output.width, output.height);
    ctx.fillStyle = "#aaa";
    ctx.fillRect(
      0,
      output.height / 2 - loadedHeight / 2,
      output.width,
      loadedHeight
    );
    ctx.font = "20px Georgia";
    ctx.fillStyle = "#fff";
    ctx.textAlign = "left";
    ctx.textBaseline = "bottom";
    
    ctx.fillText(
      isNaN(parseFloat(progress))
        ? progress
        : "Loading " + Math.round(100 * progress) + "%",
      10 + Math.random() * 3,
      output.height - 10 + Math.random() * 3
    );
  },

  // "endTime" can set the operational time of the animation in ms.
  // If the scene is running for "endTime" ms the scene
  // will call the function "end".
  // By default the value is undefined and thus "end" will never be triggered.
  endTime: 1000,

  // The function "end" will be triggered if the animation is running
  // for "endTime" ms.
  end({ engine, scene, output, timePassed, totalTimePassed, imageManager }) {
    // f.e. switch scene at the end of a cutscene
  }

  // Timing is completely optional and describes how the time is updated and
  // how often "fixedUpdate" is called
  timing: new DefaultTiming({
    // "tickChunk" will set the interval in ms that will call "fixedUpdate".
    // Default value is 16.66666667.
    // can be a function or a fixed value
    tickChunk: 1000/60,

    // "tickChunk" will set the number of frames
    // that can be skipped while rendering
    // can be a function (evaluated once) or a fixed value
    maxSkippedTickChunk: 3,

    // "tickChunkTolerance" will set the time in ms that will be ignored
    // if a frame misses the target tickChunk-time. Default value is 0.1
    // can be a function (evaluated once) or a fixed value
    tickChunkTolerance: 0.1,
  }),

  // "isDrawFrame" is a optional function that will determine if a scene should be drawn
  // If this function returns true it will still call "update" and "fixedUpdate" but
  // it will not draw the sprites
  // You can also return a frame count. F.e. return 2 will render the next 2 frames.
  // You can return an array too, to give seperate values to different canvas.
  // - engine is the engine object this scene is running in
  // - scene is the scene object this object is running in
  // - layerManager is the object that manages all objects that are in the scene
  // - output is a object with canvas information
  //   output = {
  //     canvas: null,  // the canvas object
  //     context: null, // the context2d of the canvas
  //     width: 0,      // the width of the canvas
  //     height: 0,     // the height of the canvas
  //     ratio: 1       // the ratio between width and height
  //   }
  // - timePassed is the time in ms that has passed since the last frame
  // - totalTimePassed is the time in ms that has passed since the start of the
  //   animation
  // - imageManager is the object that loads the images
  isDrawFrame({ engine, scene, layerManager, output, timePassed, totalTimePassed, imageManager }) {
    // calculation for the draw logic. f.e.
    // return totalTimePassed<=10000 || scene.hasCamChanged();
  }

  // "fixedUpdate" is a optional function that will be
  // called in fixed periodic intervals that is set in "tickChunk"
  // - engine is the engine object this scene is running in
  // - scene is the scene object this object is running in
  // - layerManager is the object that manages all objects that are in the scene
  // - output is a object with canvas information
  //   output = {
  //     canvas: null,  // the canvas object
  //     context: null, // the context2d of the canvas
  //     width: 0,      // the width of the canvas
  //     height: 0,     // the height of the canvas
  //     ratio: 1       // the ratio between width and height
  //   }
  // - timePassed is the time in ms that has passed since the last frame
  // - totalTimePassed is the time in ms that has passed since the start of the
  //   animation
  // - imageManager is the object that loads the images
  fixedUpdate({ engine, scene, layerManager, output, timePassed, totalTimePassed, imageManager }) {
    // do collision
    // logic or handle events f.e.
    // if (keydown) layerManager.getById(0).addElements(this.createExplosion());
  }

  // "update" is a optional function that will be called once per frame
  // how often this function is called depends on the frame rate
  // - engine is the engine object this scene is running in
  // - scene is the scene object this object is running in
  // - layerManager is the object that manages all objects that are in the scene
  // - output is a object with canvas information
  //   output = {
  //     canvas: null,  // the canvas object
  //     context: null, // the context2d of the canvas
  //     width: 0,      // the width of the canvas
  //     height: 0,     // the height of the canvas
  //     ratio: 1       // the ratio between width and height
  //   }
  // - timePassed is the time in ms that has passed since the last frame
  // - totalTimePassed is the time in ms that has passed since the start of the
  //   animation
  // - imageManager is the object that loads the images
  update({ engine, scene, layerManager, output, timePassed, totalTimePassed, imageManager }) {
    // set text of a object - f.e. the score
    // layerManager.getById(0).getById(0).text = this.score;

    // to draw something on the canvas every frame it's better to use
    // a callback/function in a layer of the layerManager
  }

  // "reset" sets the layers of the scene
  // will be called after the initialization and if there is a seek backwards
  reset({ engine, scene, layerManager, output, imageManager }) {
    // - you can directly work with the layerManager
    // layerManager.clear();
    // layerManager.addLayer().addElements([ SPRITES ]);
    // return layerManager;

    // - or you can return a 2d-array that will be converted to layers
    // return [
    //  [ SPRITES IN LAYER 0 ],
    //  [ SPRITES IN LAYER 1 ]
    // ]
  }
});

engine.switchScene(objectScene).run();

Layers

A scenes main task is to use the given layerManager to first move the objects of the Layers and to draw them.

import Engine from 'animationvideo/Engine.mjs';
import SceneDefault from 'animationvideo/Scenes/Default.mjs';
import Rect from 'animationvideo/Sprites/Rect.mjs';

new Engine({
  canvas: document.querySelector('canvas'),
  scene: new SceneDefault({
    // get the first layerManager at reset
    reset({layerManager}) {

      // --- layerManager functions ---
      // clear all elements
      layerManager.clear();

      // add a layer and save the layer in this object
      this.layerBackground = layerManager.addLayer();

      // add a number of layers
      [this.layerScroll1, this layerScroll2] = layerManager.addLayers(2);

      // add a layer and save the id
      this.layerMainId = layerManager.addLayerId();

      // add more then one layer at the same time and save ids
      // returns an array
      this.layerForgroundIds = layerManager.addLayerIds(3);

      // get a layer by a id
      this.layerMain = layerManager.getById(this.layerMainId);

      // loop through the objects of the layers
      layerManager.forEach(({ element, isFunction, layer, index }) => {
        // element is the current object
        // isFunction is true if element is a function
        // layer is the current layer
        // index is the id/position in the layer
      });

      // give number of layers
      console.log(layerManager.count());


      // --- layer function ---
      // add a sprite to the layer and save it
      this.spriteMainRect = this.layerMain.addElement(new Rect());
      // it can be a function
      this.spriteMainFunction = this.layerMain.addElement(
        function ({ engine, scene, layerManager, layer, output, totalTimePassed }) {
          output.context.drawImage(...)
          // return true will remove this function from the layer
          return totalTimePassed > 1000
        }
      );

      // add a sprite to the layer and return only the id
      const elementId = this.addElementForId(new Rect());


      // add an array of sprites
      [this.spriteMainRect2, this.spriteMainRect3] = this.layerMain.addElements([
        new Rect({...}),
        new Rect({...})
      ]);

      // add array of sprite to the layer and return only the ids
      const elementIds = this.layerMain.addElementsForIds([new Rect(),....]);


      // remove a element of this layer
      this.layerMain.deleteByElement(this.spriteMainRect3);

      // remove a element of this layer by the id of the element
      this.layerMain.deleteById(elementId);


      // loop through the objects of a layer
      this.layerMain.forEach(({ element, isFunction, layer, index }) => {
        // element is the current object
        // isFunction is true if element is a function
        // layer is the current layer
        // index is the id/position in the layer

        // f.e. call all reset methods of the element of the layer
        !isFunction && element.reset && element.reset()
      });

      // get a object by the id from a layer
      this.spriteFirst = this.layerMain.getById(0)

      // get a id by the elment of a layer
      this.spriteFirstID = this.layerMain.getByElement(this.spriteFirst)

      // output the number of elements in the layer
      console.log(this.layerMain.count());

      // empty the layer
      this.layerBackground.clear()

      return layerManager;
    }
  })
}).run()

Norm

This scene is similar to the Default-scene. But the coordinates are different: the middle of the canvas will be at 0, 0, left and bottom of the canvas at -1, -1 and the top right is at 1, 1. In addition the Norm has a function named transformPoint(x,y) that will transform normal x, y coordinates of the canvas (f.e. mouse position) into Norm-coordinates.

import Animationvideo from "lib/animationvideo";

const {
    Engine,
    Scenes: {Norm},
    Animations: {Forever, ChangeTo},
    Sprites: {Circle, FastBlur},
    Easing: {CubicInOut}
} = Animationvideo;

// The Engine runs the scene "Norm"
new Engine({
    // enable autoSize
    autoSize: true,
    // set canvas
    canvas: document.querySelector("canvas"),
    // The Engine uses the scene "Norm"
    scene: new Norm(
        class myExample {
            // mouse event that we will bind
            eventMouseMove(e) {
                // use transforPoint to transform mouse coordinates to internal Norm-ccordinates
                [this.mx, this.my] = this.scene.transformPoint(e.offsetX, e.offsetY);
            }

            init({engine, output, scene}) {
                // set values we need for position tracking
                this.mx = 1;
                this.my = 0.5;
                this.scene = scene;

                // add mouse move event
                output.canvas.addEventListener(
                    "mousemove",
                    this.eventMouseMove.bind(this)
                );
            }

            destroy({output}) {
                // don't forget to clean up
                output.canvas.removeEventListener("mousemove", this.eventMouseMove);
            }

            reset({layerManager}) {
                // background will be a feedback effect
                layerManager.addLayer().addElement(
                    new FastBlur({
                        alpha: 0.9,
                        scaleX: 10,
                        scaleY: 10,
                        darker: 0.3,
                        clear: true,
                        pixel: true
                    })
                );

                // above is a circle that will move to the mouse every 500ms
                this.layerMove = layerManager.addLayer();
                layerManager.addLayer().addElements([
                    new Circle({
                        x: this.mx,
                        y: this.my,
                        scaleX: 0.1,
                        scaleY: 0.1,
                        color: "#F00",
                        animation: new Forever([
                            new ChangeTo(
                                {
                                    x: () => this.mx,
                                    y: () => this.my
                                },
                                500,
                                CubicInOut
                            )
                        ])
                    })
                ]);
                return layerManager;
            }
        }
    )
}).run(); // start the engine

Test code at codesandbox.io

NormCamera

This is a Norm-Scene that has controls for zooming and moving the content of the canvas with a camera. There is support for mobile.

import "./styles.css";
import Animationvideo from "lib/animationvideo";

const {
    Engine,
    Scenes: {NormCamera},
    Animations: {Remove, ChangeTo},
    Sprites: {Image, Rect},
    Easing: {QuadOut}
} = Animationvideo;

// The Engine runs the scene "NormCamera"
new Engine({
    autoSize: true,
    canvas: document.querySelector("canvas"),
    // The Engine uses the scene "NormCamera"
    scene: new NormCamera({
        cam: {
            // use alternative camera controls
            // -> you will move the camera with second mouse button/two finger touch
            alternative: true,
            zoomMax: 10, // max zoom factor
            zoomMin: 0.5, // min zoom factor
            zoomFactor: 1.2, // scoll factor of the mouse wheel
            tween: 4, // how fast to interpolate the new position - higher = slower
            registerEvents: true, // let NormCamera be the mouse
            preventDefault: true, // block all other events that are bound
            enabled: true, // enable camera movement - with this you can lock the camera
            callResize: true, // call the resize events of the sprites
            doubleClickDetectInterval: 350 // how long to wait (ms) for double click detection
        },
        // click event
        // x, y is in Norm-space
        click({event, scene, x, y, imageManager}) {
            scene.zoomTo(x - 0.2, y - 0.2, x + 0.2, y + 0.2);
        },
        // double click event
        // x, y is in Norm-space
        doubleClick({event, scene, x, y, imageManager}) {
            scene.zoomTo(-1, -1, 1, 1);
        },
        // event while marking a region - only with alternative camera controls
        // x1,y1 will be the upper left corner, x2,y2 the bottom right corner in Norm Space
        // fromX, fromY is the start position of the region
        // toX, toY is the current position of the region
        regionMove({event, scene, x1, y1, x2, y2, fromX, fromY, toX, toY, imageManager}) {
            this.spriteMarker.enabled = true;
            this.spriteMarker.x = x1;
            this.spriteMarker.y = y1;
            this.spriteMarker.width = x2 - x1;
            this.spriteMarker.height = y2 - y1;
        },
        // event after marking a region - only with alternative camera controls
        // x1,y1 will be the upper left corner, x2,y2 the bottom right corner in Norm Space
        // fromX, fromY is the start position of the region
        // toX, toY is the current position of the region
        region({event, scene, x1, y1, x2, y2, fromX, fromY, toX, toY, imageManager}) {
            this.spriteMarker.enabled = false;
            this.layerOverlay.addElement(
                new Rect({
                    x: x1,
                    y: y1,
                    width: x2 - x1,
                    height: y2 - y1,
                    color: "#fff",
                    compositeOperation: "lighter",
                    animation: [new ChangeTo({alpha: 0}, 3000, QuadOut), new Remove()]
                })
            );
        },
        // event when moving the mouse/finger over the canvas
        mouseMove({event, scene, x, y, imageManager}) {
        },
        // event when start clicking the mouse/touching the finger over the canvas
        mouseDown({event, scene, x, y, imageManager}) {
        },
        // event when end clicking the mouse/touching the finger over the canvas
        mouseUp({event, scene, x, y, imageManager}) {
        },
        // event when moving the mouse out of the canvas
        mouseOut({event, scene, imageManager}) {
        },

        images() {
            return {imageFile: "https://placekitten.com/400/400"};
        },
        reset({layerManager}) {
            layerManager.addLayer().addElement(new Rect({clear: true}));
            layerManager.addLayer().addElement(
                new Image({
                    normCover: true,
                    image: "imageFile"
                })
            );
            this.layerOverlay = layerManager.addLayer();
            this.spriteMarker = this.layerOverlay.addElement(
                new Rect({
                    enabled: false,
                    color: "#fff",
                    norm: false,
                    compositeOperation: "difference"
                })
            );
            return layerManager;
        }
    })
}).run(); // start the engine

Test code at codesandbox.io

Timing

Every scene is given a timing, that describes, how time is measured and how often the fixed update function is called. The default timing will measure the time with performance.now() and call the fixed update 60 times in a second.

Audio

After setting a "audioElement" the time for the animation is given by this audio-element. "end" of the scene will be automatically called without giving "endTime".

import Animationvideo from "lib/animationvideo";

const {
    Engine,
    Scenes: {Default: SceneDefault},
    Timing: {Audio: TimingAudio},
    Animations: {ChangeTo, Wait, Remove},
    Sprites: {Rect, Path, StarField, FastBlur},
    Easing: {QuadInOut, ElasticOut, BounceOut, QuadOut}
} = Animationvideo;

new Engine({
    // clicking on the canvas will start the audio
    clickToPlayAudio: true,
    // the canvas for the animation
    canvas: document.querySelector("canvas"),
    // The Engine uses the scene "Audio"
    scene: new SceneDefault({
        // audio element that plays the music
        timing: new TimingAudio({
            audioElement: document.getElementById('audio')
        }),
        // function that runs when the audio ends
        end() {
            window.alert("audio done");
        },
        // show totalTimePassed
        update({totalTimePassed}) {
            const tickElement = document.getElementById("tick");
            if (tickElement) {
                tickElement.innerText = Math.round(totalTimePassed);
            }
        },
        // initialisation of the scene with sprites
        reset() {
            return [
                // first layer is the background
                [
                    new Rect({
                        color: "#117",
                        animation: [
                            500,
                            new ChangeTo({color: "#88C"}, 1000),
                            new Wait(14500),
                            new ChangeTo({color: "#C88"}, 300),
                            new ChangeTo({color: "#FCC"}, 3000)
                        ]
                    })
                ],
                // effect rects
                [
                    new Rect({
                        color: "#66b",
                        width: 100,
                        x: -100,
                        animation: [
                            500,
                            new ChangeTo({x: 100}, 1000, ElasticOut),
                            new Wait(14500),
                            new ChangeTo({alpha: 0}, 300),
                            new Remove()
                        ]
                    }),
                    new Rect({
                        color: "#66b",
                        width: 100,
                        x: 800,
                        animation: [
                            500,
                            new ChangeTo({x: 600}, 1000, ElasticOut),
                            new Wait(14500),
                            new ChangeTo({alpha: 0}, 300),
                            new Remove()
                        ]
                    }),
                    new Rect({
                        color: "#449",
                        width: 100,
                        x: -100,
                        animation: [
                            500,
                            new ChangeTo({x: 200}, 1000, ElasticOut),
                            new Wait(14500),
                            new ChangeTo({alpha: 0}, 300),
                            new Remove()
                        ]
                    }),
                    new Rect({
                        color: "#449",
                        width: 100,
                        x: 800,
                        animation: [
                            500,
                            new ChangeTo({x: 500}, 1000, ElasticOut),
                            new Wait(14500),
                            new ChangeTo({alpha: 0}, 300),
                            new Remove()
                        ]
                    }),
                    new Rect({
                        color: "#227",
                        width: 100,
                        x: -100,
                        animation: [
                            500,
                            new ChangeTo({x: 300}, 1000, ElasticOut),
                            new Wait(14500),
                            new ChangeTo({alpha: 0}, 300),
                            new Remove()
                        ]
                    }),
                    new Rect({
                        color: "#227",
                        width: 100,
                        x: 800,
                        animation: [
                            500,
                            new ChangeTo({x: 400}, 1000, ElasticOut),
                            new Wait(14500),
                            new ChangeTo({alpha: 0}, 300),
                            new Remove()
                        ]
                    })
                ],
                // logo that morphs
                [
                    new Path({
                        path:
                            "M123.3 5.5c-5.7 1.3-10.9 4.8-14.6 9.8-9 12.1-4.8 31 8.6 37.8L121 55v47.7l-4 3.2c-3.7 3-5.3 3.4-19.9 5.7l-15.9 2.5-3.6-2.5c-8.3-5.6-24.3-6.2-31.6-1.1-2.8 1.9-4.3 3.9-5 6.5l-1 3.7-9.7 1.6c-12 2-16.5 4.8-20.5 12.7-3.5 6.9-4.8 13.8-4.8 25.4 0 10 2.3 16.9 7.2 21.3 2.2 2.1 68.6 37.5 80.3 42.9l5 2.3 36-2.9c19.8-1.6 39.2-3.1 43-3.4 40.2-3.1 37.7-2.8 42.2-6.2 5.5-4.2 7.5-12.4 6.3-25.1-1-9.5-2.5-12.8-20.3-43.8-20.4-35.6-22.3-38.4-29.1-41.9-3.3-1.7-6.9-3.9-8-5-5.8-5.1-13.9-7.1-23.7-5.8l-5.6.8-.6-14c-1.2-25.1-1.3-24.2 3.2-26.5 14.2-7.3 17.6-27.4 6.8-39.7-2.2-2.5-5.1-5.1-6.6-5.8-4.7-2.5-12.2-3.3-17.8-2.1z",
                        color: "#000",
                        borderColor: "#FFF",
                        lineWidth: 3,
                        x: 270,
                        y: 30,
                        alpha: 0,
                        scaleY: 0.7,
                        rotationInDegree: -5,
                        animation: [
                            // wait 2000 ms
                            2000,
                            // intro
                            new ChangeTo(
                                {
                                    rotationInDegree: 0,
                                    scaleY: 1,
                                    alpha: 1
                                },
                                1500,
                                BounceOut
                            ),
                            new Wait(500),
                            new ChangeTo(
                                {
                                    x: 260,
                                    y: 20,
                                    scaleX: 1.1,
                                    scaleY: 1.1
                                },
                                4000,
                                QuadInOut
                            ),
                            new Wait(1000),
                            // morph
                            new ChangeTo(
                                {
                                    path:
                                        "M384,48.734c-70.692,0-128,57.308-128,128c0-70.692-57.308-128-128-128s-128,57.308-128,128c0,137.424,188.048,252.681,241.805,282.821c8.823,4.947,19.567,4.947,28.39,0C323.952,429.416,512,314.158,512,176.734C512,106.042,454.692,48.734,384,48.734z",
                                    scaleX: 0.5,
                                    scaleY: 0.5
                                },
                                1000,
                                BounceOut
                            ),
                            new Wait(6000),
                            new ChangeTo(
                                {
                                    x: 215,
                                    y: -25,
                                    scaleX: 0.7,
                                    scaleY: 0.7
                                },
                                6500,
                                QuadInOut
                            )
                        ]
                    })
                ],
                // effect stars
                [
                    new StarField({
                        moveX: 0,
                        animation: [
                            4000,
                            new ChangeTo({moveY: -4}, 1000, QuadInOut),
                            new Wait(2000),
                            new ChangeTo({moveY: 0}, 200, QuadOut),
                            new Wait(3300),
                            new ChangeTo(
                                {
                                    moveX: 4,
                                    moveY: -2
                                },
                                1000,
                                QuadInOut
                            ),
                            new Wait(3000),
                            new ChangeTo(
                                {
                                    moveX: 0,
                                    moveY: 0
                                },
                                200,
                                QuadOut
                            ),
                            new Remove()
                        ]
                    })
                ],
                // last layer consits of a blur effect
                [
                    new FastBlur({
                        compositeOperation: "lighter", // make a glow
                        gridSize: 10, // the glow has the size of 10 times 10
                        // pixel: true,
                        darker: 0.5, // turn down the glow
                        alpha: 0, // not visible
                        animation: [
                            // wait 2000 ms
                            2000,
                            // blend in the half visible glow
                            new ChangeTo({alpha: 0.4}, 1500, QuadInOut)
                        ]
                    })
                ]
            ];
        }
    })
}).run(); // start the engine

Test code at codesandbox.io

Sprites

Sprites are the objects that are drawn on the screen. They are the main ingredient of an animation.

Image

Renders an image to the canvas. Can be a real image or the reference to an image loaded with the images routine of the scene. You can tint an image (give it a color), but this will create a new canvas in the background and can be slow.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import Image from "animationvideo/Sprites/Image.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    // load images beforehand
    images() {
      return { imageFile: "https://placekitten.com/400/400" };
    },
    // initialisation of the scene with sprites
    reset() {
      return [
        [
          new Image({
            enabled: true,
            image: "imageFile", // name of the key of the image defined in "images"
            x: 0, // position of the image
            y: 0,
            width: undefined, // width and height of the image. Undefined to take
            height: undefined, // it from the original image.
            rotation: 0, // use rotationInDegree to give values in degree
            scaleX: 1, // scalling of the image
            scaleY: 1,
            alpha: 1, // transparency
            compositeOperation: "source-over",
            position: Image.CENTER, // or Image.LEFT_TOP - pivot of the image
            frameX: 0, // left corner of the sprite that will be cut out from an image
            frameY: 0, // top corner of the sprite that will be cut out from an image
            frameWidth: 0, // width of the sprite that will be cut out from an image
            frameHeight: 0, // height of the sprite that will be cut out from an image
            norm: false, // resize the image, so it hits the corner of the canvas
            normCover: false, // resize the image, so it's completly covering the canvas
            normToScreen: false, // it will be norm-ed to the visible, zoomed out screen, not to the full -1 to 1 canvas
            animation: undefined,
            tint: 0, // tint the image with the color "color". A value between 0 (no tint) and 1 (image is completly in this color)
            color: '#fff' // color for the tint
          })
        ]
      ];
    }
  })
}).run();

You can also use Sprite Sheets. You can cut out a single Sprite with frameX, frameY, frameWidth, frameHeight. See the ImageFrame-Animation for an example.

Rect

Renders a rectangle in a color. Can be used in a short form to clear the screen.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import Rect from "animationvideo/Sprites/Rect.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new Rect({
            enabled: true,
            x: undefined, // Position - default upper left corner
            y: undefined,
            width: undefined, // Size - default full screen
            height: undefined,
            rotation: 0, // rotation in radian. Use rotationInDegree to give values in degree
            alpha: 1, // transparency
            compositeOperation: "source-over",
            color: "#fff", // color of the rect
            borderColor: undefined, // optional border color - undefined to disable the border
            lineWidth: 1, // size of the border
            clear: false, // clear the rect instead of filling with color
            // resize the rect, so it hits the corner of the canvas
            // default is true if x, y, width and height is undefined
            norm: false,
            animation: undefined
          })
        ],
        [
          new Rect({ clear: true }) // <- short form to clear the full canvas
        ]
      ];
    }
  })
}).run();

Circle

Renders a circle in a color.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import Circle from "animationvideo/Sprites/Circle.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new Circle({
            enabled: true,
            x: 0, // Position
            y: 0,
            scaleX: 1, // scalling of the circle
            scaleY: 1,
            rotation: 0, // rotation in radian. Use rotationInDegree to give values in degree
            alpha: 1, // transparency
            compositeOperation: "source-over",
            color: "#fff", // color of the rect
            animation: undefined
          })
        ]
      ];
    }
  })
}).run();

Path

Renders a "Path". With this you can render vector graphics. A special effect is the clipping. This will allow you to render stuff only inside the path.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import Path from "animationvideo/Sprites/Path.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new Path({
            enabled: true,
            x: 0, // Position
            y: 0,
            scaleX: 1, // scalling of the path
            scaleY: 1,
            rotation: 0, // rotation in radian. Use rotationInDegree to give values in degree
            alpha: 1, // transparency
            compositeOperation: "source-over",
            path: "...", // the svg path or a Path2D-Object
            color: undefined, // color to fill the path
            borderColor: undefined, // color of the border of the path
            lineWidth: 1, // line width of the border
            clip: false, // true will render "sprite" inside the path
            sprite: [], // the sprites that will be rendered inside the path if clip is true
            fixed: false, // the position, rotation and scalling will be the same as the path itself
            animation: undefined, // in the animation you can even morph the path with ChangeTo!
            polyfill: true // "true" will inject a workaround for edge and older browsers if needed
          })
        ]
      ];
    }
  })
}).run();

Text

Renders text at the canvas.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import Text from "animationvideo/Sprites/Text.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new Text({
            enabled: true,
            x: 0, // Position
            y: 0,
            scaleX: 1, // scalling of the text
            scaleY: 1,
            rotation: 0, // rotation in radian. Use rotationInDegree to give values in degree
            alpha: 1, // transparency
            compositeOperation: "source-over",
            text: undefined, // Text to show
            font: "26px monospace", // font to use
            position: Text.CENTER, // or Text.LEFT_TOP - pivot of the text
            color: undefined, // fill-color of the text
            borderColor: undefined, // border color of the text
            lineWidth: 1, // border size
            animation: undefined
          })
        ]
      ];
    }
  })
}).run();

Callback

Callback that will be called to manually render something on the canvas.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteCallback from "animationvideo/Sprites/Callback.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteCallback({
            enabled: true,
            // set the callback
            callback: function(context, timePassed, additionalParameter, sprite) {
              // in this function you can do whatever you want
              context.drawImage(..);
              ....
            };
            animation: undefined
          })
        ]
      ];
    }
  })
}).run();

FastBlur

Creates a new Canvas and copies the screen on top of it. Because the new canvas is much smaller than the current one, the image will be blurred. You can use it to apply glow-effect or to copy the current screen to a part of the screen.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteFastBlur from "animationvideo/Sprites/FastBlur.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteFastBlur({
            enabled: true,
            x: undefined, // Position - default upper left corner
            y: undefined,
            width: undefined,
            height: undefined,
            norm: false, // is true by default if x, y, width and height are undefined. Set the size of the canvas to the original canvas size
            scaleX: 1, // the scalled internal size of the canvas. F.e. if scaleX and scaleY is 2 then the the internal
            scaleY: 1, // canvas has half of the size of the original canvas
            gridSize: undefined, // if defined overrides scaleX and scaleY. The internal width and height of the canvas. Usefull for Norm-Scenes
            alpha: 1, // transparency
            compositeOperation: "source-over", // "lighter" will give you a glow effect
            darker: 0, // makes the picture darker by rendering a black color with alpha over the canvas. Useful for "lighter"
            pixel: false, // will make the image look pixelated. This can be used to create a "censored" effect
            clear: false, // clear the screen before rendering back to the screen from the canvas
            animation: undefined // the animation
          })
        ]
      ];
    }
  })
}).run();

StarField

Renders moving "stars".

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import Rect from "animationvideo/Sprites/Rect.mjs";
import StarField from "animationvideo/Sprites/StarField.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new Rect({color: '#000'})
        ],
        [
          new StarField({
            enabled: true,
            x: undefined, // Position - default upper left corner
            y: undefined,
            width: undefined, // Size - default full screen
            height: undefined,
            // resize, so it hits the corner of the canvas
            // default is true if x, y, width and height is undefined
            norm: false,
            alpha: 1, // transparency
            compositeOperation: "source-over",
            color: "#fff", // color of the rect
            count: 40, // how many stars
            // where the stars moves to - you don't see anything if everything is zero
            moveX: 0.,
            moveY: 0.,
            moveZ: 0.,
            lineWidth: undefined, // size of the stars
            highScale: true // false is faster, but true is needed for "Norm"-scenes
            animation: undefined, // the animation
          })
        ]
      ];
    }
  })
}).run();

Group

Renders a Group of Sprites. This is used to move them together or to apply effects at the same time.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import Group from "animationvideo/Sprites/Group.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new Group({
            // the sprites that will be rendered inside
            // f.e. [ new Rect({...}), new Image({...})]
            sprite: [],
            enabled: true,
            x: 0, // Position - default upper left corner
            y: 0,
            scaleX: 1, // scalling of the path
            scaleY: 1,
            rotation: 0, // rotation in radian. Use rotationInDegree to give values in degree
            alpha: 1, // transparency
            compositeOperation: "source-over",
            animation: undefined // the animation
          })
        ]
      ];
    }
  })
}).run();

Canvas

Creates a new Canvas and renders sprites on top of it. For pre-rendering and feedback effects.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteCanvas from "animationvideo/Sprites/Canvas.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteCanvas({
            // the sprites that will be rendered inside
            // f.e. [ new Rect({...}), new Image({...})]
            sprite: [],
            enabled: true,
            x: undefined, // Position - default upper left corner
            y: undefined,
            width: undefined,
            height: undefined,
            canvasWidth: undefined, // internal size of the canvas - can not be changed afterwards
            canvasHeight: undefined, // internal size of the canvas - can not be changed afterwards
            isDrawFrame: true, // true - draw all sprites always, false - draw only once, function is allowed too and called by frame
            norm: false, // is true by default if x, y, width and height are undefined. Set the size of the canvas to the original canvas size
            scaleX: 1, // the scalled internal size of the canvas. F.e. if scaleX and scaleY is 2 then the the internal
            scaleY: 1, // canvas has half of the size of the original canvas
            gridSize: undefined, // if defined overrides scaleX and scaleY. The internal width and height of the canvas.
            rotation: 0, // rotation in radian. Use rotationInDegree to give values in degree
            alpha: 1, // transparency
            compositeOperation: "source-over",
            animation: undefined // the animation
          })
        ]
      ];
    }
  })
}).run();

Particle

Renders a transparent colored circle on the screen. Used for particle effects.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteParticle from "animationvideo/Sprites/Particle.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteParticle({
            enabled: true,
            x: 0, // Position
            y: 0,
            scaleX: 1, // scalling/size of the particle
            scaleY: 1,
            alpha: 1, // transparency
            compositeOperation: "source-over", // render effect. Use "lighter" to get a glow.
            color: "#fff", // color of the particle
            animation: undefined
          })
        ]
      ];
    }
  })
}).run();

Emitter

Renders a number of objects at once. All parameters can be modified with a function, so you can create powerful particle effects with one (complex) call.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteEmitter from "animationvideo/Sprites/Emitter.mjs";
import SpriteParticle from "animationvideo/Sprites/Particle.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteEmitter({
            self: {
              // parameter like in group that will be assigned to the emitter itself
            },
            class: SpriteParticle, // default is undefined. The sprite that should be genereated
            count: 1, // number of sprites to generate
            ...
            // each parameter will be part of the generated child
            // if the parameter is a function the index will be given
            // f.e.
            // color: (i)=>`RGBA($(i),255,255,1)`
            // compositeOperation: (i) => i % 2 ? "source-over" : "lighter"
            // animation: (i) => [
            // i * 10,
            // new ChangeTo...
            // ]
          })
        ]
      ];
    }
  })
}).run();

Scroller

Scroller is a Emitter for Text. It will cut a full text in it's letters and each letter will be a Text sprite. A space will be created too but it will be disabled (enabled: false) by default.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteScroller from "animationvideo/Sprites/Scroller.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteScroller({
            self: {
              // parameter like in group that will be assigned to the scroller itself
            },
            text: "...", // the text that should be rendered
            ...
            // each other parameter will be part of the generated child
            // if the parameter is a function the index will be given
            // f.e.
            // color: (i)=>`RGBA($(i),255,255,1)`
            // compositeOperation: (i) => i % 2 ? "source-over" : "lighter"
            // animation: (i) => [
            // i * 10,
            // new ChangeTo...
            // ]
          })
        ]
      ];
    }
  })
}).run();

StackBlur

A better but slower blur than FastBlur by Mario Klingemann. Creates a new Canvas and copies the screen on top of it. Because the new canvas is much smaller than the current one, the image will be blurred. You can use it to apply glow-effect or to copy the current screen to a part of the screen.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteStackBlur from "animationvideo/Sprites/StackBlur.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteStackBlur({
            // settings from FastBlur
            enabled: true,
            x: undefined, // Position - default upper left corner
            y: undefined,
            width: undefined,
            height: undefined,
            norm: false, // is true by default if x, y, width and height are undefined. Set the size of the canvas to the original canvas size
            scaleX: 1, // the scalled internal size of the canvas. F.e. if scaleX and scaleY is 2 then the the internal
            scaleY: 1, // canvas has half of the size of the original canvas
            gridSize: undefined, // if defined overrides scaleX and scaleY. The internal width and height of the canvas. Usefull for Norm-Scenes
            alpha: 1, // transparency
            compositeOperation: "source-over", // "lighter" will give you a glow effect
            darker: 0, // makes the picture darker by rendering a black color with alpha over the canvas. Useful for "lighter"
            pixel: false, // will make the image look pixelated. This can be used to create a "censored" effect
            clear: false, // clear the screen before rendering back to the screen from the canvas
            animation: undefined, // the animation
            // Special settings for Stackblur
            onCanvas: false, // will override all other settings and applies the blur directly on the underlying canvas. This is a big performence gain but you will lose some possible effects
            radius: undefined, // the radius of the blur. The more the blurier.
            radiusPart: undefined, // if radiusPart is set it will define radius as a part of the screen. Smaller values give more blur. radius = max(canvasWidth/canvasHeight) / radiusPart
            radiusScale: true // scale the radius with the zoom factor of the cam in NormCamera-Scene and the scene autoSize Factor
          })
        ]
      ];
    }
  })
}).run();

Animations

All sprites have a configuration for animation. This animation will be played back and is very precise, so can be synced to an audio source.

Sequence

Given an array of animation-commands, Sequence will play back the commands one after another. You can define more than one array and they will be played back in parallel. The sequence finish when every array is finished. They don't repeat.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteRect from "animationvideo/Sprites/StackBlurCanvas.mjs";
import Sequence from "animationvideo/Animations/Sequence.mjs";
import ChangeTo from "animationvideo/Animations/ChangeTo.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteRect({
            color: '#fff',
            animation: new Sequence([
              new ChangeTo({ color:'#f00' }, 1000),
              new ChangeTo({ color:'#00f' }, 1000)
            ])
          })
        ]
      ];
    }
  })
}).run();

Short form for Sequence

If you only have one array of animation-commands, you can give the array directly to the animations-attribute and don't have to create a new [Sequence-Object].(#sequence)

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteRect from "animationvideo/Sprites/StackBlurCanvas.mjs";
import ChangeTo from "animationvideo/Animations/ChangeTo.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteRect({
            color: '#fff',
            animation: [
              new ChangeTo({ color:'#f00' }, 1000),
              new ChangeTo({ color:'#00f' }, 1000)
            ]
          })
        ]
      ];
    }
  })
}).run();

Short form for Wait

You can add a wait time by giving the Sequence a number as first parameter. This time can even be negative. A negative value will fast forward the animation to the point in time.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteRect from "animationvideo/Sprites/StackBlurCanvas.mjs";
import ChangeTo from "animationvideo/Animations/ChangeTo.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteRect({
            x: 0,
            y: 0,
            width: 100,
            height: 100,
            color: '#fff',
            animation: [
              2500,
              new ChangeTo({ color:'#f00' }, 10000),
              new ChangeTo({ color:'#00f' }, 10000)
            ]
          })
          new SpriteRect({
            x: 100,
            y: 100,
            width: 100,
            height: 100,
            color: '#fff',
            animation: [
              -2500
              new ChangeTo({ color:'#f00' }, 10000),
              new ChangeTo({ color:'#00f' }, 10000)
            ]
          })
        ]
      ];
    }
  })
}).run();

Labels

Parallel sequences

Loop

Loop works like Sequence but repeats the commands a given amount of time.

import Engine from "animationvideo/Engine.mjs";
import SceneDefault from "animationvideo/Scenes/Default.mjs";
import SpriteRect from "animationvideo/Sprites/StackBlurCanvas.mjs";
import Loop from "animationvideo/Animations/Loop.mjs";
import ChangeTo from "animationvideo/Animations/ChangeTo.mjs";

new Engine({
  canvas: document.querySelector("canvas"),
  scene: new SceneDefault({
    reset() {
      return [
        [
          new SpriteRect({
            color: '#fff',
            animation: new Loop(10, // repeat 10 times and then stop
              new ChangeTo({ color:'#f00' }, 1000),
              new ChangeTo({ color:'#00f' }, 1000)
            )
          })
        ]
      ];
    }
  })
}).run();

Forever

State

Wait

WaitDisabled

ChangeTo

Move

Image

ImageFrame

Shake

Callback

If

Once

ShowOnce

End

EndDisabled

Remove

Stop

StopDisabled

TODO

  • more readme
  • more tests

License

MIT