首页 > 解决方案 > 如何为大像素的渐变设置动画?

问题描述

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

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);
  }
}

这工作得很好,我喜欢它的外观,但是当它与其他代码一起重复动画时,它显着减慢了其余代码。我认为一个可能的解决方案可能是以某种方式绘制具有较低“质量?”的渐变,我考虑的另一个解决方案是将此绘图保存在单独的画布中并将其绘制到播放器位置,但这将使其无法添加多个光源,我想通过简单地添加它们的效果来做到这一点。我可能只需要处理滞后问题,而且我对这些东西很陌生,但如果有人能帮助我,那就太好了。

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

标签: javascripthtml5-canvasradial-gradients

解决方案


这是因为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 值 - 所以现在渲染应该更快。

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


推荐阅读