如何用p5.js基于p5.Vector数组实现带渐隐拖尾的curveVertex?
首先,我明白你想要的是鼠标拖尾用curveVertex实现平滑曲线的同时,从新到旧自然渐隐,而不是直接消失。核心问题是原来的代码只移除旧点,没有给每个点的透明度做动态计算——我们需要给每个拖尾点加上时间戳,根据它存在的时长来调整透明度,再结合curveVertex的特性来实现效果。
下面是两种可行的方案,你可以根据需求选择:
方案1:平滑带状渐隐拖尾(视觉效果更好)
这个方案用TRIANGLE_STRIP绘制带状拖尾,每个顶点的透明度随时间递减,同时保持曲线的平滑性:
const trail = sk => { let points = []; // 存储点的位置和时间戳 let fadeTime = 1000; // 拖尾完全消失的时长(毫秒) let trailWidth = 8; // 拖尾的宽度 sk.setup = () => { sk.createCanvas(300, 600); sk.noStroke(); // 用填充色实现渐变拖尾 }; sk.draw = () => { sk.background(255); // 白色背景,可根据需求修改 // 记录鼠标按下时的点(如果要无按压拖尾,直接移除mouseIsPressed判断即可) if (sk.mouseIsPressed) { points.push({ pos: sk.createVector(sk.mouseX, sk.mouseY), time: sk.millis() }); } // 移除已经完全透明的点,避免数组无限增长 points = points.filter(point => sk.millis() - point.time < fadeTime); // 绘制渐隐拖尾 if (points.length > 1) { sk.beginShape(sk.TRIANGLE_STRIP); for (let i = 0; i < points.length; i++) { const currentPoint = points[i]; // 计算当前点的透明度:从255(新点)降到0(旧点) const elapsedTime = sk.millis() - currentPoint.time; const alpha = sk.constrain(sk.map(elapsedTime, 0, fadeTime, 255, 0), 0, 255); sk.fill(0, alpha); // 黑色拖尾,可修改颜色 // 计算拖尾的左右偏移方向,让拖尾跟随鼠标移动方向平滑变化 let direction; if (i === 0) { // 第一个点用下一个点的方向 direction = points[i+1].pos.copy().sub(currentPoint.pos).rotate(sk.HALF_PI).normalize().mult(trailWidth/2); } else { // 其他点用当前点和前一个点的方向 direction = currentPoint.pos.copy().sub(points[i-1].pos).rotate(sk.HALF_PI).normalize().mult(trailWidth/2); } // 添加拖尾左右两侧的顶点 sk.vertex(currentPoint.pos.x + direction.x, currentPoint.pos.y + direction.y); sk.vertex(currentPoint.pos.x - direction.x, currentPoint.pos.y - direction.y); } sk.endShape(); } }; };
方案2:线条式渐隐拖尾(贴合你原本的curveVertex需求)
如果更倾向于线条形态的拖尾,我们可以通过绘制多条长度递减、透明度递减的曲线叠加,模拟渐隐效果:
const trail = sk => { let points = []; let fadeTime = 1000; sk.setup = () => { sk.createCanvas(300, 600); sk.noFill(); sk.strokeWeight(2); }; sk.draw = () => { sk.background(255); if (sk.mouseIsPressed) { points.push({ pos: sk.createVector(sk.mouseX, sk.mouseY), time: sk.millis() }); } // 清理完全透明的点 points = points.filter(point => sk.millis() - point.time < fadeTime); // 用curveVertex绘制渐隐线条 if (points.length > 3) { // curveVertex至少需要4个点才能生成平滑曲线 // 从最新的点往旧点遍历,绘制多条曲线,每条透明度递减 for (let i = points.length - 1; i >= 3; i--) { const elapsedTime = sk.millis() - points[i].time; const alpha = sk.constrain(sk.map(elapsedTime, 0, fadeTime, 255, 0), 0, 255); sk.stroke(0, alpha); sk.beginShape(); // 添加曲线的起始控制点(第一个点) sk.curveVertex(points[0].pos.x, points[0].pos.y); // 绘制从第一个点到当前点的曲线段 for (let j = 0; j <= i; j++) { sk.curveVertex(points[j].pos.x, points[j].pos.y); } // 添加曲线的结束控制点(当前点) sk.curveVertex(points[i].pos.x, points[i].pos.y); sk.endShape(); } } else if (points.length > 1) { // 点数量不足时,用普通线条过渡,保证拖尾连续 for (let i = 1; i < points.length; i++) { const elapsedTime = sk.millis() - points[i].time; const alpha = sk.constrain(sk.map(elapsedTime, 0, fadeTime, 255, 0), 0, 255); sk.stroke(0, alpha); sk.line(points[i-1].pos.x, points[i-1].pos.y, points[i].pos.x, points[i].pos.y); } } }; };
关键思路解析
- 点的时间戳管理:不再只存坐标,而是给每个点添加创建时间,这样能精准计算每个点的存在时长,对应透明度的变化。
- 动态透明度计算:用
sk.map()把点的存在时长映射到0-255的alpha区间,sk.constrain()避免出现负数或超出范围的透明度值。 - 性能优化:过滤掉已经超过
fadeTime的点,防止数组无限膨胀,减少不必要的绘制计算。
内容的提问来源于stack exchange,提问作者Pandemonium
相关产品推荐
相关产品推荐

