我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!
点生线,线生面,只要有了基本的构图元素,我们可以用canvas画出各种图案,画棵树简直就是小case了。
画树的思路很简单,大树干分叉出若干的树枝,以不同角度和长度延伸出去并逐渐变细,树枝的末端再延伸树枝,所以很容易想到用递归来实现。
所以递归函数里是在画一层树枝,需要的参数就是:
startX:树枝起始的x轴坐标
startY:树枝起始的y轴坐标
length:长度
angle:分叉角度
depth:剩余层数
branchWidth:该层的树枝粗细
于是我们可以实现画出躯干的函数:
drawTree(startX, startY, length, angle, depth, branchWidth) {
let _this = this;
var newLength,
newAngle,
newDepth,
endX,
endY,
maxAngle = (2 * Math.PI) / 5,
subBranches;
//开始绘制路径
_this.ctx.beginPath();
_this.ctx.moveTo(startX, startY);
endX = startX + Math.cos(angle) * length;
endY = startY + Math.sin(angle) * length;
_this.ctx.lineCap = "round";
_this.ctx.lineWidth = branchWidth;
_this.ctx.lineTo(endX, endY);
_this.ctx.strokeStyle = "#442525"; // 树干棕色
_this.ctx.stroke();
if (depth <= 4) {
_this.leafArr.push([endX, endY]);
}
newDepth = depth - 1;
if (newDepth <= 0) {
return; // 如果绘制到叶子节点后就结束递归
}
subBranches = _this.getRandomInt(2, 3);
branchWidth *= 0.75;
for (var i = 0; i < subBranches; i++) {
newLength = length * 0.75 + 0.25 * length * Math.random();
newAngle = angle + Math.random() * maxAngle - maxAngle * 0.5;
_this.drawTree(endX, endY, newLength, newAngle, newDepth, branchWidth);
}
}
然后是画树叶,树叶是分布在比较末端的树枝周围的,所以用leafArr数组记录一下靠末端树枝路径的终结点坐标,然后画出叶子:
drawAllLeaves() {
let _this = this;
_this.leafArr.forEach(item => {
_this.drawLeaf(item[0], item[1], _this.getRandomInt(15, 25));
});
},
drawLeaf(x, y, num) {
let _this = this;
for (let i = 0; i < num; i++) {
x += (Math.random() - 0.5) * _this.getRandomInt(0, 25);
y += (Math.random() - 0.5) * _this.getRandomInt(0, 25);
_this.ctx.beginPath();
let num1 = Math.random();
_this.ctx.fillStyle =
"rgba(247," +
(Math.random() * 190) +
",190," +
(num1 >= 0.5 ? num1 - 0.2 : num1) +
")";
_this.ctx.arc(x, y, _this.getRandomInt(2, 5), 0, 2 * Math.PI);
_this.ctx.fill();
}
}