Fills & strokes
Paint styles, stroke alignment (inside, center or outside), fill and stroke without a seam, and hairlines that fade instead of breaking up.
Updated pixi-silk 0.1.0
Paint styles#
fill() and stroke() accept a colour, a gradient or a style object:
g.circle(0, 0, 20).fill(0xff375f); // any Pixi ColorSource
g.circle(0, 0, 20).fill('rgba(255, 55, 95, 0.5)');
g.circle(0, 0, 20).fill(linear([0x0a84ff, 0xbf5af2])); // a gradient
g.circle(0, 0, 20).fill({ color: 0xff375f, alpha: 0.5, blur: 6 }); // FillStyle
g.circle(0, 0, 20).stroke({ width: 4, color: 0xffffff, cap: 'round', alignment: 'inside' });
FillStyle | |
|---|---|
color | any Pixi ColorSource (with a gradient, it tints the ramp) |
alpha | multiplies the colour’s alpha |
gradient | a linear, radial, conic or along gradient |
blur | gaussian standard deviation in local units (Blur) |
StrokeStyle adds width (a number, or an array for tapering),
alignment, cap, dash, dashOffset and dashFit.
Alignment#
Source src/demos/alignment.demo.ts
import { Text } from 'pixi.js';
import { SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';
const MODES = ['inside', 'center', 'outside'] as const;
export default defineDemo({
size: [480, 200],
controls: {
width: { type: 'range', label: 'stroke width', min: 1, max: 24, step: 1, value: 12, unit: 'px' },
},
setup({ stage, params, tick }) {
const g = new SilkGraphics();
stage.addChild(g);
MODES.forEach((mode, i) => {
const label = new Text({ text: mode, style: { fill: 0x8e8e93, fontSize: 13, fontFamily: 'system-ui' } });
label.anchor.set(0.5, 0);
label.position.set(90 + i * 150, 168);
stage.addChild(label);
});
tick(() => {
g.clear();
MODES.forEach((alignment, i) => {
const x = 40 + i * 150;
// fill + stroke on the same shape share one instance and never leave a seam
g.roundRect(x, 40, 100, 100, 22, 0.6)
.fill(0x2c2c2e)
.stroke({ width: params.width, color: 0x30d158, alpha: 0.85, alignment });
g.roundRect(x, 40, 100, 100, 22, 0.6).stroke({ width: 1, color: 0xffffff, alpha: 0.6 }); // the geometric edge
});
});
},
});Strokes on closed shapes can sit inside the edge, centred on it, or outside. alignment takes the
words or Pixi’s numbers (1 inside, 0.5 center, 0 outside). Strokes on open shapes (lines,
arcs) are always centred.
Fill + stroke in one primitive#
When stroke() directly follows fill(), both paints go into the same primitive. The fragment
shader splits each pixel’s coverage exactly into three parts: the stroke band, the fill outside it,
and the fill under it. This avoids the halo you get when you draw an inner stroke on top of an
anti-aliased fill edge.
Hairlines#
Source src/demos/hairlines.demo.ts
import { SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';
// Lines thinner than a device pixel keep one pixel of width and fade instead of breaking up.
export default defineDemo({
size: [480, 220],
controls: {
scale: { type: 'range', label: 'width scale', min: 0.05, max: 2, step: 0.01, value: 0.5 },
},
setup({ stage, params, tick }) {
const g = new SilkGraphics();
stage.addChild(g);
tick((t) => {
g.clear();
for (let i = 0; i < 24; i++) {
const a = -Math.PI / 2 + (i / 23) * Math.PI + Math.sin(t * 0.3) * 0.05;
const w = (0.1 + i * 0.12) * params.scale;
g.line(240, 200, 240 + Math.cos(a) * 220, 200 + Math.sin(a) * 190).stroke({
width: w,
color: 0xffffff,
});
}
});
},
});A stroke cannot be drawn thinner than one device pixel. So Silk keeps a thinner stroke one pixel
wide and scales its alpha by width / 1px. The total ink stays proportional to the width. Lines
never break into dots, and zooming out keeps every line visible. The threshold is the minStrokePx
setting (default 1).
Per-object settings#
const g = new SilkGraphics({ dither: 1, aaWidth: 1, minStrokePx: 1 });
g.aaWidth = 1.5; // softer edges (device pixels of AA ramp)
g.dither = 0; // turn gradient / blur dithering off
Defaults live in SilkGraphics.defaults.
API reference: SilkGraphics, FillStyle, StrokeStyle