立体并排视频黑白效果闪烁问题排查求助
问题原因分析
你遇到的画面频繁切换问题,核心根源是两个按钮的点击事件各自启动了独立的无限递归setTimeout循环:
- 点击「正常」按钮时,
myFunction会持续循环调用自己,不断把原始视频帧绘制到canvas上 - 点击「黑白」按钮时,
myFunction1也会启动自己的无限循环,持续处理黑白帧并覆盖到canvas上 - 这两个循环会同时运行、交替修改canvas内容,最终导致画面在正常与黑白状态之间频繁跳变
另外代码里还有个小拼写错误:back.hight=ch应该是back.height=ch,不过这不是导致切换问题的直接原因。
修复方案
我们需要把逻辑改成单一动画循环+模式状态管理的方式:只维护一个全局渲染循环,通过变量记录当前要使用的效果模式,每次循环根据模式处理画面,避免多个循环互相干扰。
修改后的完整代码如下:
<!DOCTYPE html> <meta charset=utf-8 /> <script src="video.js" type="text/javascript"></script> <link href="video-js.css" rel="stylesheet" type="text/css"> <title>Mon projet </title> <h1> <marquee>Streaming </marquee></h1> <center> <h1> Choisir effet </h1> <span id="cvsModeLbl">Mode:</span> <input type="button" id="cvsbtnNormal" value="Normal" /> <input type="button" id="cvsbtnBW" value="Black & White" /> <h1> Vidéo Side by side</h1> <video id=v controls loop width="500" height="400"> <source src=video2.mp4 type=video/mp4> <source src=video2.webm type=video/webm> <source src=video2.ogg type=video/ogg> </video> <canvas id=c width="500" height= "400"></canvas> <style> canvas { background: black; } </style> <script> var v = document.getElementById('v'); var canvas = document.getElementById('c'); var context = canvas.getContext('2d'); var back = document.createElement('canvas'); var backcontext = back.getContext('2d'); var back1 = document.createElement('canvas'); var backcontext1 = back1.getContext('2d'); v.crossOrigin = 'anonymous'; var cw,ch; cw = v.width; ch = v.height; back.width = cw; back.height = ch; // 修复拼写错误:hight -> height back1.width = cw; back1.height = ch; // 新增全局变量:记录当前选中的效果模式 var currentEffect = 'normal'; var effectNormal = document.getElementById("cvsbtnNormal"); var effectBw = document.getElementById("cvsbtnBW"); // 按钮点击仅修改模式,不启动新循环 effectNormal.addEventListener("click", function() { currentEffect = 'normal'; }); effectBw.addEventListener("click", function() { currentEffect = 'bw'; }); // 单一渲染循环:根据当前模式处理画面 function renderFrame() { context.clearRect(0, 0, cw, ch); if (currentEffect === 'normal') { // 正常模式:直接绘制原始视频帧 context.drawImage(v, 0, 0, cw, ch); } else if (currentEffect === 'bw') { // 黑白模式:先绘制原始帧,再处理灰度效果 context.drawImage(v, 0, 0, cw, ch); var idata = context.getImageData(0, 0, cw, ch); var data = idata.data; for(var i = 0; i < data.length; i+=4) { var r = data[i], g = data[i+1], b = data[i+2]; // 使用更符合人眼视觉的灰度计算公式 var gray = r * 0.299 + g * 0.587 + b * 0.114; data[i] = gray; data[i+1] = gray; data[i+2] = gray; } context.putImageData(idata, 0, 0); } // 用requestAnimationFrame替代setTimeout,动画更流畅、性能更优 requestAnimationFrame(renderFrame); } // 启动渲染循环 renderFrame(); </script>
关键改进点说明
- 单一渲染循环:使用
requestAnimationFrame替代setTimeout,它是浏览器专门为动画优化的API,能和屏幕刷新率同步,动画更流畅且性能更稳定 - 模式状态管理:通过
currentEffect变量统一控制当前渲染效果,按钮点击仅修改这个变量,不会启动多个冲突的循环 - 修复拼写错误:修正了
back.hight的拼写问题 - 优化灰度计算:使用符合人眼视觉感受的灰度公式(0.299R + 0.587G + 0.114B),比简单的
(r+g+b)/3效果更自然
修改后点击按钮切换效果时,画面会稳定显示对应模式,不会再出现频繁跳变的问题。
内容的提问来源于stack exchange,提问作者Amel maouche
相关产品推荐
相关产品推荐

