如何为具有大像素的渐变设置动画?

时间:2019-03-01 03:52:23

标签: javascript html5-canvas radial-gradients

我正在一个项目中,我希望黑暗覆盖屏幕,而角色却在黑暗中发光。我尝试为场景设置动画,然后使用以下代码在其上绘制黑暗:

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;

var pixelSize = 30;
var width = canvasWidth/pixelSize;
var height = canvasHeight/pixelSize;

var lightX = canvasWidth/2;
var lightY = canvasHeight/2;

var lightDiameter = 100;
var a = lightDiameter*pixelSize;

for(var x = 0; x < width; x++) {
  for(var y = 0; y < height; y++) {
    var alpha = 1.25 - a/(Math.pow(x*30 - lightX, 2) + Math.pow(y*30 - 
lightY, 2));
    ctx.fillStyle = "rgba( 25, 25, 30," + alpha + ")";
    ctx.fillRect(x*pixelSize, y*pixelSize, pixelSize, pixelSize);
  }
}

这很好用,我喜欢它的外观,但是当它与其他代码一起重复制作动画时,会大大降低其余部分的速度。我认为可能的解决方案可能是以某种方式绘制具有较低“质量”的渐变,我考虑过的另一种解决方案是将该图形保存在单独的画布中,并将其转换为玩家位置,但这将导致无法添加我想通过简单地添加它们的效果来实现多种光源。我可能只需要处理滞后问题,而我对这个问题是个菜鸟,但是如果有人可以帮助我,那真是太好了。

为澄清起见,我在绘图循环中使用此代码,并且在每次迭代中都会对其进行重新计算。我希望以这种方式重新计算,以便可以有多个移动的光源。

1 个答案:

答案 0 :(得分:-1)

这是因为与其他方法相比,fillRect的运行速度很慢。您可能可以改用ImageData对象来加快处理速度。

执行此操作的方法是将所有内容渲染到画布上,获取相应的ImageData,修改其内容并将其放回画布上:

var ctx = canvas.getContext("2d");
// render stuff here
var imageData = ctx.getImageData(0,0,canvasWidth,canvasHeight);
for (let x=0;x<canvasWidth;x++){
    for (let y=0;y<canvasHeight;y++){
        let i = (x+y*canvasWidth)*4;
        let alpha = calculateAlpha(x,y); // your method here (should result in a value between 0 and 1)
        imageData.data[i] = (1-alpha)*imageData.data[i]+alpha*25;
        imageData.data[i+1] = (1-alpha)*imageData.data[i+1]+alpha*25;
        imageData.data[i+2] = (1-alpha)*imageData.data[i+2]+alpha*30;
        imageData.data[i+3] = 1-(1-alpha)*(1-imageData.data[i+3]);
    }
}
ctx.putImageData(imageData,0,0);

这应该按像素进行照明,并且比一直使用clearRect要快得多。但是,它可能仍然使速度变慢,因为您每帧都要进行大量计算。在这种情况下,您可以通过使用CSS在位于主画布上方的第二个画布中进行照明来加快照明速度:

<div id="container">
    <canvas id="canvas"></canvas>
    <canvas id="lightingCanvas"></canvas>
</div>

Css:

#container {
     position: relative;
}
#canvas, #lightingCanvas {
    position: absolute;
    top: 0;
    left: 0;
}
#container, #canvas, #lightingCanvas {
     width: 480px;
     height: 360px;
}

Javascript:

var canvas = document.getElementById("lightingCanvas")
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgb(25,25,30)";
ctx.fillRect(0,0,canvas.width,canvas.height);
var imageData = ctx.getImageData(0,0,canvasWidth,canvasHeight);
for (let x=0;x<canvasWidth;x++){
    for (let y=0;y<canvasHeight;y++){
        let i = (x+y*canvasWidth)*4;
        let alpha = calculateAlpha(x,y); // your method here (should result in a value between 0 and 1)
        imageData.data[i+3] = 255*alpha;
    }
}
ctx.putImageData(imageData,0,0);

通过这种方式,浏览器可以为您处理混合,您只需要插入正确的alpha值-因此现在渲染速度应该更快。

这也将允许您将大像素带回-只需在第二个画布上使用较低的分辨率,并使用诸如image-rendering : -webkit-crisp-edges之类的css效果即可使画布在放大时像素化。