Charming Canvas
Charming Canvas wraps common 2D canvas setup and drawing so you spend less time on boilerplate. The helpers return or use a standard CanvasRenderingContext2D—nothing is hidden, so you can mix Charming calls with any native 2D API.
Setting up a sharp canvas on a high-DPI screen is verbose by hand:
const width = 100;
const height = 100;
const dpr = window.devicePixelRatio;
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.width = width * dpr;
canvas.height = height * dpr;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
context.scale(dpr, dpr);
context.beginPath();
context.moveTo(10, 10);
context.lineTo(width - 10, height - 10);
context.stroke();With Charming, creation and a line look like this:
const context = cm.context2d({width: 100, height: 100});
cm.strokeLine(context, 10, 10, 90, 90);You still set strokeStyle, fillStyle, lineWidth, and so on on the same context:
const context = cm.context2d({width: 100, height: 100});
context.strokeStyle = "black";
context.lineWidth = 2;
context.strokeRect(10, 10, 80, 80);
cm.strokeLine(context, 10, 10, 90, 90);cm.context2d(options)
Returns a pixel-aligned 2D context: the canvas backing store is sized to width * dpr by height * dpr, CSS size uses width and height, and the context is scaled so you draw in CSS pixels.
Options:
- width — CSS width of the canvas (default
300). - height — CSS height (default
150). - dpr — device pixel ratio (default
window.devicePixelRatio). - container — If set, the canvas is appended: a string is treated as a
querySelectorselector; a node is used as the parent. If omitted, appendcontext.canvasyourself.
(() => {
const context = cm.context2d({width: 100, height: 100});
context.fillRect(0, 0, 100, 100);
context.fillStyle = "white";
context.beginPath();
context.arc(50, 50, 25, 0, Math.PI * 2);
context.fill();
return context.canvas;
})();const context = cm.context2d({width: 100, height: 100});
context.fillRect(0, 0, 100, 100);
context.fillStyle = "white";
context.beginPath();
context.arc(50, 50, 25, 0, Math.PI * 2);
context.fill();cm.strokeLine(context, x1, y1, x2, y2)
Strokes from (x1, y1) to (x2, y2) using the current stroke style and line width.
(() => {
const context = cm.context2d({width: 100, height: 100});
context.strokeStyle = "black";
context.lineWidth = 2;
cm.strokeLine(context, 10, 10, 90, 90);
return context.canvas;
})();const context = cm.context2d({width: 100, height: 100});
context.strokeStyle = "black";
context.lineWidth = 2;
cm.strokeLine(context, 10, 10, 90, 90);cm.fillCircle(context, cx, cy, r)
Fills a circle at (cx, cy) with radius r using the current fill style.
(() => {
const context = cm.context2d({width: 100, height: 100});
context.fillStyle = "steelblue";
cm.fillCircle(context, 50, 50, 35);
return context.canvas;
})();const context = cm.context2d({width: 100, height: 100});
context.fillStyle = "steelblue";
cm.fillCircle(context, 50, 50, 35);cm.strokeCircle(context, cx, cy, r)
Strokes a circle at (cx, cy) with radius r using the current stroke style and line width.
(() => {
const context = cm.context2d({width: 100, height: 100});
context.strokeStyle = "teal";
context.lineWidth = 3;
cm.strokeCircle(context, 50, 50, 35);
return context.canvas;
})();const context = cm.context2d({width: 100, height: 100});
context.strokeStyle = "teal";
context.lineWidth = 3;
cm.strokeCircle(context, 50, 50, 35);cm.strokeEllipse(context, cx, cy, rx, ry)
Strokes an axis-aligned ellipse at (cx, cy) with radii rx and ry.
(() => {
const context = cm.context2d({width: 100, height: 100});
context.strokeStyle = "purple";
context.lineWidth = 2;
cm.strokeEllipse(context, 50, 50, 40, 25);
return context.canvas;
})();const context = cm.context2d({width: 100, height: 100});
context.strokeStyle = "purple";
context.lineWidth = 2;
cm.strokeEllipse(context, 50, 50, 40, 25);cm.fillEllipse(context, cx, cy, rx, ry)
Fills an axis-aligned ellipse at (cx, cy) with radii rx and ry.
(() => {
const context = cm.context2d({width: 100, height: 100});
context.fillStyle = "coral";
cm.fillEllipse(context, 50, 50, 42, 28);
return context.canvas;
})();const context = cm.context2d({width: 100, height: 100});
context.fillStyle = "coral";
cm.fillEllipse(context, 50, 50, 42, 28);