如何修复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
相关产品推荐
相关产品推荐

