LEARNING OBJECTIVES ⌵
- Convert fluently between human degrees ($0^\circ\text{–}360^\circ$) and graphics radians ($0\text{–}2\pi$).
- Master all parameters of
ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise). - Construct rounded fillets and corners using the tangent-based
ctx.arcTo()method. - Build high-performance circular progress meters and multi-segment donut storage visualizers.
📖 The Mental Model & Story (Intuitive Foundation)
The Angle Wheel & The $3\text{ O'Clock}$ Origin
While human beings measure rotations in degrees ($360^\circ$ for a full circle), computer graphics engines speak exclusively in Radians.
A radian is the angle created when you take the radius of a circle and wrap it directly along the circle's curved perimeter:
- A full circle ($360^\circ$) has a circumference of $2\pi r$, which equals $2\pi$ Radians ($\approx 6.28318\text{ rad}$).
- A half circle ($180^\circ$) equals $\pi$ Radians ($\approx 3.14159\text{ rad}$).
- A quarter turn ($90^\circ$) equals $\pi / 2$ Radians ($\approx 1.57079\text{ rad}$).
+-----------------------------------------------------------------------------+
| THE CANVAS RADIAN COMPASS |
+-----------------------------------------------------------------------------+
12 o'clock
1.5 * PI (270°)
|
|
|
9 o'clock | 3 o'clock (ORIGIN)
1.0 * PI (180°) -------------+------------- 0.0 / 2.0 * PI (0° / 360°)
|
|
|
v
6 o'clock
0.5 * PI (90°)
Crucial Rule: In Canvas, $0$ Radians is always at 3 o'clock (the positive $X$-axis). Angles increase clockwise toward 6 o'clock ($0.5\pi$), 9 o'clock ($1.0\pi$), and 12 o'clock ($1.5\pi$).
Technical Deep Dive & Specifications
The ctx.arc() Method Specification
ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise);
Parameter Reference Matrix
| Parameter | Type | Required | Description & Constraints |
|---|---|---|---|
x |
number |
Yes | The $X$-coordinate of the circle's center point. |
y |
number |
Yes | The $Y$-coordinate of the circle's center point. |
radius |
number |
Yes | Distance from the center to the arc's outer edge (must be non-negative). |
startAngle |
number |
Yes | The angle in radians at which the arc begins, measured from the positive $X$-axis. |
endAngle |
number |
Yes | The angle in radians at which the arc terminates. |
counterclockwise |
boolean |
Optional | false (default) draws clockwise; true draws counter-clockwise. |
The Conversion Formula
To convert human degrees to Canvas radians:
const radians = (degrees * Math.PI) / 180;
const degToRad = deg => (deg * Math.PI) / 180;
Starting Arcs at the Top ($12\text{ O'Clock}$)
Because $0$ radians is at 3 o'clock, progress meters and clocks that start at the top ($12\text{ o'clock}$) must begin at $-0.5\pi$ (or $1.5\pi$):
const startAngle = -Math.PI / 2; // 12 o'clock
const progress = 0.75; // 75% complete
const endAngle = startAngle + (progress * 2 * Math.PI);
ctx.beginPath();
ctx.arc(100, 100, 50, startAngle, endAngle, false);
ctx.stroke();
Tangent Fillets with ctx.arcTo()
The ctx.arcTo(x1, y1, x2, y2, radius) method creates an arc that is tangent to two intersecting lines: the line from the current position to $(x_1, y_1)$, and the line from $(x_1, y_1)$ to $(x_2, y_2)$. It is ideal for constructing custom rounded rectangles:
Current Point (P0) ---------> Corner Target (P1: x1, y1)
/
(Arc / Fillet of Radius R)
/
v
Destination Target (P2: x2, y2)
function drawRoundedRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r); // Top-right corner
ctx.arcTo(x + w, y + h, x, y + h, r); // Bottom-right corner
ctx.arcTo(x, y + h, x, y, r); // Bottom-left corner
ctx.arcTo(x, y, x + w, y, r); // Top-left corner
ctx.closePath();
ctx.stroke();
}
💻 Interactive Code Playground
Starter Code
Line-by-Line Code Breakdown
- Lines 49–54: Draws the full $360^\circ$ ($0$ to $2\pi$) inactive background track for the circular meter using a dark slate stroke (
#1e293b). - Lines 57–65: Computes the dynamic sweep angle starting at $-0.5\pi$ ($12\text{ o'clock}$). Applying
ctx.lineCap = 'round'creates modern, pill-shaped rounded tips on the animated neon progress bar. - Lines 82–97: Multi-segment donut chart loop. Instead of calculating complex dual-concentric path polygons, setting
ctx.lineWidth = 26on successive stroked arcs creates instant, perfectly aligned donut slices.
Expected Browser Render Output
+-------------------------------------------------------------+
| ( CPU LOAD ) ( MEMORY ) |
| / 78% \ / [Apps] \ |
| | [===Arc===] | | [Sys] [Free] |
| \ / \ / |
| Progress Meter Donut Chart |
+-------------------------------------------------------------+🏋️ Hands-On Exercise
🎯 The Challenge: Build a Cloud Storage Multi-Segment Donut Meter with Slice Pop-Out
Instructions:
- Given four storage allocation categories:
- Videos: $40%$ (
#8b5cf6) - Images: $30%$ (
#06b6d4) - Documents: $15%$ (
#f59e0b) - Available: $15%$ (
#334155)
- Videos: $40%$ (
- Render a 2-inch wide donut meter ($r=70\text{px}$, $\text{thickness}=24\text{px}$).
- Implement slice extrusion for the "Videos" category:
- When drawing the largest category (Videos), translate its center point outwards by $8\text{px}$ along its mid-angle vector:
$dx = \cos(\text{midAngle}) \times 8$, $dy = \sin(\text{midAngle}) \times 8$.
- When drawing the largest category (Videos), translate its center point outwards by $8\text{px}$ along its mid-angle vector:
- Render category legend labels and center free capacity.
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Passing Degree Values Directly to
arc(): Callingctx.arc(x, y, r, 0, 360)treats $360$ as radians ($360\text{ radians} \approx 57\text{ full rotations}$), resulting in broken rendering. Always convert degrees:(deg * Math.PI) / 180. - Assuming $0\text{ Radians}$ is $12\text{ O'Clock}$: $0$ radians is at $3\text{ o'clock}$ (positive $X$-axis). Always subtract $\pi / 2$ (
-Math.PI / 2) if you want your gauge to start at the top. - Unwanted Connecting Segments: If a path was previously active and you call
ctx.arc(), Canvas will automatically draw a straight line from the last pen position to the start of the arc. Always callctx.beginPath()beforectx.arc().
💡 Pro Tips
- Pill-Shaped Progress Bars with
lineCap = 'round': When drawing gauge arcs with thick line widths, settingctx.lineCap = 'round'turns the sharp boxy ends of the arc into rounded caps. - Full Circle Shortcut: To draw a full circle, use
ctx.arc(x, y, radius, 0, Math.PI * 2). ctx.arcTo()for Smooth UI Corners: When drawing custom rounded rectangles, tabs, or dialogue speech bubbles,ctx.arcTo()produces mathematically smoother corners than manual Bézier approximations.
📌 Key Takeaways
- Canvas angles are measured in Radians: $360^\circ = 2\pi\text{ rad}$, $180^\circ = \pi\text{ rad}$, $90^\circ = \pi/2\text{ rad}$.
- $0$ Radians points to $3\text{ o'clock}$ ($+X$ direction); clockwise rotation moves toward $6\text{ o'clock}$.
ctx.arc(x, y, r, startAngle, endAngle, counterclockwise)defines circular trajectories.- Stroking an arc with a thick
lineWidthis the most efficient way to render donut charts and circular meters. - Always invoke
ctx.beginPath()beforectx.arc()to prevent stray connecting lines from previous sub-paths. - --