我有一棵发光树
背景
突然有一天,我想要用canvas绘制一棵二叉树,最好能来点发光特效。
绘制发光树思路
- 递归绘制,根据输入起点坐标、长度、倾斜角度、深度等信息绘制树干
- 每次绘制,长度、宽度、深度递减,深度为0时终止
具体实现
1、改变坐标系
众所周知,canvas的坐标原点是在左上角,X轴正方形向右,Y轴正方形下下。这个设置和向上生长的树有些差异,所以,我们简单更改一下坐标系。
2、绘制函数
/**
* 绘制树
* @param {Number} startX 起点X坐标
* @param {Number} startY 起点Y坐标
* @param {Number} length 树干长度
* @param {Number} angle 倾斜角度,用弧度计算
* @param {Number} depth 当前深度,为0时终止
**/
function drawTree(startX, startY, length, angle, depth) {
// TODO
}
3、每一根树干的起终点,小学生都会的数学计算
// 已知 startX,startY, angle, length
const endX = startX + Math.cos(angle) * length;
const endY = startY + Math.sin(angle) * length;
4、随机颜色,最简单的随机rgb
function random(n) {
return Math.floor(Math.random() * n);
}
function randomColor() {
return `rgb(${random(255)}, ${random(255)}, ${random(255)})`
}
5、手把手教学,canvasAPI
// 开始绘制
ctx.beginPath();
// 移动到起点位置
ctx.moveTo(startX, startY);
// 画线到终点位置
ctx.lineTo(endX, endY);
const color = randomColor();
// 设置画笔颜色
ctx.strokeStyle = color;
// 设置限宽,随深度降低而变细
ctx.lineWidth = depth * 1.5;
// 设置一下连接处的样式
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// 发光吧少年
ctx.shadowBlur = 25;
ctx.shadowColor= color;
// 完成
ctx.stroke();
5、递归搞起来
// 从第二次开始,每层偏转30度
const branchAngle = Math.PI / 6;
// 修改这个值可以控制每个分支的长度缩放比例
const branchLength = 0.7;
// 左分枝
drawTree(endX, endY, length * branchLength, angle - branchAngle, depth - 1);
// 右分枝
drawTree(endX, endY, length * branchLength, angle + branchAngle, depth - 1);
6、别忘记了首次调用
function reset() {
const { innerWidth: w, innerHeight: h, devicePixelRatio: dpr } = window;
canvas.width = w * dpr;
canvas.height = h * dpr;
// 改变坐标系
ctx.translate(canvas.width / 2, canvas.height);
ctx.scale(1, -1);
const width = canvas.width;
const height = canvas.height;
const startX = 0;
const startY = height / 3;
const length = Math.min(w, h, 600) / 2;
const angle = Math.PI / 2;
const depth = 10;
ctx.clearRect(-canvas.width / 2, 0, canvas.width, canvas.height);
drawTree(startX, startY, length, angle, depth);
}