为什么这个html5 canvas动画如此密集?

时间:2018-08-06 09:11:02

标签: javascript html5 canvas html5-canvas

我使用画布创建此动画,并将svg转换为画布形状。大多数情况下,它会加热我的计算机,并且风扇开始运转。

只是想知道代码,html5 canvas,canvas路径或动画递归中是否有如此密集的内容?

在codepen上查看:https://codepen.io/benbyford-the-lessful/pen/ZjjVdR?editors=1010#

    // check program is being run

console.log('bg animation running...');


// setup canvas
var canvas = document.getElementById('bgCanvas');
var ctx = canvas.getContext('2d')

// redo this - canvas size
//
var width = window.innerWidth,
    height = window.innerHeight;

canvas.width = width * 2;
canvas.height = height * 2;

var gridSquareWidth = 20;
var gridWidth = (width * 2) / gridSquareWidth,
    gridHeight = (height * 2) / gridSquareWidth;



var grid = [];

// create default grid array
for (var x = 0; x < gridWidth; x++) {
    grid[x] = [];

    for (var y = 0; y < gridHeight; y++) {
        var rand  = getRandomArbitrary(0,5);
        var rand2  = getRandomArbitrary(0,2);

        if(rand2 == 1 || x < (gridWidth / 4) || x > (gridWidth / 2) || y < (gridHeight / 4) || y > (gridHeight / 2)){
            rand--;
        }

        if(rand > 2) grid[x][y] = 1;
    }
}


//
// main update function
//
var animationSpeed = 0.1;
var animationSpeedCount = 0;
var running = true;
function update(dt) {

    if(running){
        animationSpeedCount += dt;
        if(animationSpeedCount > animationSpeed){

            moveGrid();
            animationSpeedCount = 0;
        }

        draw();
    }
}

var noOfFrames = 3;
var waveOffset = 15;
var increment = 0;
function moveGrid() {

    var x = increment;
    var x2 = increment - noOfFrames - waveOffset;

    // add frmae wave
    for (var i = 0; i < noOfFrames; i++) {
        moveONeFrameForward(x, true);
        x--;
    }
    // go back frmae wave
    for (var i = 0; i < noOfFrames; i++) {
        moveONeFrameForward(x2, false);
        x2--;
    }

    // var x column, add of subtract by 1
    function moveONeFrameForward(x, add){

        if(x < 0){
            x = Math.ceil(gridWidth + x);
        }

        if(x > 0 && x < gridWidth){

            for (var y = 0; y < gridHeight; y++) {
                if(grid[x][y] > 0){
                    if(add){
                        grid[x][y] = grid[x][y] + 1;
                    }else{
                        if(grid[x][y] > 1) grid[x][y] = grid[x][y] - 1;
                    }
                }
            }
        }
    }

    // increment column
    increment += 1;
    if(increment > gridWidth){
        increment = 0;

        // stop running
        // running = false;
    }
}

var fills = ["#eeeeee","#efefef","#fefefe","#ffffff"];

function draw() {
    // clear canvas to white
    ctx.fillStyle = '#dddddd';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    for (var x = 0; x < gridWidth; x++) {
        for (var y = 0; y < gridHeight; y++) {

            var offsetX = x * gridSquareWidth;
            var offsetY = y * gridSquareWidth;
            var frame = 0;

            switch (grid[x][y]) {
                case 1:
                    frame = 1
                    break;
                case 2:
                    frame = 2;
                    break;
                case 3:
                    frame = 3;
                    break;
                case 4:
                    frame = 4;
                    break;
                default:
            }
            if(frame) drawframe(ctx, frame, offsetX, offsetY, fills);
        }
    }
}


// The main game loop
var lastTime = 0;
function gameLoop() {
    var now = Date.now();
    var dt = (now - lastTime) / 1000.0;

    update(dt);

    lastTime = now;
    window.requestAnimationFrame(gameLoop);
};

// start game
gameLoop();


//
// UTILITIES
//

// cross browser requestAnimationFrame - https://gist.github.com/mrdoob/838785
if ( !window.requestAnimationFrame ) {

    window.requestAnimationFrame = ( function() {

        return window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(
            /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
            window.setTimeout( callback, 1000 / 60 );
        };

    })();
}

function getRandomArbitrary(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
}


var frame1Center = 4.1;
var frame2Center = 2.1;
function drawframe(ctx, frame, x, y, fills) {

    ctx.strokeStyle = 'rgba(0,0,0,0)';
    ctx.lineCap = 'butt';
    ctx.lineJoin = 'miter';
    ctx.miterLimit = 4;
    ctx.fillStyle = fills[frame-1];

    switch (frame) {
        case 1:
            ctx.beginPath();
            ctx.moveTo(3.1+x+frame1Center,0+y);
            ctx.lineTo(0.6+x+frame1Center,0+y);
            ctx.bezierCurveTo(0.3+x+frame1Center,0+y,0+x+frame1Center,0.3+y,0+x+frame1Center,0.6+y);
            ctx.lineTo(0.3+x+frame1Center,12.1+y);
            ctx.bezierCurveTo(0.3+x+frame1Center,12.4+y,0.6+x+frame1Center,12.7+y,0.8999999999999999+x+frame1Center,12.7+y);
            ctx.lineTo(3.4+x+frame1Center,12.7+y);
            ctx.bezierCurveTo(3.6999999999999997+x+frame1Center,12.7+y,4+x+frame1Center,12.399999999999999+y,4+x+frame1Center,12.1+y);
            ctx.lineTo(4+x+frame1Center,0.6+y);
            ctx.bezierCurveTo(4.1+x+frame1Center,0.3+y,3.7+x+frame1Center,0+y,3.1+x+frame1Center,0+y);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            break;
        case 2 || 6:
            ctx.beginPath();
            ctx.moveTo(4.4+x+frame2Center,0+y);
            ctx.bezierCurveTo(3.1+x+frame2Center,0+y,0+x+frame2Center,0.8+y,0+x+frame2Center,2.1+y);
            ctx.bezierCurveTo(0+x+frame2Center,3.4000000000000004+y,0.3+x+frame2Center,12.5+y,1.6+x+frame2Center,12.799999999999999+y);
            ctx.bezierCurveTo(2.8+x+frame2Center,13+y,6+x+frame2Center,12+y,6+x+frame2Center,10.7+y);
            ctx.bezierCurveTo(6+x+frame2Center,9.1+y,5.7+x+frame2Center,0+y,4.4+x+frame2Center,0+y);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            break;
        case 3 || 5:
            ctx.beginPath();
            ctx.moveTo(5.2+x,0 +y);
            ctx.bezierCurveTo(7.5 +x,0+y,9.3+x,6.5+y,9.3 +x,8.7+y);
            ctx.bezierCurveTo(9.3+x,10.899999999999999+y,6.300000000000001+x,12.799999999999999+y,4.1000000000000005+x,12.799999999999999+y);
            ctx.bezierCurveTo(1.9000000000000004+x,12.799999999999999+y,0+x,6.3+y,0+x,4.1+y);
            ctx.bezierCurveTo(0+x,1.8999999999999995+y,3+x,0+y,5.2+x,0+y);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            break;
        case 4:
            ctx.beginPath();
            ctx.arc(5.9+x,6.3+y,5.8,0,6.283185307179586,true);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            break;
        default:
    }
};

1 个答案:

答案 0 :(得分:2)

很难确切地说出“为什么”,但是肯定有一些地方可以改进。

  • 首先,您绘制的图形是所需图形的两倍。
    将画布大小设置为渲染的大小,您可能会发现性能有了很大的改善。

  • 然后,您在每次绘制时都绘制了很多子路径(并且为上下文设置了很多次属性)
    您可以尝试将所有这些子路径合并为更大的子路径,并按fillStyle分组,以使光栅化器每帧仅工作四次。这样也可以提高性能。

但是我个人会采取的方法是在4个不同的画布上预渲染所有4个不同的状态。然后,仅使用 drawImage 绘制所需的条。
最好的情况是,您最终只有4次调用drawImage,而一次却只有8次调用。

这是粗略的概念证明:

// setup canvas
var canvas = document.getElementById('bgCanvas');
var ctx = canvas.getContext('2d')

// don't set it twice as big as needed
var width = window.innerWidth,
  height = window.innerHeight;

canvas.width = width;
canvas.height = height;

var gridSquareWidth = 10;
var gridWidth = (width) / gridSquareWidth,
  gridHeight = (height) / gridSquareWidth;

var grid = [];

// create default grid array
for (var x = 0; x < gridWidth; x++) {
  grid[x] = [];

  for (var y = 0; y < gridHeight; y++) {
    var rand = getRandomArbitrary(0, 5);
    var rand2 = getRandomArbitrary(0, 2);

    if (rand2 == 1 || x < (gridWidth / 4) || x > (gridWidth / 2) || y < (gridHeight / 4) || y > (gridHeight / 2)) {
      rand--;
    }

    if (rand > 2) grid[x][y] = 1;
  }
}

var fills = ["#eeeeee", "#efefef", "#fefefe", "#ffffff"];

var frame1Center = 4.1;
var frame2Center = 2.1;

// the 4 points drawers
var drawers = [draw0, draw1, draw2, draw3];
// initialise our four possible states
var states = [
  initState(0),
  initState(1),
  initState(2),
  initState(3)
];

//
// main update function
//
var running = true;
var speed = 2;

var waveWidth = 200;
var waveMargin = gridSquareWidth * 4;
var waveStart = 0;
var waveEnd = waveWidth;

// start game
update();

function initState(status) {
  var c = canvas.cloneNode();
  var ctx = c.getContext('2d');
  ctx.scale(0.5, 0.5); // to circumvent values being set for scale(2)

  ctx.beginPath(); // single path
  ctx.fillStyle = fills[status];
  for (var x = 0; x < gridWidth; x++) {
    for (var y = 0; y < gridHeight; y++) {
      if (grid[x][y]) {
        drawers[status](ctx, x * gridSquareWidth * 2, y * gridSquareWidth * 2);
      }
    }
  }
  ctx.fill(); // single fill

  return c;
}

function draw0(ctx, x, y) {
  ctx.moveTo(3.1 + x + frame1Center, 0 + y);
  ctx.lineTo(0.6 + x + frame1Center, 0 + y);
  ctx.bezierCurveTo(0.3 + x + frame1Center, 0 + y, 0 + x + frame1Center, 0.3 + y, 0 + x + frame1Center, 0.6 + y);
  ctx.lineTo(0.3 + x + frame1Center, 12.1 + y);
  ctx.bezierCurveTo(0.3 + x + frame1Center, 12.4 + y, 0.6 + x + frame1Center, 12.7 + y, 0.8999999999999999 + x + frame1Center, 12.7 + y);
  ctx.lineTo(3.4 + x + frame1Center, 12.7 + y);
  ctx.bezierCurveTo(3.6999999999999997 + x + frame1Center, 12.7 + y, 4 + x + frame1Center, 12.399999999999999 + y, 4 + x + frame1Center, 12.1 + y);
  ctx.lineTo(4 + x + frame1Center, 0.6 + y);
  ctx.bezierCurveTo(4.1 + x + frame1Center, 0.3 + y, 3.7 + x + frame1Center, 0 + y, 3.1 + x + frame1Center, 0 + y);
  ctx.closePath();
}

function draw1(ctx, x, y) {
  ctx.moveTo(4.4 + x + frame2Center, 0 + y);
  ctx.bezierCurveTo(3.1 + x + frame2Center, 0 + y, 0 + x + frame2Center, 0.8 + y, 0 + x + frame2Center, 2.1 + y);
  ctx.bezierCurveTo(0 + x + frame2Center, 3.4000000000000004 + y, 0.3 + x + frame2Center, 12.5 + y, 1.6 + x + frame2Center, 12.799999999999999 + y);
  ctx.bezierCurveTo(2.8 + x + frame2Center, 13 + y, 6 + x + frame2Center, 12 + y, 6 + x + frame2Center, 10.7 + y);
  ctx.bezierCurveTo(6 + x + frame2Center, 9.1 + y, 5.7 + x + frame2Center, 0 + y, 4.4 + x + frame2Center, 0 + y);
  ctx.closePath();
}

function draw2(ctx, x, y) {
  ctx.moveTo(5.2 + x, 0 + y);
  ctx.bezierCurveTo(7.5 + x, 0 + y, 9.3 + x, 6.5 + y, 9.3 + x, 8.7 + y);
  ctx.bezierCurveTo(9.3 + x, 10.899999999999999 + y, 6.300000000000001 + x, 12.799999999999999 + y, 4.1000000000000005 + x, 12.799999999999999 + y);
  ctx.bezierCurveTo(1.9000000000000004 + x, 12.799999999999999 + y, 0 + x, 6.3 + y, 0 + x, 4.1 + y);
  ctx.bezierCurveTo(0 + x, 1.8999999999999995 + y, 3 + x, 0 + y, 5.2 + x, 0 + y);
  ctx.closePath();
}

function draw3(ctx, x, y) {
  ctx.moveTo(5.9 + x, 6.3 + y);
  ctx.arc(5.9 + x, 6.3 + y, 5.8, 0, 2 * Math.PI);
}


function update(dt) {

  if (running) {
    draw();
    moveGrid();
  }
  window.requestAnimationFrame(update);
}


function moveGrid() {
  waveStart = (waveStart + speed) % canvas.width;
  waveEnd = (waveStart + waveWidth) % canvas.width;
}

function draw() {
  ctx.fillStyle = '#dddddd';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  var x = 0;
  // the roll logic is a bit dirty... sorry.
  if (waveEnd < waveStart) {
    x = waveEnd - waveWidth;
    drawStrip(1, x, waveMargin);

    x = waveEnd - waveWidth + waveMargin;
    drawStrip(3, x, (waveWidth - (waveMargin * 2)));

    x = waveEnd - waveMargin;
    drawStrip(2, x, waveMargin);
    x = waveEnd;
  }

  drawStrip(0, x, waveStart - x);
  drawStrip(1, waveStart, waveMargin);
  drawStrip(3, waveStart + waveMargin, waveWidth - (waveMargin * 2));
  drawStrip(2, waveStart + (waveWidth - waveMargin), waveMargin);
  drawStrip(0, waveEnd, canvas.width - Math.max(waveEnd, waveStart));
}

function drawStrip(state, x, w) {
  if(x < 0) w = w + x;
  if (w <= 0) return;
  x = Math.max(x, 0);
  ctx.drawImage(states[state],
    Math.max(x, 0), 0, w, canvas.height,
    Math.max(x, 0), 0, w, canvas.height
  );

}



function getRandomArbitrary(min, max) {
  return Math.floor(Math.random() * (max - min) + min);
}
:root,body,canvas {margin: 0}
<canvas id="bgCanvas"></canvas>