Working with Strokes
Strokes define the outlines of shapes, paths, and text in FxCanvas. With precise control over width, style, caps, joins, and dashing, you can create everything from simple borders to complex decorative lines.
Understanding Strokes
A stroke is the outline drawn around a shape or path. Unlike fills which color the interior, strokes follow the boundary, allowing for borders, frames, and line art.
Basic Stroke Properties
Line Width
The lineWidth property controls the thickness of the stroke.
import { CanvasEncoder, Color } from 'fxcanvas';
const canvas = new CanvasEncoder(400, 200);
// Thin line
canvas.setLineWidth(1);
canvas.strokeRect(50, 50, 100, 100);
// Thick line
canvas.setLineWidth(10);
canvas.strokeRect(200, 50, 100, 100);Stroke Style
The strokeStyle property sets the color or pattern of the stroke.
// Solid color
canvas.setStrokeStyle(new Color(0, 0, 255)); // blue
// Gradient
const gradient = new LinearGradient(0, 0, 100, 0);
gradient.addColorStop(0, new Color(255, 0, 0)); // red
gradient.addColorStop(1, new Color(0, 0, 255)); // blue
canvas.setStrokeStyle(gradient);
// Pattern
const img = new Image('pattern.png');Beyond colors, gradients can create dynamic stroke effects; see Working with Gradients.
Patterns can also be used for textured strokes; explore in Working with Patterns.
Colors are the simplest stroke styles; see Working with Colors for color options.
Line Caps
The lineCap property controls how line endings are drawn. Line caps determine the shape of the ends of lines:
'butt': Flat ends that stop exactly at the line endpoint'round': Rounded ends that extend beyond the endpoint by half the line width, creating a semicircle'square': Square ends that extend beyond the endpoint by half the line width, creating a rectangle
canvas.setLineWidth(10);
// Butt cap - flat ending
canvas.setLineCap('butt');
canvas.beginPath();
canvas.moveTo(50, 50);
canvas.lineTo(150, 50);
canvas.stroke();
// Round cap - rounded ending
canvas.setLineCap('round');
canvas.beginPath();
canvas.moveTo(50, 100);
canvas.lineTo(150, 100);
canvas.stroke();
// Square cap - extends with square
canvas.setLineCap('square');
canvas.beginPath();
canvas.moveTo(50, 150);
canvas.lineTo(150, 150);
canvas.stroke();Line Joins
The lineJoin property controls how corners are drawn where lines meet. Line joins determine how two line segments connect at corners:
'miter': Sharp, pointed corners that extend the lines to meet at a single point, creating long spikes at sharp angles'round': Rounded corners that fill the angle with a circular arc'bevel': Flat, chopped corners that cut off the point where the lines would meet
canvas.setLineWidth(10);
// Miter join - pointed corners
canvas.setLineJoin('miter');
canvas.strokeRect(50, 50, 100, 100);
// Round join - rounded corners
canvas.setLineJoin('round');
canvas.strokeRect(200, 50, 100, 100);
// Bevel join - flat corners
canvas.setLineJoin('bevel');
canvas.strokeRect(350, 50, 100, 100);Miter Limit
For miter joins, the miterLimit property prevents extremely long spikes at sharp angles. The miter limit is the maximum ratio of miter length (the distance from the corner to the spike tip) to line width. When this ratio is exceeded at very sharp angles, the join automatically switches to 'bevel' to prevent excessively long corner spikes. The default value is 10.
canvas.setLineJoin('miter');
canvas.setMiterLimit(5); // Lower limit for sharper angles
// Very sharp angles will bevel instead of creating long spikesDashed Lines
Dashed lines create broken line patterns with customizable dash and gap lengths.
The setLineDash([dashLength, gapLength, ...]) method sets the dash pattern.
// Simple dash pattern
canvas.setLineDash([10, 5]); // 10px dash, 5px gap
canvas.strokeRect(50, 50, 100, 100);
// Complex pattern
canvas.setLineDash([15, 5, 5, 5]); // Long dash, short gap, short dash, short gap
canvas.beginPath();
canvas.moveTo(200, 50);
canvas.lineTo(300, 50);
canvas.stroke();Dash Offset
The lineDashOffset property shifts the dash pattern along the line.
canvas.setLineDash([10, 5]);
canvas.setLineDashOffset(0);
canvas.strokeRect(50, 50, 100, 100);
canvas.setLineDashOffset(7); // Shift pattern
canvas.strokeRect(200, 50, 100, 100);Stroking Different Elements
Stroking Shapes
canvas.setLineWidth(3);
canvas.setStrokeStyle(new Color(0, 128, 0)); // green
canvas.strokeRect(50, 50, 100, 100);
canvas.setStrokeStyle(new Color(255, 0, 0)); // red
canvas.strokeEllipse(200, 100, 50, 30);Stroking Paths
canvas.setLineWidth(2);
canvas.setStrokeStyle(new Color(0, 0, 255)); // blue
canvas.setLineDash([8, 4]);
canvas.beginPath();
canvas.moveTo(50, 150);
canvas.quadraticCurveTo(100, 100, 150, 150);
canvas.stroke();Stroking Text
canvas.setFont(new Font({ size: 48, faces: [new FontFace('Arial', null, { weight: 'bold' })] }));
canvas.setLineWidth(2);
canvas.setStrokeStyle(new Color(0, 0, 0)); // black
canvas.strokeText('Outlined Text', 50, 200);
canvas.setFillStyle(new Color(255, 255, 255)); // white
canvas.fillText('Outlined Text', 50, 200);Images can be integrated into stroked paths; see Working with Images.
Advanced Stroke Techniques
Variable-width Strokes
const ctx = new CanvasEncoder(645, 719);
ctx.setFillStyle(new Color(0xe7, 0xf6, 0xff));
ctx.fillRect(0, 0, 256, 256);
const points = [];
const steps = 600;
const xStart = 20;
const xEnd = 236;
const xStep = (xEnd - xStart) / steps;
const frequency = 12 * (steps / 150);
for (let i = 0; i <= steps; i++) {
const progress = i / steps; // 0 to 1
const x = xStart + i * xStep;
const y = 128 + Math.sin(i / frequency) * 70;
const thickness = 1 + progress * 20;
// Color gradient: red to purple to blue.
const r = Math.round(255 * (1 - progress));
const g = Math.round(100 * (1 - progress * 0.5));
const b = Math.round(200 * progress + 55);
const color = new Color(r, g, b);
points.push({ x, y, thickness, color });
}
ctx.setLineCap(LineCap.Round);
ctx.setLineJoin(LineJoin.Round);
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length - 1; i++) {
const p0 = points[i - 1];
const p1 = points[i];
const p2 = points[i + 1];
const cpx = p1.x;
const cpy = (p0.y + p2.y) / 2;
ctx.setLineWidth(p1.thickness);
ctx.setStrokeStyle(p1.color);
ctx.quadraticCurveTo(cpx, cpy, p2.x, p2.y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(p2.x, p2.y);
}
// Final segment
ctx.setLineWidth(points[points.length - 1].thickness);
ctx.setStrokeStyle(points[points.length - 1].color);
ctx.stroke();Custom Dash Patterns
// Morse code pattern
canvas.setLineDash([20, 5, 5, 5, 5, 5]); // Dash-dot-dash
canvas.strokeRect(50, 300, 200, 100);Mastering strokes gives you complete control over outlines, enabling everything from technical diagrams to artistic line work in your FxCanvas applications.