如何使用大像素设置渐变动画?

Oth*_* Me 6 javascript radial-gradients html5-canvas

我正在开展一个项目,在这个项目中,我希望黑暗覆盖屏幕,黑暗中的角色会发光.我尝试使用此代码为场景设置动画,然后在其上绘制黑暗:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

这很好用,我喜欢它看起来的方式,但是当它与其他代码一起反复动画时,它显着减慢了其余部分.我认为一个可能的解决方案可能是以某种方式绘制一个具有较低"质量?"的渐变,我考虑的另一个解决方案是将此绘图保存在单独的画布中并将其绘制转换为播放器位置但这样就无法添加多种光源,我想通过简单地添加它们的效果来做.我可能只需要处理滞后问题,而且我对这个东西都是一个菜鸟,但是如果有人能帮助我那将是美妙的.

为了澄清,我在绘图循环中使用此代码,并且在每次迭代中重新计算它.我宁愿这样重新计算,所以我可以有多个移动的光源.

pea*_*iac -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);
Run Code Online (Sandbox Code Playgroud)

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

<div id="container">
    <canvas id="canvas"></canvas>
    <canvas id="lightingCanvas"></canvas>
</div>
Run Code Online (Sandbox Code Playgroud)

CSS:

#container {
     position: relative;
}
#canvas, #lightingCanvas {
    position: absolute;
    top: 0;
    left: 0;
}
#container, #canvas, #lightingCanvas {
     width: 480px;
     height: 360px;
}
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

这样,浏览器就会为您处理混合,您只需插入正确的 alpha 值 - 因此渲染现在应该更快。

这也将允许您恢复大像素 - 只需在第二个画布上使用较低的分辨率并使用一些 CSS 效果,例如在放大时使画布像素化。image-rendering: -webkit-crisp-edges