Spriteling
A scriptable spritesheet animation engine that works directly on DOM elements (no canvas). It's built as ES5 compatible JavaScript, has no external dependencies and a tiny footprint (~6KB minified/gzipped).
Install using node
npm install --save spriteling
Then either import, require or use directly as <script> tag:
import Spriteling from 'spriteling'
const Spriteling = require('spriteling')
<script type="text/javascript" src="spriteling.min.js"></script>
Example 1: Basic looping animation
Create a DOM element on the page and give it an id. Pass it to a new instance of Spriteling, giving it some information about the spritesheet and where to put it on the page. Finally call .play() on it with some optional parameters.
<div id="sprite"></div>
<script>
const sprite = new Spriteling({
url: '/spritesheets/arcade.png',
top: 200,
left: 100,
cols: 3,
rows: 9
}, '#sprite')
sprite.play({
run: -1,
delay: 100
})
</script>
You can see it in action at "Arcade" animation
Example 2: Scripted animation
Looping sure is nice! But when we have a non-linear animation to build, we need to have fine-grained control over the individual steps. Like the sequencing of the sprites and timing them correctly. Take this spritesheet of a reading character for example:
For this animation, simply looping the images won't do. We need to add an animation script to get the desired result:
<div id="sprite"></div>
<script>
const sprite = new Spriteling({
url: '/spritesheets/reading.png',
top: 200,
left: 100,
cols: 5,
rows: 3
}, '#sprite', true)
sprite.addScript('read', [
// read lines
{sprite: 1, delay: 1000},
{sprite: 2, delay: 800},
{sprite: 1},
{sprite: 3, delay: 400},
{sprite: 1},
// read lines
{sprite: 2, delay: 800},
{sprite: 1},
{sprite: 3, delay: 400},
{sprite: 1},
// read lines
{sprite: 2, delay: 400},
{sprite: 1},
{sprite: 3, delay: 800},
{sprite: 1},
// blink
{sprite: 4},
{sprite: 1},
// turn page
{sprite: 5},
{sprite: 6},
{sprite: 7},
{sprite: 8},
{sprite: 9},
{sprite: 10},
{sprite: 11},
{sprite: 12}
])
sprite.play('read', {
run: -1,
delay: 200
})
</script>
You can see it in action at "Reading" demo. Another scripted animation can be seen at "Dog training" demo.
Example 3: Multiple instances
Spriteling is not hardware-accelerated, there is no need for a clunky WebGL canvas element or CSS properties that need GPU support. In fact, under the hood there is nothing more then a background positioning trick doing the hard work, which is as fast as it can get. Even under stress it performs quite well, as you can see in the "Multiple instances" demo
More advanced examples
But we shouldn't stop there! When adding positioning to the animation script, we can easily create a walking character. And the character truly comes alive when we add user interaction, like mouse events.
Feel free to check out these "Spriteling included" websites:
- Website galaxy.fili.nl
- Website filidorwiese.nl
- Website whois.wildlife.la
Now it's up to you. Please keep me posted on what you've created!
API
constructor(options, element, debug): Spriteling
Create a new Spriteling instance, example usage:
const sprite = new Spriteling({
url: '/spritesheets/walking-character.png',
top: 200,
left: 100,
cols: 5,
rows: 3
}, '#sprite')
SpriteSheetOptions
options: Property | Type | Required | Default value | Explanation |
---|---|---|---|---|
url | string |
no | null | url to spritesheet, if not set the css background-image will be used |
cols | number |
yes | null | Number of columns in the spritesheet |
rows | number |
yes | null | Number of rows in the spritesheet |
cutOffFrames | number |
no | 0 | Number of sprites not used in the spritesheet, for example the last sprite in a sheet might be blank |
top bottom left right | number |
no | Initial position of the placeholder element, will use current if not provided | |
startSprite | number |
no | 1 | Sprite number to show when done loading |
downsizeRatio | number |
no | 1 | For HiDPI or Retina support, you can supply a higher resolution spritesheet (x2) and downsize it back to regular size with this property |
onLoaded | function |
no | null | Callback function that will be called when loading has finished |
HTMLElement | string
(optional)
element: Can be a CSS selector or existing DOM element or null, in which case a new div element will be created
boolean
(optional)
debug: Can be used to enable debug logging in console, useful when fine-tuning the animation scripts
number
)
.showSprite( sprite: Stop the current animation and show the specified sprite
spriteling.showSprite(4)
number
.currentSprite(): Get the current spriteNumber that is shown
const currentSprite = spriteling.currentSprite()
string
, script: Array<Frame>
)
.addScript(name: Add a named animation sequence, for example:
spriteling.addScript('walk', [
{ sprite: 22, delay: 100 },
{ sprite: 23, delay: 100, left: 10 },
{ sprite: 24, delay: 150, left: 5 },
{ sprite: 23, delay: 100, left: 10 }
])
string
name: Can be any string value
Array<Frame>
script: The script
parameter should be an array consisting of frame objects. These frame objects can have the following properties:
Property | Type | Required | Default value | Explanation |
---|---|---|---|---|
sprite | number |
yes | Which sprite from spritesheet to show (counted from top-left to bottom-right) | |
delay | number |
no | delay defined with .play() | Time in ms to wait after this frame has been rendered |
top bottom left right | number |
no | 0 | Move the position of the placeholder to any direction after frame has been rendered |
string
, options: AnimationOptions
)
.play(scriptName: Resume/play current or given animation. Method can be called in four ways:
sprite.play() // resume current animation sequence (if not set - loops over all sprites once)
sprite.play(scriptName) // play given animation script
sprite.play(scriptName, { options }) // play given animation script with given options
sprite.play({ options }) // play current animation with given options
string
scriptName: loads a previously added animation with .addScript()
AnimationOptions
options: Property | Type | Required | Default value | Explanation |
---|---|---|---|---|
play | boolean |
no | true | Start playing the animation right away |
run | number |
no | -1 | The number of times the animation should run, -1 = infinite |
delay | number |
no | 50 | Default delay for all frames that don't have a delay set |
tempo | number |
no | 1 | Timescale for all delays, double-speed = 2, half-speed = .5 |
reversed | boolean |
no | false | Direction of the animation head, true == backwards |
script | Array<Frame> |
no | all frames | New unnamed animation sequence |
onPlay() | function |
no | Callback called when animator starts playing | |
onStop() | function |
no | null | Callback called when animator stops playing |
onFrame() | function |
no | null | Callback called when the new frame is rendered |
onOutOfView() | function |
no | null | Callback called when the placeholder is no longer in view. Could be used to stop the animation when it moved out of the document boundary. |
The callbacks allow for interactions between sprites to take place. In the following example sprite1 will trigger play() on sprite2 after it's animation has been completed:
const sprite1 = Spriteling({
url: '/spritesheets/ping.png',
cols: 3,
rows: 9
}, '#sprite1')
const sprite2 = Spriteling({
url: '/spritesheets/pong.png',
cols: 3,
rows: 9
}, '#sprite2')
sprite1.play({
run: 3,
script: [
{ sprite:1 },
{ sprite:2 },
{ sprite:3, delay:350},
{ sprite:4 }
],
onStop: () => {
sprite2.play()
}
})
.isPlaying()
Get the current play state
const isPlaying = spriteling.isPlaying()
number
)
.setTempo( tempo: Set playback tempo, double-speed = 2, half-speed = .5 (default:1)
spriteling.setTempo(2)
number
.getTempo(): Get playback tempo, double-speed = 2, half-speed = .5 (default:1)
const tempo = spriteling.getTempo()
.next()
Step the animation ahead one frame
spriteling.next()
.previous()
Step the animation backwards one frame
spriteling.previous()
number
): boolean
.goTo(frame: Jump to certain frame within current animation sequence. Returns true if succeeded.
spriteling.goTo(10)
.reverse()
Reverse direction of play
spriteling.reverse()
.isReversed()
Get the current direction of play
const isReversed = spriteling.isReversed()
.stop()
Stop the animation
spriteling.stop()
.reset()
Reset playhead to first frame
spriteling.reset()
.destroy()
Removes the element and kills the animation loop
spriteling.destroy()
Compatibility
Spriteling should work on almost anything. From IE7 to phones and tablets. Let me know if you find it doesn't work on a particular device and I'll see if I can fix that.
License
The artwork in this repository has been created by Arthur van 't Hoog and is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) license.
Meaning you are free to:
- Share — copy and redistribute the material in any medium or format
Under the following terms:
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
- NonCommercial — You may not use the material for commercial purposes.
- NoDerivatives — If you remix, transform, or build upon the material, you may not distribute the modified material.