✨Koch snowflake
Last updated
Last updated
⬆️ 需要: Vector, drawOnCanvas2D()
const { PI } = Math;
const { log } = console;
// degrees
function deg(x) { return PI * x / 180 }
// draw on canvas
drawOnCanvas2D('#playground', (c) => {
// Draw a level-n Koch snowflake fractal,
// with lower-left corner at (x,y) and side length len.
function snowflake(n, x, y, len) {
// save current transformation matrix
// because:
// - we're going to translate the matrix
// - calling `leg()` will change the matrix
c.save();
c.translate(x, y); // translate coord system to (x,y)
c.moveTo(0, 0); // begin a new subpath
// draw 3 legs
[0, -120, -120].forEach(angle => {
c.rotate(deg(angle));
leg(n);
})
// close subpath
c.closePath();
// restore original transformation matrix before calling `snowflake()`
c.restore();
// Draw a single leg of a level-n Koch snowflake.
// - translates coordinate system to the end of leg.
// - easily call rotate() after drawing a leg.
function leg(n) {
// base case:
// ---------------------------------
if (n === 0) { c.lineTo(len, 0) } // - just a horizontal line
// recursive case:
// ---------------------------------
// - draw 4 sublegs like:  ̄\/ ̄
else {
// save the current transformation matrix
// because we are going to "shrink" the matrix now
c.save();
c.scale(1 / 3, 1 / 3); // shrink the matrix
// 2nd, 3rd, 4th sublegs
[0, 60, -120, 60].forEach(angle => {
c.rotate(deg(angle));
leg(n - 1);
})
// restore the original transformation matrix
c.restore();
}
// translate coord system to end of leg.
c.translate(len, 0);
}
}
const side = 125; // starting side length
const dx = 25; // padding between 2 snowflakes
const P = vec(dx, side); // starting point
const v = vec(side + dx, 0); // vector for translation
// add snowflake subpaths from level 0 to level 4
for(let i = 0; i < 5; i++) {
snowflake(i, ...P.add(v.times(i)).coords, side);
}
// draw all snowflakes
c.fillStyle = 'hsl(120 80% 50% / 0.3)';
c.fill();
c.stroke();
});
replit:canvas Koch snowflake