Как я могу анимировать градиент с большими пикселями? - PullRequest
0 голосов
/ 01 марта 2019

Я работаю над проектом, в котором я хотел бы, чтобы тьма покрывала экран, а персонаж светился в темноте.Я попытался анимировать сцену, затем нарисовал тьму над ней, используя этот код:

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 голосов
/ 02 марта 2019

Это потому, что 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);

Таким образом, браузер позаботится о смешивании для вас, и вам просто нужно подключить правильные альфа-значения - так что рендеринг должен быть еще быстрее.

Это также позволит вамчтобы вернуть большие пиксели - просто используйте более низкое разрешение на втором холсте и используйте некоторый эффект CSS, такой как image-rendering: -webkit-crisp-edges, чтобы сделать пикселизированный холст при увеличении.

...