javascript - 如何为大像素的渐变设置动画?
问题描述
我正在做一个项目,我想让黑暗覆盖屏幕,让角色在黑暗中发光。我尝试为场景设置动画,然后使用以下代码在其上绘制黑暗:
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);
}
}
这工作得很好,我喜欢它的外观,但是当它与其他代码一起重复动画时,它显着减慢了其余代码。我认为一个可能的解决方案可能是以某种方式绘制具有较低“质量?”的渐变,我考虑的另一个解决方案是将此绘图保存在单独的画布中并将其绘制到播放器位置,但这将使其无法添加多个光源,我想通过简单地添加它们的效果来做到这一点。我可能只需要处理滞后问题,而且我对这些东西很陌生,但如果有人能帮助我,那就太好了。
为了澄清,我在绘图循环中使用此代码,并且在每次迭代中都重新计算它。我宁愿以这种方式重新计算,这样我就可以拥有多个移动的光源。
解决方案
这是因为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
推荐阅读
- python - 使用curve_fit进行多元高斯拟合的问题
- python - 如何拆分具有多个定界符的字符串,但每个定界符仅一次?Python
- reactjs - React-Bootstrap 轮播滑块:如何检测显示中的当前幻灯片
- mongodb - 使用 mongoimport 时,可以指定 _id 应该是 UUID 吗?
- javascript - 如何使用字典在 JavaScript 返回值中实现回调
- c++ - 参考偏移量中看似无关的规范
- c# - 如何以管理员身份使用windows服务启动软件?
- html - 里面的引号
显示为“”或类似字符的元素
- visual-studio - 调试和构建 Web 成功但发布失败视觉 2019
- azure-log-analytics - T-SQL queries in Azure Log Analytics Workspaces?