Animation
Redraw SilkGraphics cheaply every frame. Animate with damp, dampAngle, Spring and ease so motion feels the same at 60, 120 or 144 Hz.
Updated pixi-silk 0.1.0
Source src/demos/spring.demo.ts
import { Container } from 'pixi.js';
import { damp, SilkGraphics, Spring } from 'pixi-silk';
import { defineDemo } from './runtime';
// Frame-rate independent motion: the knob glides the same at 60, 120 or 144 Hz.
export default defineDemo({
size: [480, 200],
touchAction: 'none',
controls: {
stiffness: { type: 'range', label: 'stiffness', min: 40, max: 400, step: 1, value: 170 },
damping: { type: 'range', label: 'damping', min: 4, max: 40, step: 1, value: 14 },
},
setup({ stage, params, tick }) {
const g = new SilkGraphics();
const hit = new Container();
const x = new Spring(120);
let glow = 0;
let dragging = false;
hit.eventMode = 'static';
hit.hitArea = { contains: (px: number, py: number) => px >= 0 && px <= 480 && py >= 0 && py <= 200 };
hit.on('pointerdown', (e) => {
dragging = true;
x.target = Math.min(440, Math.max(40, e.getLocalPosition(stage).x));
});
hit.on('globalpointermove', (e) => {
if (dragging) x.target = Math.min(440, Math.max(40, e.getLocalPosition(stage).x));
});
hit.on('pointerup', () => (dragging = false));
hit.on('pointerupoutside', () => (dragging = false));
stage.addChild(g, hit);
tick((_t, dt) => {
x.stiffness = params.stiffness;
x.damping = params.damping;
x.step(dt);
glow = damp(glow, dragging ? 1 : 0, 10, dt);
g.clear();
g.pill(40, 94, 400, 12).fill(0x2c2c2e);
g.pill(40, 94, Math.max(12, x.value - 40), 12).fill(0x0a84ff);
g.circle(x.value, 100, 22 + glow * 6).fill({ color: 0x0a84ff, alpha: 0.35 * glow, blur: 10 });
g.circle(x.value, 100, 20).fill(0xffffff);
});
},
});Immediate mode is cheap#
The simplest way to animate is to clear and redraw every frame. clear() keeps the GPU buffers.
Each primitive is 40 floats, and one instanced draw call renders them all:
app.ticker.add((ticker) => {
const t = ticker.lastTime / 1000;
g.clear();
g.arcSweep(80, 80, 44, -Math.PI / 2, Math.PI * 2 * (0.5 + 0.4 * Math.sin(t))).stroke({ width: 12, cap: 'round', color: 0x30d158 });
});
Put static parts in a second SilkGraphics and draw it once.
Frame-rate independent motion#
Easing by a fixed fraction per frame (x += (target - x) * 0.1) runs twice as fast on a 120 Hz
display. The helpers take the frame time instead:
import { damp, dampAngle, Spring, ease } from 'pixi-silk';
x = damp(x, target, 10, dt); // exponential approach, lambda is about 1 / seconds
angle = dampAngle(angle, target, 8, dt); // shortest way around the circle
const spring = new Spring(0, 170, 26); // value, stiffness, damping
spring.target = 1;
app.ticker.add((t) => draw(spring.step(t.deltaMS / 1000)));
ease.outCubic(0.5); // inOutCubic, outCubic, inOutSine, outExpo
Spring.step() integrates with fixed sub-steps, so it stays stable with large frame times.
spring.settled tells you when to stop redrawing.
Sub-pixel motion#
Slow-moving shapes glide instead of snapping from pixel to pixel. Silk renders coordinates exactly
as given, and createSilkApp turns off pixel rounding. The sub-pixel lab page
compares Silk with tessellated graphics.