UNPKG

animationvideo

Version:

Javascript-Libary for animation and audio syncing

1,350 lines (1,155 loc) 42.7 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 - [Perfect audio sync](https://codesandbox.io/s/eloquent-field-55tk6?fontsize=14) - [Zoom with gloom](https://codesandbox.io/s/quirky-ives-hwqqb?fontsize=14) - [Follow mouse move and feedback effect](https://codesandbox.io/s/infallible-wildflower-w3uo7?fontsize=14) or in the **index.html**. # Table of Contents <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> - [Installation](#installation) - [General overview](#general-overview) - [How to import](#how-to-import) - [Example](#example) - [Engine](#engine) - [Constructor-option "autoSize"](#constructor-option-autosize) - [Commands](#commands) - [Scenes](#scenes) - [Default](#default) - [Layers](#layers) - [Norm](#norm) - [Audio](#audio) - [NormAudio](#normaudio) - [Sprites](#sprites) - [Image](#image) - [Rect](#rect) - [Circle](#circle) - [Path](#path) - [Text](#text) - [Callback](#callback) - [FastBlur](#fastblur) - [StarField](#starfield) - [Group](#group) <!-- END doctoc generated TOC please keep comment here to allow auto update --> # Installation Install it for example with ```bash 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](https://www.npmjs.com/package/eases). You can install both together: ```bash npm i animationvideo eases ``` # General overview The [Engine](#engine) of AnimationVideo will run [Scenes](#scenes) that consists of [Sprites](#sprites) that can be manipulated with [Animations](#animations). ## How to import The best way to import components of AnimationVideo is the direct import of the modules. ```js 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](https://webpack.js.org/). For older packer you can use the main package and extract the needed components. ```js import AnimationVideo from "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. ```html <script src="dist/animationvideo.umd.js"></script> <script> AnimationVideo.Engine(document.querySelector("canvas")) .switchScene( AnimationVideo.Scenes.Norm({ reset: () => [ [AnimationVideo.Sprites.Rect({ clear: true })], [ AnimationVideo.Sprites.Circle({ scaleX: 0.5, scaleY: 0.5, color: "#F00", animation: AnimationVideo.Animations.Forever([ AnimationVideo.Animations.ChangeTo( { color: "#00F" }, 1000 ), 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: ```js 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](https://codesandbox.io/s/quirky-ives-hwqqb?fontsize=14) ## 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. ```js 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 // 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. ```js 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. ```js 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 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" engine.destroy(); ``` ## Scenes A scene controls what happens on the screen and in the engine. It uses [Sprites](#sprites) and [Animations](#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: ```js import Engine from 'animationvideo/Engine.mjs' import SceneDefault from 'animationvideo/Scenes/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: 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 // } // - 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 // - 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. 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, progress }) { // replace the loading screen const ctx = output.context; const loadedHeight = typeof progress === "number" ? Math.max(1, progress * output.h) : output.h; // clean the screen ctx.globalCompositeOperation = "source-over"; ctx.globalAlpha = 1; ctx.clearRect(0, 0, output.w, output.h); ctx.fillStyle = "#aac"; ctx.fillRect(0, output.h / 2 - loadedHeight / 2, output.w, loadedHeight); ctx.font = "20px Georgia"; ctx.fillStyle = "#fff"; ctx.textAlign = "left"; ctx.textBaseline = "bottom"; let text = progress; // isNumber if (!isNaN(parseFloat(progress)) && !isNaN(progress - 0)) { text = "Loading " + Math.round(100 * progress) + "%"; } ctx.fillText( text, 10 + Math.random() * 3, output.h - 10 + Math.random() * 3 ); engine && engine.normalizeContext(ctx); }, // "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 }) { // f.e. switch scene at the end of a cutscene } // "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 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 or a fixed value tickChunkTolerance: 0.1, // "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 fixedUpdate({ engine, scene, layerManager, output, timePassed, totalTimePassed }) { // 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 update({ engine, scene, layerManager, output, timePassed, totalTimePassed }) { // 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 }) { // - 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](#layers) and to draw them. ```js 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](#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. ```js import Animationvideo from "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](https://codesandbox.io/s/infallible-wildflower-w3uo7?fontsize=14) ### Audio This scene is similar to the [Default](#default)-scene. In addition to the Default-scene-functions you have to set an "**audioElement**". "**end**" is automatically called without giving "**endTime**". ```js import Animationvideo from "animationvideo"; const { Engine, Scenes: { Audio }, 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 Audio({ // audio element that plays the music audioElement: document.querySelector("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](https://codesandbox.io/s/eloquent-field-55tk6?fontsize=14) ### NormAudio This scene is similar to the [Audio](#audio)-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. See [Norm](#norm) for more information. ### NormCamera This is a [Norm](#norm)-Scene that has controls for zooming and moving the content of the canvas with a _camera_. There is support for mobile. ## 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. ```js 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 animation: undefined }) ] ]; } }) }).run(); ``` You can also use Sprite Sheets. You can cut out a single Sprite with _frameX_, _frameY_, _frameWidth_, _frameHeight_. See the [ImageFrame](#imageframe)-Animation for an example. ### Rect Renders a rectangle in a color. Can be used in a short form to clear the screen. ```js 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. ```js 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 cirlce 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"](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D). With this you can render vector graphics. A special effect is the clipping. This will allow you to render stuff only inside the path. ```js 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. ```js 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. ```js 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 ### StarField Renders moving "stars". ```js 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, // in the animation you can even morph the path with ChangeTo! }) ] ]; } }) }).run(); ``` ### Group Renders a Group of Sprites. This is used to move them together or to apply effects at the same time. ```js 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 // in the animation you can even morph the path with ChangeTo! }) ] ]; } }) }).run(); ``` ### Canvas ### Particle ### Emitter ### Scroller ### StackBlur ### StackBlurCanvas ## Animations ### Sequence #### Labels ### Loop ### Forever ### State ### Wait ### WaitDisabled ### ChangeTo ### Move ### Image ### ImageFrame ### Shake ### Callback ### If ### Once ### ShowOnce ### End ### EndDisabled ### Remove ### Stop ### StopDisabled # TODO - write demo with audio - more tests - debug error messages in console log # License [MIT](https://github.com/Kauto/animationvideo/blob/master/LICENSE)