如何在p5.js中使用鼠标绘制平滑曲线
问题分析与解决方案
你遇到的核心问题是历史点存储重复和二次贝塞尔曲线参数逻辑错误,导致无法生成平滑曲线,以下是具体修复方案:
1. 核心问题拆解
(1)历史点数组冗余
在draw函数中,你每次按下鼠标时会同时推入pmouseX/Y和mouseX/Y两个点,导致数组中出现连续重复的点(如[p1, p2, p2, p3, p3, p4...]),后续计算曲线时相当于在同一点间绘制,自然呈现直线。
(2)二次贝塞尔曲线使用错误
p5.js的quadraticVertex(cpX, cpY, endX, endY)参数顺序是控制点坐标在前,终点坐标在后,你之前的循环逻辑和终点处理不符合这个规则,导致曲线无法正确连接。
2. 完整修复代码
let history = []; const lineWeight = 10; function setup() { createCanvas(window.innerWidth, window.innerHeight); background(255); } function drawLine(x1, y1, x2, y2) { strokeWeight(lineWeight); stroke(0); line(x1, y1, x2, y2); } function keyPressed() { if (key === " ") { background(255); // 按顺序重绘原始线条,添加递增延迟实现动画 for (let i = 1; i < history.length; i++) { setTimeout(() => { drawLine(history[i-1][0], history[i-1][1], history[i][0], history[i][1]); }, i * 10); } } } function draw() { if (mouseIsPressed) { const lastPoint = history[history.length - 1]; // 仅添加与上一个点不同的位置,避免重复 if (!lastPoint || (mouseX !== lastPoint[0] && mouseY !== lastPoint[1])) { drawLine(mouseX, mouseY, pmouseX, pmouseY); history.push([mouseX, mouseY]); // 限制历史点数量,防止内存占用过高 history = history.slice(-1000); } } } function mouseReleased() { if (history.length < 2) return; // 至少需要2个点才能生成曲线 beginShape(); noFill(); stroke('red'); strokeWeight(2); // 起始点 vertex(history[0][0], history[0][1]); // 绘制中间平滑段:以当前点为控制点,当前点与下一个点的中点为终点 for (let i = 1; i < history.length - 1; i++) { const midX = (history[i][0] + history[i + 1][0]) / 2; const midY = (history[i][1] + history[i + 1][1]) / 2; quadraticVertex(history[i][0], history[i][1], midX, midY); } // 连接到最后一个点 const lastIdx = history.length - 1; quadraticVertex(history[lastIdx][0], history[lastIdx][1], history[lastIdx][0], history[lastIdx][1]); endShape(); }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js" integrity="sha512-DWtDo/6AXxH1t9p7GCWqmC4XTVK/eje94XTV6QYB39rGllLN8Tr3izDf6lkmebgqRnYh4wtSFm4CvBoA9SrdpA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
3. 关键修复说明
- 历史点去重:只存储与上一个位置不同的鼠标坐标,确保数组中每个点都是有效移动节点。
- 曲线逻辑修正:严格遵循p5.js的
quadraticVertex参数规则,用相邻点的中点作为曲线段终点,当前点作为控制点,实现平滑过渡。 - 动画优化:将
setTimeout的延迟改为i * 10,确保线条按绘制顺序逐段显示,而非同时触发。
内容的提问来源于stack exchange,提问作者Vixey
相关产品推荐
相关产品推荐

