使用putImageData实现Canvas火焰透明背景的问题求助
解决Canvas火焰效果背景不透明问题
问题根源
- 初始获取的
imageData所有像素默认是(0,0,0,255)(黑色不透明),未初始化透明背景 putImageData直接覆盖画布像素,不受globalCompositeOperation或globalAlpha参数影响,所以调整这两个属性无效- 代码仅更新火焰区域的颜色值,无火焰区域的alpha通道仍保持255,显示为黑色
修改方案
1. 初始化透明背景
获取imageData后,遍历所有像素将alpha通道设为0:
var imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight); var data = imageData.data; // 初始化所有像素为完全透明 for (var i = 0; i < data.length; i += 4) { data[i + 3] = 0; }
2. 动态控制像素透明度
在绘制火焰的循环中,仅对有火焰的像素设置不透明alpha,无火焰区域保持透明:
var skipRows = 2; for (var y = skipRows; y < canvasHeight; ++y) { for (var x = 0; x < canvasWidth; ++x) { var index = y * canvasWidth * 4 + x * 4; var value = fire[(y - skipRows) * canvasWidth + x]; if (value > 0) { data[index] = colors[value][0]; data[++index] = colors[value][1]; data[++index] = colors[value][2]; data[++index] = 255; } else { // 无火焰时设置为透明 data[index + 3] = 0; } } }
完整修改后的JS代码
document.addEventListener("DOMContentLoaded", function() { var canvas = document.getElementById('fire'); var ctx = canvas.getContext('2d'); var canvasWidth = canvas.width; var canvasHeight = canvas.height; var intensity = null; var fps = 30; var threshold = 0.5; var imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight); var data = imageData.data; var numPixels = data.length / 4; // 初始化所有像素为透明 for (var i = 0; i < data.length; i += 4) { data[i + 3] = 0; } var colors = []; for (var i = 0; i < 256; i++) { var color = []; color[0] = color[1] = color[2] = 0; colors[i] = color; } for (var i = 0; i < 32; ++i) { colors[i][2] = i << 1; colors[i + 32][0] = i << 3; colors[i + 32][2] = 64 - (i << 1); colors[i + 64][0] = 255; colors[i + 64][1] = i << 3; colors[i + 96][0] = 255; colors[i + 96][1] = 255; colors[i + 96][2] = i << 2; colors[i + 128][0] = 255; colors[i + 128][1] = 255; colors[i + 128][2] = 64 + (i << 2); colors[i + 160][0] = 255; colors[i + 160][1] = 255; colors[i + 160][2] = 128 + (i << 2); colors[i + 192][0] = 255; colors[i + 192][1] = 255; colors[i + 192][2] = 192 + i; colors[i + 224][0] = 255; colors[i + 224][1] = 255; colors[i + 224][2] = 224 + i; } var fire = []; for (var i = 0; i < canvasWidth * canvasHeight; i++) { fire[i] = 0; } var time = new Date().getTime(); function randomizeThreshold() { threshold += Math.random() * 0.2 - 0.1; threshold = Math.min(Math.max(threshold, 0.5), 0.8); } function burnBurnBurn() { window.requestAnimationFrame(burnBurnBurn); var now = new Date().getTime(); dt = now - time; if (dt < (1000 / fps)) return; time = now; var bottomLine = canvasWidth * (canvasHeight - 1); for (var x = 0; x < canvasWidth; x++) { var value = 0; if (Math.random() > threshold) value = 255; fire[bottomLine + x] = value; } var value = 0; for (var y = 0; y < canvasHeight; ++y) { for (var x = 0; x < canvasWidth; ++x) { if (x == 0) { value = fire[bottomLine]; value += fire[bottomLine]; value += fire[bottomLine - canvasWidth]; value /= 3; } else if (x == canvasWidth - 1) { value = fire[bottomLine + x]; value += fire[bottomLine - canvasWidth + x]; value += fire[bottomLine + x - 1]; value /= 3; } else { value = fire[bottomLine + x]; value += fire[bottomLine + x + 1]; value += fire[bottomLine + x - 1]; value += fire[bottomLine - canvasWidth + x]; value /= 4; } if (value > 1) value -= 1; value = Math.floor(value); var index = bottomLine - canvasWidth + x; fire[index] = value; } bottomLine -= canvasWidth; } var skipRows = 2; for (var y = skipRows; y < canvasHeight; ++y) { for (var x = 0; x < canvasWidth; ++x) { var index = y * canvasWidth * 4 + x * 4; var value = fire[(y - skipRows) * canvasWidth + x]; if (value > 0) { data[index] = colors[value][0]; data[++index] = colors[value][1]; data[++index] = colors[value][2]; data[++index] = 255; } else { data[index + 3] = 0; } } } if (intensity == null) { if (Math.random() > 0.95) { randomizeThreshold(); } } ctx.putImageData(imageData, 0, 0); }; window.requestAnimationFrame(burnBurnBurn); });
HTML代码(无需修改)
<canvas id="fire"></canvas>
内容的提问来源于stack exchange,提问作者Leather
相关产品推荐
相关产品推荐

