Frontend Development 32 min read

Unlock Stunning Canvas Animations with SpriteJS – A Complete Beginner’s Guide

This article introduces SpriteJS, a cross‑platform canvas library from 360 Qiwang Team, showing how to create structured UI, draw shapes, apply transitions, use the Web Animation API, work with textures, paths, groups, events, D3 integration, physics engines and particle systems, all with practical JavaScript code examples.

360 Zhihui Cloud Developer
360 Zhihui Cloud Developer
360 Zhihui Cloud Developer
Unlock Stunning Canvas Animations with SpriteJS – A Complete Beginner’s Guide

What is SpriteJS?

SpriteJS is an open‑source, cross‑terminal canvas drawing library created by the 360 Qiwang Team. It provides a DOM‑like object model for canvas, allowing developers to create sprites, paths, groups and apply animations in browsers, mini‑programs and Node.js environments.

Core Features

Canvas‑based document object model

Four basic sprite types: Sprite, Path, Label, Group

CSS‑compatible properties (box model, transform, zIndex, etc.)

Powerful Transition and Animation APIs

Support for sprite sheets, resource pre‑loading and texture atlases

Event handling similar to DOM (mouse, touch, keyboard)

Compatibility with D3, Matter‑js, Proton and other third‑party libraries

Creating a Simple Sprite

Below is the basic code to create a red rounded rectangle using the native Canvas API and the equivalent SpriteJS code.

const canvas = document.getElementById('paper');
const context = canvas.getContext('2d');
const [x, y, w, h, r] = [200, 200, 200, 200, 50];
context.fillStyle = 'red';
context.beginPath();
context.moveTo(x + r, y);
context.arcTo(x + w, y, x + w, y + h, r);
context.arcTo(x + w, y + h, x, y + h, r);
context.arcTo(x, y + h, x, y, r);
context.arcTo(x, y, x + w, y, r);
context.closePath();
context.fill();
const scene = new spritejs.Scene('#container');
const layer = scene.layer();
const s = new spritejs.Sprite({
  anchor: 0.5,
  bgcolor: 'red',
  pos: [300, 300],
  size: [200, 200],
  borderRadius: 50
});
layer.append(s);

Transition API

SpriteJS makes animation as easy as chaining transition calls that return promises.

const scene = new spritejs.Scene('#container');
const layer = scene.layer();
const s = new spritejs.Sprite({
  anchor: 0.5,
  bgcolor: 'red',
  pos: [300, 300],
  size: [200, 200],
  borderRadius: 50
});
layer.append(s);

s.transition(2.0).attr({bgcolor: 'green'});

Multiple properties can be animated together, and promises enable sequential animations.

s.transition(2.0).attr({bgcolor: 'green', width: w => w + 100});
await s.transition(2.0).attr({bgcolor: 'yellow', height: h => h + 100});

Web Animation API

SpriteJS also supports the standard Web Animation API for more complex keyframe animations.

const scene = new spritejs.Scene('#container');
const layer = scene.layer();
const s = new spritejs.Sprite({
  anchor: 0.5,
  bgcolor: 'red',
  pos: [300, 300],
  size: [200, 200],
  borderRadius: 50
});
layer.append(s);

s.animate([
  {rotate: 0, borderRadius: 50, bgcolor: 'red'},
  {rotate: 360, borderRadius: 0, bgcolor: 'green', offset: 0.7},
  {rotate: 720, borderRadius: 50, bgcolor: 'blue'}
], {
  duration: 3000,
  iterations: Infinity,
  direction: 'alternate',
  easing: 'ease-in-out'
});

Using Textures

Sprites can display images by setting the textures attribute.

const scene = new spritejs.Scene('#container');
const layer = scene.layer();
const s = new spritejs.Sprite({
  textures: 'https://p0.ssl.qhimg.com/t01a72262146b87165f.png',
  anchor: 0.5,
  pos: [300, 300],
  size: [200, 200],
  scale: 0.5
});
layer.append(s);

Multiple textures can be combined with rect and srcRect for sprite sheets.

const texture = 'https://p5.ssl.qhimg.com/t01a2bd87890397464a.png';
const s = new spritejs.Sprite();
s.attr({
  anchor: 0.5,
  textures: [{src: texture, rect: [0,0,190,268], srcRect: [0,0,190,268]},
             {src: texture, rect: [200,278,190,268], srcRect: [191,269,190,268]},
             {src: texture, rect: [0,278,190,268], srcRect: [0,269,190,268]},
             {src: texture, rect: [200,0,190,268], srcRect: [191,0,190,268]}],
  border: [2, 'grey'],
  pos: [300, 300]
});
layer.append(s);

Path – Vector Shapes

SpriteJS provides a Path type that can render SVG path data.

const scene = new spritejs.Scene('#container');
const layer = scene.layer();
const p = new spritejs.Path({
  path: {d: 'M280,250A200,200,0,1,1,680,250A200,200,0,1,1,280,250Z', transform: {scale: 0.5}, trim: true},
  strokeColor: '#033',
  fillColor: '#839',
  lineWidth: 12,
  pos: [100, 50]
});
layer.appendChild(p);

Paths can also be animated with transition or animate .

p.transition(2.0).attr({d: paths[++i % paths.length]});

Group – Nested Elements

Groups allow multiple sprites to be transformed together.

const {Scene, Group, Sprite} = spritejs;
const scene = new Scene('#paper', {viewport: ['auto','auto'], resolution: [1200,1200]});
const layer = scene.layer('fglayer');
const lemon = new Sprite();
lemon.attr({bgcolor: '#fed330', size: [180,180], border: [6,'#fdbd2c'], borderRadius: 90});
const lemonGroup = new Group();
lemonGroup.attr({anchor: 0.5, pos: [610,600], size: [180,180], bgcolor: '#faee35', border: [6,'#fdbd2c'], borderRadius: 90});
lemonGroup.append(lemon);
layer.append(lemonGroup);
lemonGroup.animate([{rotate: 360}], {duration: 10000, iterations: Infinity});

Event Handling

Sprites can listen to mouse, touch and custom events just like DOM elements.

const s1 = new spritejs.Sprite();
s1.attr({anchor: [0.5,0.5], pos: [770,300], size: [300,300], rotate: 45, bgcolor: '#3c7'});
layer.append(s1);
s1.on('mouseenter', () => s1.attr('border', [4, 'blue']));
s1.on('mouseleave', () => s1.attr('border', [0, '']));

Keyboard events can be delegated by overriding pointCollision in a custom class.

class KeyButton extends spritejs.Label {
  pointCollision(evt) { return evt.originalEvent.key === this.text; }
}
KeyButton.defineAttributes({init(attr){ attr.setDefault({font: '42px Arial', border: {width:4,color:'black',style:'solid'}, width:50, height:50, anchor:[0.5,0.5], textAlign:'center', lineHeight:50}); }});

Integration with D3

Because SpriteJS mimics DOM APIs, it works smoothly with D3 for data‑driven visualizations.

const paper = new spritejs.Scene('#paper', {viewport:['auto','auto'], resolution:[1600,1200], stickMode:'width'});
const dataset = [125,121,127,193,309];
const linear = d3.scaleLinear().domain([100, d3.max(dataset)]).range([0,500]);
const colors = ['#fe645b','#feb050','#c2af87','#81b848','#55abf8'];
const s = d3.select(paper).append('fglayer');
const chart = s.selectAll('sprite').data(dataset).enter().append('sprite')
  .attr('x', 450)
  .attr('y', (d,i)=>200+i*95)
  .attr('width',0)
  .attr('height',80)
  .attr('bgcolor','#ccc');
chart.transition().duration(2000).attr('width', d=>linear(d)).attr('bgcolor',(d,i)=>colors[i]);

Physics Engine (Matter‑js)

SpriteJS can render bodies from Matter‑js, updating their position and rotation each frame.

const {Engine, World, Bodies, Composite} = Matter;
const engine = Engine.create();
const stack = Composites.stack(100,100,6,6,0,0,(x,y)=>Bodies.rectangle(x,y,15,15));
World.add(engine.world, [stack]);
function render(){
  Engine.update(engine,16);
  const bodies = Composite.allBodies(engine.world);
  bodies.forEach((body,i)=>{
    const {position, angle} = body;
    const pos = [Math.round(position.x*10)/10, Math.round(position.y*10)/10];
    const rotate = Math.round(180*angle*10/Math.PI)/10;
    let block = blocks[i];
    if(!block){
      const {min, max} = body.bounds;
      block = new spritejs.Sprite();
      block.attr({anchor:0.5, size:[max.x-min.x, max.y-min.y], pos, rotate, bgcolor: body.render.fillStyle});
      blocks[i]=block; layer.append(block);
    } else {
      block.attr({pos, rotate});
    }
  });
  requestAnimationFrame(render);
}
render();

Particle System (Proton)

SpriteJS can host Proton particle emitters.

const {Scene, ProtonRenderer} = spritejs;
const scene = new Scene('#container', {viewport:[600,600], resolution:[600,600]});
const layer = scene.layer('fglayer');
const proton = new Proton();
const emitter = new Proton.Emitter();
emitter.rate = new Proton.Rate(Proton.getSpan(10,20), 0.1);
emitter.addInitialize(new Proton.Radius(1,12));
emitter.addInitialize(new Proton.Life(2,4));
emitter.addInitialize(new Proton.Velocity(3, Proton.getSpan(0,360), 'polar'));
emitter.addBehaviour(new Proton.Color('#ff0000','random'));
emitter.addBehaviour(new Proton.Alpha(1,0));
emitter.p.x = layer.canvas.width/2;
emitter.p.y = layer.canvas.height/2;
emitter.emit(5);
proton.addEmitter(emitter);
const renderer = new ProtonRenderer(layer);
proton.addRenderer(renderer);
function tick(){ requestAnimationFrame(tick); proton.update(); }
tick();

External Clock Example (Curvejs)

SpriteJS can be driven by an external animation clock, as demonstrated with Curvejs.

(async function(){
  const scene = new spritejs.Scene('#curvejs', {resolution:[1540,600], viewport:'auto'});
  const layer = scene.layer('fglayer', {autoRender:false});
  await scene.preload(['https://p0.ssl.qhimg.com/t01a72262146b87165f.png','https://s5.ssl.qhres.com/static/5f6911b7b91c88da.json']);
  const s = new spritejs.Sprite('bird1.png');
  s.attr({anchor:[0.5,0.5], pos:[300,100], transform:{scale:[0.5,0.5]}, offsetPath:'M10,80 q100,120 120,20 q140,-50 160,0', zIndex:200});
  s.animate([{offsetDistance:0},{offsetDistance:1}],{duration:3000,direction:'alternate',iterations:Infinity});
  s.animate([{scale:[0.5,0.5],offsetRotate:'auto'},{scale:[0.5,-0.5],offsetRotate:'reverse'},{scale:[0.5,0.5],offsetRotate:'auto'}],{duration:6000,iterations:Infinity,easing:'step-end'});
  s.animate([{textures:'bird1.png'},{textures:'bird2.png'},{textures:'bird3.png'}],{duration:300,direction:'alternate',iterations:Infinity});
  layer.appendChild(s);
  const {Stage, Curve, motion} = curvejs;
  const stage = new Stage(layer.canvas);
  const randomColor = () => ['#22CAB3','#90CABE','#A6EFE8','#C0E9ED','#DBD4B7','#D4B879','#ECCEB2','#F2ADA6','#FF7784'][Math.floor(Math.random()*9)];
  stage.add(new Curve({points:[378,123,297,97,209,174,217,258], color:randomColor(), motion:motion.rotate, data:Math.PI/20}));
  stage.add(new Curve({points:[378,123,385,195,293,279,217,258], color:randomColor(), motion:motion.rotate, data:Math.PI/20}));
  function tick(){ stage.update(); layer.draw(false); requestAnimationFrame(tick); }
  tick();
})();

This guide demonstrates how SpriteJS simplifies canvas programming, offering a high‑level API for drawing, animating, handling events, and integrating with other libraries, making it a powerful tool for modern front‑end developers.

frontendanimationgraphicsJavaScriptcanvasWeb DevelopmentSpriteJS
360 Zhihui Cloud Developer
Written by

360 Zhihui Cloud Developer

360 Zhihui Cloud is an enterprise open service platform that aims to "aggregate data value and empower an intelligent future," leveraging 360's extensive product and technology resources to deliver platform services to customers.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.