You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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);
      }
    }
  };
};

关键思路解析

  1. 点的时间戳管理:不再只存坐标,而是给每个点添加创建时间,这样能精准计算每个点的存在时长,对应透明度的变化。
  2. 动态透明度计算:用sk.map()把点的存在时长映射到0-255的alpha区间,sk.constrain()避免出现负数或超出范围的透明度值。
  3. 性能优化:过滤掉已经超过fadeTime的点,防止数组无限膨胀,减少不必要的绘制计算。

内容的提问来源于stack exchange,提问作者Pandemonium

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:54:49