Canvas 递归绘制随机分形树
用 Canvas 递归分形画随机树,LCG 伪随机保证可复现,点击换一棵,并适配高清屏 DPR。
#canvas #recursion #generative-art
Canvas 递归绘制随机分形树
分形树是 Canvas 入门的经典练习:从树干出发,每画一节枝条就递归分叉,直到深度耗尽。加上伪随机种子,每次点击都能生成形态各异但风格统一的树。下面 demo 支持点击换树、窗口缩放高清重绘。
核心链路
1. 递归分形:depth 驱动一切
drawBranch(x, y, len, angle, depth, maxDepth) 是核心函数:
- 画当前枝段(线宽由
depth / maxDepth的平方决定,主干粗、末梢细) - 按深度决定分叉数量:主干区 2~3 叉,末梢 0~1 叉
- 子枝长度乘以
0.63~0.78的随机缩放,逐层缩短 depth <= 2时在末梢稀疏开花
较粗的枝段还有概率从线段中途再分叉,让树形更自然。
2. 角度约束:树枝只朝上半平面
Canvas 的 Y 轴向下,所以 sin(angle) <= 0 表示朝上。clampAngleUpward 把角度限制在 [-π, 0],避免枝条插进地面:
function clampAngleUpward(angle) {
let a = angle
while (a <= -Math.PI) a += Math.PI * 2
while (a > Math.PI) a -= Math.PI * 2
if (a > 0) a = -a
return a
}
3. LCG 伪随机:可复现的「随机」
用线性同余生成器(LCG)代替 Math.random(),同一 seed 下每次绘制结果一致;点击画布时重置 seed,得到新树:
function rand() {
seed = (seed * 16807 + 0) % 2147483647
return (seed - 1) / 2147483646
}
4. devicePixelRatio 高清适配
设置 canvas.width = cssWidth * dpr,再用 ctx.setTransform(dpr, 0, 0, dpr, 0, 0) 缩放坐标系,Retina 屏上线条不会发虚。resize 时按容器尺寸重算并整棵重绘。
在线体验
点击下方画布可重新生成一棵树。也可 全屏预览 打开独立页面。
正在加载随机树…
小结
随机分形树的套路很固定:递归 + 深度递减 + 随机参数。难点在于细节——线宽平滑过渡、中途分叉、角度约束、末梢开花,这些让「算法树」看起来像「自然树」。如果你要做自己的生成艺术,可以从固定 maxDepth 和单一分叉规则开始,再逐步加随机性和装饰。