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

如何修复p5.js草图添加旋转录制视频后的圆圈闪烁故障

问题解决:p5.js添加旋转视频后圆圈闪烁故障

问题描述

我正在制作一个基于麦克风录制的声音频率和音量生成圆圈的p5.js草图。最初代码运行正常,但在中心添加旋转的录制视频后,圆圈开始出现闪烁故障。

初始正常代码

let delay = 1000;

function preload() {
  img = loadImage("mask.png");
}

function setup() {
  createCanvas(1280, 720);

  // 创建麦克风音频输入
  mic = new p5.AudioIn();
  mic.start();

  // 创建FFT对象获取声音频率
  fft = new p5.FFT();
  fft.setInput(mic);
}

function draw() {
  // 绘制黑胶底座
  noStroke();
  fill(0);
  ellipse(width / 2, height / 2, 10);
  noFill();
  stroke(200);
  strokeWeight(3);
  ellipse(width / 2, height / 2, 660);

  // 获取麦克风音量和频谱数据
  let vol = mic.getLevel();
  let spectrum = fft.analyze();

  // 获取各频段能量值
  let bass = fft.getEnergy("bass");
  let lowMid = fft.getEnergy("lowMid");
  let mid = fft.getEnergy("mid");
  let highMid = fft.getEnergy("highMid");

  // 将频率映射为圆圈颜色
  let hue1 = map(bass, 0, 255, -90, 370);
  let hue2 = map(lowMid, 0, 255, -75, 340);
  let hue3 = map(mid, 0, 255, -15, 315);
  let hue4 = map(highMid, 0, 255, 0, 85);

  // 将音量映射为圆圈线条粗细
  let circleStroke = map(vol, 0, 1, 1, 40);

  // 延迟绘制多个圆圈
  let numCircles = 16;
  for (let i = 1; i < numCircles; i++) {
    let diameter = i * 30 + 140;
    setTimeout(function () {
      strokeWeight(circleStroke);
      stroke(hue1, hue2, hue3 + hue4);
      noFill();
      ellipse(width / 2, height / 2, diameter);
    }, 1000 * i);
  }
}

添加视频后故障代码

let delay = 1000;
let capture;
let angle = 0;

function preload() {
  img = loadImage("mask.png");
}

function setup() {
  createCanvas(1280, 720);

  // 创建麦克风音频输入
  mic = new p5.AudioIn();
  mic.start();

  // 创建FFT对象获取声音频率
  fft = new p5.FFT();
  fft.setInput(mic);

  // 捕获摄像头视频
  capture = createCapture(VIDEO);
  capture.size(320, 220);
  capture.hide();
}

function draw() {
  // 绘制旋转视频
  push();
  translate(width / 2, height / 2);
  imageMode(CENTER);
  rotate(angle);
  image(capture, 0, 0);
  angle += 0.03;
  pop();
  
  // 绘制视频遮罩
  imageMode(CENTER);
  image(img, width / 2, height / 2, width, height);

  // 绘制黑胶底座
  noStroke();
  fill(0);
  ellipse(width / 2, height / 2, 10);
  noFill();
  stroke(200);
  strokeWeight(3);
  ellipse(width / 2, height / 2, 660);

  // 获取麦克风音量和频谱数据
  let vol = mic.getLevel();
  let spectrum = fft.analyze();

  // 获取各频段能量值
  let bass = fft.getEnergy("bass");
  let lowMid = fft.getEnergy("lowMid");
  let mid = fft.getEnergy("mid");
  let highMid = fft.getEnergy("highMid");

  // 将频率映射为圆圈颜色
  let hue1 = map(bass, 0, 255, -90, 370);
  let hue2 = map(lowMid, 0, 255, -75, 340);
  let hue3 = map(mid, 0, 255, -15, 315);
  let hue4 = map(highMid, 0, 255, 0, 85);

  // 将音量映射为圆圈线条粗细
  let circleStroke = map(vol, 0, 1, 1, 40);

  // 延迟绘制多个圆圈
  let numCircles = 16;
  for (let i = 1; i < numCircles; i++) {
    let diameter = i * 30 + 140;
    setTimeout(function () {
      strokeWeight(circleStroke);
      stroke(hue1, hue2, hue3 + hue4);
      noFill();
      ellipse(width / 2, height / 2, diameter);
    }, 1000 * i);
  }
}

故障原因分析

  • setTimeout与draw循环冲突:draw函数每秒执行约60次,每次都会创建15个延迟任务,大量任务堆积导致圆圈重复叠加,引发闪烁。
  • 未清屏导致画面残留:没有添加background()清除上一帧内容,视频帧和旧圆圈图形不断叠加,加剧闪烁。
  • 颜色模式不匹配:默认RGB模式下,超出0-255的色相映射值会被截断,导致颜色异常,增加视觉闪烁感。

修复后代码

let capture;
let angle = 0;
let displayedCircles = 0;
let startTime;

function preload() {
  img = loadImage("mask.png");
}

function setup() {
  createCanvas(1280, 720);
  colorMode(HSB, 360, 255, 255);
  
  mic = new p5.AudioIn();
  mic.start();

  fft = new p5.FFT();
  fft.setInput(mic);

  capture = createCapture(VIDEO);
  capture.size(320, 220);
  capture.hide();
  
  startTime = millis();
}

function draw() {
  background(0);
  
  // 绘制旋转视频
  push();
  translate(width / 2, height / 2);
  imageMode(CENTER);
  rotate(angle);
  image(capture, 0, 0);
  angle += 0.03;
  pop();
  
  // 绘制视频遮罩
  imageMode(CENTER);
  image(img, width / 2, height / 2, width, height);

  // 绘制黑胶底座
  noStroke();
  fill(0);
  ellipse(width / 2, height / 2, 10);
  noFill();
  stroke(200);
  strokeWeight(3);
  ellipse(width / 2, height / 2, 660);

  // 获取音频数据
  let vol = mic.getLevel();
  let bass = fft.getEnergy("bass");
  let lowMid = fft.getEnergy("lowMid");
  let mid = fft.getEnergy("mid");
  let highMid = fft.getEnergy("highMid");

  // 映射色相和线条粗细
  let hue = map(bass + lowMid + mid + highMid, 0, 4*255, 0, 360);
  let circleStroke = map(vol, 0, 1, 1, 40);

  // 根据时间进度显示圆圈
  let elapsedTime = millis() - startTime;
  let numCircles = 16;
  while (displayedCircles < numCircles && elapsedTime >= displayedCircles * 1000) {
    let diameter = (displayedCircles + 1) * 30 + 140;
    strokeWeight(circleStroke);
    stroke(hue, 255, 255);
    noFill();
    ellipse(width / 2, height / 2, diameter);
    displayedCircles++;
  }
}

关键修改点

  • 移除setTimeout:用millis()记录时间进度,在draw循环中控制圆圈显示时机,避免任务堆积。
  • 添加background(0)清屏:每帧清空画布,消除旧画面残留。
  • 切换到HSB颜色模式:适配色相映射逻辑,颜色显示更稳定。
  • 优化颜色计算:合并频段能量值映射为单一色相,颜色变化更自然。

内容的提问来源于stack exchange,提问作者Sara Maria Natalini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 12:57:01