如何在p5.js中实现递归树的平滑生长(替代瞬间生成)
实现p5.js中树的平滑生长
首先咱们得先搞清楚你之前用setTimeout为啥会出问题:p5.js的绘图变换(translate、rotate这些)是和当前的绘图上下文绑定的,而setTimeout是异步执行的——当延迟的代码跑起来的时候,之前的绘图状态早就变了,而且每次redraw()都会清掉画布,之前画的分支也没了,自然会长出奇怪的树形。
下面给你两种可行的解决方案,都是利用p5.js的帧动画系统来实现自然的平滑生长:
方案一:用任务队列控制分支绘制
核心思路是把要绘制的分支任务存在一个队列里,每一帧从队列里取出几个任务来画,这样就能灵活控制生长速度,实现循序渐进的效果。
let branchQueue = []; const maxDepth = 10; function setup() { createCanvas(600, 600); background(255); strokeWeight(10); // 初始化根节点任务:记录位置、旋转角度、缩放比例、生长深度 branchQueue.push({x: width/2, y: height-20, angle: 0, scale: 1, depth: 0}); } function draw() { // 每一帧绘制3个分支,数字越大生长速度越快 for (let i = 0; i < 3; i++) { if (branchQueue.length === 0) { noLoop(); // 所有分支绘制完成后停止动画 return; } const currentBranch = branchQueue.shift(); // 取出队列首个任务 drawSingleBranch(currentBranch); } } function drawSingleBranch(branch) { push(); translate(branch.x, branch.y); rotate(branch.angle); scale(branch.scale); // 绘制当前分支线段 line(0, 0, 0, -height/10); if (branch.depth < maxDepth) { const newDepth = branch.depth + 1; const newScale = branch.scale * 0.8; // 计算当前分支顶端的全局坐标 const nextGlobalX = branch.x + (0 * branch.scale); const nextGlobalY = branch.y + (-height/10 * branch.scale); // 添加随机摆动角度 const wiggleAngle = random(-0.05, 0.05); // 60%概率触发分叉 if (random(1.0) < 0.6) { // 添加右侧分支任务 branchQueue.push({ x: nextGlobalX, y: nextGlobalY, angle: branch.angle + 0.3 + wiggleAngle, scale: newScale, depth: newDepth }); // 添加左侧分支任务 branchQueue.push({ x: nextGlobalX, y: nextGlobalY, angle: branch.angle - 0.3 + wiggleAngle, scale: newScale, depth: newDepth }); } else { // 继续笔直生长 branchQueue.push({ x: nextGlobalX, y: nextGlobalY, angle: branch.angle + wiggleAngle, scale: branch.scale, depth: branch.depth }); } } pop(); } function mouseReleased() { // 重置队列,触发重新生长 branchQueue = []; branchQueue.push({x: width/2, y: height-20, angle: 0, scale: 1, depth: 0}); background(255); loop(); }
方案二:用深度计数器逐帧递进
另一种思路是控制当前允许绘制的最大深度,每一帧缓慢增加这个深度值,让树从根节点开始,逐步向外生长:
let currentMaxDepth = 0; const maxDepth = 10; const growthSpeed = 0.3; // 每次增加的深度值,数值越小生长越平缓 function setup() { createCanvas(600, 600); background(255); strokeWeight(10); } function draw() { background(255); translate(width/2, height-20); branch(0, currentMaxDepth); // 逐步提升允许的最大深度,直到达到目标 if (currentMaxDepth < maxDepth) { currentMaxDepth += growthSpeed; } else { noLoop(); } } function branch(depth, targetDepth) { if (depth > targetDepth) return; line(0,0,0,-height/10); push(); translate(0,-height/10); rotate(random(-0.05,0.05)); if (random(1.0) < 0.6 && depth + 1 <= targetDepth) { // 绘制右侧分支 rotate(0.3); scale(0.8); branch(depth + 1, targetDepth); pop(); // 重置状态绘制左侧分支 push(); translate(0,-height/10); rotate(random(-0.05,0.05)); rotate(-0.6); scale(0.8); branch(depth + 1, targetDepth); } else { // 继续笔直生长 branch(depth, targetDepth); } pop(); } function mouseReleased() { currentMaxDepth = 0; loop(); }
你可以根据自己的需求调整生长速度:方案一修改循环内的绘制数量,方案二调整growthSpeed参数即可。
内容的提问来源于stack exchange,提问作者Rashid
相关产品推荐
相关产品推荐

