我正在尝试渲染一些火焰,但是我无法使用 putImageData() 实现透明画布,导致黑色画布但绘制良好的火焰。
我尝试了“globalcompositeoperation”“source-over”和“globalAlpha”但无济于事。
唯一的问题是画布绘制了黑色背景,而不是用透明画布背景渲染火焰。
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;
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];
data[index] = colors[value][0];
data[++index] = colors[value][1];
data[++index] = colors[value][2];
data[++index] = 255;
}
}
if (intensity == null) {
if (Math.random() > 0.95) {
randomizeThreshold();
}
}
ctx.putImageData(imageData, 0, 0);
};
window.requestAnimationFrame(burnBurnBurn);
});
<canvas id="fire"></canvas>
因为你是在整个画布上操作并设置其 alpha 值,所以你应该坚持渲染火焰的小区域!
因此,这三个改变是必要的...
var canvasHeight = canvas.height-430;
var imageData = ctx.getImageData(0, 430, canvasWidth, canvasHeight);
ctx.putImageData(imageData, 0, 430);
“430”是你的“神奇”数字......你的火焰高度,所以只坚持那个区域!
这是完整的程序...
<!DOCTYPE html>
<head>
<style type="text/css">
</style>
</head>
<body onload="Init();">
<script type="text/javascript">
function Init(){
var canvas = document.getElementById('fire');
var ctx = canvas.getContext('2d');
var canvasWidth = canvas.width;
var canvasHeight = canvas.height-430;
var intensity = null;
var fps = 30;
var threshold = 0.5;
var imageData = ctx.getImageData(0, 430, canvasWidth, canvasHeight);
var data = imageData.data;
var numPixels = data.length / 4;
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];
data[index] = colors[value][0];
data[++index] = colors[value][1];
data[++index] = colors[value][2];
data[++index] =255;
}
}
if (intensity == null) {
if (Math.random() > 0.95) {
randomizeThreshold();
}
}
ctx.putImageData(imageData, 0, 430);
};
window.requestAnimationFrame(burnBurnBurn);
}
</script>
<canvas id="fire" width="500" height="500" style="border:1px solid silver;"></canvas>
</body>
</html>
顺便说一句,你永远不应该在循环内声明标识符,否则你会耗尽堆栈。
检查每个循环并将标识符声明移到循环之外。