Canvas循环绘制时阴影过度叠加问题求助
Canvas循环绘制时阴影过度叠加异常
单次绘制带阴影效果的圆角矩形时显示正常,但在循环处理视频帧的场景下,背景会出现阴影过度叠加的问题。
单次绘制正常代码示例
export const titleRender = async () => { const canvas = createCanvas(1920, 1080); const ctx = canvas.getContext('2d'); const img = await loadImage('blueBackground.png') ctx.drawImage(img, 0, 0, 1920, 1920); return canvas; }; const drawEffects = async () => { const effectsCanvas = createCanvas(1920, 1080) const ctx = effectsCanvas.getContext('2d'); ctx.beginPath(); ctx.roundRect(77 , 234 , 1766 , 846,[20, 20, 0, 0]); ctx.closePath() // 应用阴影效果 ctx.shadowColor = '#00ff44'; ctx.shadowBlur = 68; ctx.shadowOffsetX = 5; ctx.shadowOffsetY = 20; // 设置背景色并填充矩形 ctx.fillStyle = '#ffaadd'; ctx.fill(); ctx.strokeStyle = 'rgba(26, 26, 26, 0.46)'; ctx.lineWidth = 2; ctx.stroke(); // 重置阴影设置 ctx.shadowColor = 'transparent'; ctx.shadowBlur = 0; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; ctx.clip() return effectsCanvas; } async function test() { const img = await loadImage('overlayimage.jpg') const backgroundCanvas= createCanvas(1920, 1080) const titleComposition = await titleRender(); const effectsCanvas = await drawEffects() const backgroundCTX = backgroundCanvas.getContext('2d'); const effectsCTX = effectsCanvas.getContext('2d'); backgroundCTX.drawImage(titleComposition, 0, 0, titleComposition.width, titleComposition.height) effectsCTX.drawImage(img, 77 , 234 , 1766 , 846 ) backgroundCTX.drawImage(effectsCanvas, 0, 0, effectsCanvas.width, effectsCanvas.height) const out = fs.createWriteStream(`comp/ren${1}.png`); const stream = backgroundCanvas.createPNGStream(); stream.pipe(out); out.on('finish', () => console.log('Wows')) return true; } test()
运行效果:圆角矩形后方有平滑绿色阴影,显示正常。
循环绘制异常代码示例
async function test() { const img = await loadImage('green/overlay.jpg') const backgroundCanvas= createCanvas(1920, 1080) const titleComposition = await titleRender(); const effectsCanvas = await drawEffects() const backgroundCTX = backgroundCanvas.getContext('2d'); const effectsCTX = effectsCanvas.getContext('2d'); backgroundCTX.drawImage(titleComposition, 0, 0, titleComposition.width, titleComposition.height) for (let i = 0; i<=20; i++) { effectsCTX.drawImage(img, 77 , 234 , 1766 , 846 ) backgroundCTX.drawImage(effectsCanvas, 0, 0, effectsCanvas.width, effectsCanvas.height) const out = fs.createWriteStream(`comp/ren${1}.png`); const stream = backgroundCanvas.createPNGStream(); stream.pipe(out); out.on('finish', () => console.log('Wows')) } return true; } test()
运行效果:阴影出现过度叠加的异常。
问题原因
effectsCanvas重复绘制未清空:循环中每次调用effectsCTX.drawImage时,都是在同一个effectsCanvas上叠加绘制,之前的阴影和图像会保留,导致每一轮循环都叠加一次阴影。backgroundCanvas未重置:循环中每次向backgroundCanvas绘制effectsCanvas时,没有先清空画布,导致每帧的阴影都叠加在之前的内容上。- 文件名未使用循环变量:原代码中写入的文件都是
ren${1}.png,会重复覆盖同一文件,无法观察到每帧的变化。
解决方案
方案1:每次循环重新创建画布
在循环内部重新创建背景画布和效果画布,确保每帧都是全新的绘制环境:
async function test() { const img = await loadImage('green/overlay.jpg') const titleComposition = await titleRender(); for (let i = 0; i<=20; i++) { // 每帧重新创建画布实例 const backgroundCanvas= createCanvas(1920, 1080) const effectsCanvas = await drawEffects() const backgroundCTX = backgroundCanvas.getContext('2d'); const effectsCTX = effectsCanvas.getContext('2d'); // 重新绘制基础背景 backgroundCTX.drawImage(titleComposition, 0, 0, titleComposition.width, titleComposition.height) effectsCTX.drawImage(img, 77 , 234 , 1766 , 846 ) backgroundCTX.drawImage(effectsCanvas, 0, 0, effectsCanvas.width, effectsCanvas.height) // 使用循环变量命名文件,避免覆盖 const out = fs.createWriteStream(`comp/ren${i}.png`); const stream = backgroundCanvas.createPNGStream(); stream.pipe(out); out.on('finish', () => console.log(`第${i}帧处理完成`)) } return true; } test()
方案2:循环内清空画布(性能更优)
如果不想重复创建画布实例,可以在每帧绘制前清空已有画布内容:
async function test() { const img = await loadImage('green/overlay.jpg') const backgroundCanvas= createCanvas(1920, 1080) const titleComposition = await titleRender(); for (let i = 0; i<=20; i++) { // 重新创建效果画布,或用clearRect清空已有内容 const effectsCanvas = await drawEffects() const backgroundCTX = backgroundCanvas.getContext('2d'); const effectsCTX = effectsCanvas.getContext('2d'); // 清空背景画布 backgroundCTX.clearRect(0, 0, backgroundCanvas.width, backgroundCanvas.height); // 重新绘制基础背景 backgroundCTX.drawImage(titleComposition, 0, 0, titleComposition.width, titleComposition.height) effectsCTX.drawImage(img, 77 , 234 , 1766 , 846 ) backgroundCTX.drawImage(effectsCanvas, 0, 0, effectsCanvas.width, effectsCanvas.height) const out = fs.createWriteStream(`comp/ren${i}.png`); const stream = backgroundCanvas.createPNGStream(); stream.pipe(out); out.on('finish', () => console.log(`第${i}帧处理完成`)) } return true; } test()
额外优化点
如果drawEffects的绘制逻辑固定,可以将阴影矩形的绘制结果缓存为静态画布,循环中只需要复用该画布并叠加图像,减少重复绘制阴影的开销。
内容的提问来源于stack exchange,提问作者Aviato
相关产品推荐
相关产品推荐

