如何使用JavaScript中的requestAnimationFrame()减慢动画播放速度

时间:2019-02-21 13:25:19

标签: javascript html animation requestanimationframe

现在,我正在用JavaScript编写Conway的生活游戏,而我遇到的问题是使用requestAnimationFrame()进行动画处理。当我在浏览器中加载游戏时,动画在一瞬间就运行得太快了。我想知道是否有人可以提供有关如何减慢动画速度以便可以看到动画正常运行的建议?

JavaScript代码如下:

$(function(event) {

    var GRIDWIDTH = 400;
    var GRIDHEIGHT = 400;
    var gameGrid = createGrid(GRIDWIDTH); //Grid used to display the grid
    var newGameGrid = createGrid(GRIDWIDTH); //Grid used to update game state

    //creates the grid for the game, using an array of empty arrays
    function createGrid(rows) {
      var gridArray = []; //is the game grid
      //for loop that creates the grid array, each element is an empty array to create the multi-dimensional array
      for (var i = 0; i < rows; i++) {
        gridArray[i] = [];
      }
    return gridArray;
    }

    //will populate the grid randomly with alive and dead cells
    function populateGrid() {
      for (var rows = 0; rows < GRIDHEIGHT; rows++) { //goes across the rows within the grid
        for (var cols = 0; cols < GRIDWIDTH; cols++) { //goes across the columns within the grid
            var cell = Math.floor(Math.random() * 2 ); //the cells of the grid is either a 1 or 0 (alive or dead), chosen at random
            if (cell === 1) { //if the cell variable is the same as the integer value 1 and same type int then the element at gameGrid[rows][cols] is set to a 1 else a 0, this is done randomly.
            gameGrid[rows][cols] = 1;
          }
            else {
            gameGrid[rows][cols] = 0;
          }
        }
      }
    }

    //Will draw the game grid on the screen including cells
    function drawGrid() {
      // var gameCanvas = $("#gameCanvas"); //gets the gameCanvas element
      var ctx = $("#gameCanvas")[0].getContext("2d");
      ctx.clearRect(0, 0, 400, 400);
      //both loops go through the rows and columns respectively and draws the pixel in the grid in the specified colour
      for (var rows = 1; rows < GRIDHEIGHT; rows++) {
        for (var cols = 1; cols < GRIDWIDTH; cols++) {
          if (gameGrid[rows][cols] === 1) { //if the element is a 1 the pixel is coloured (red) to represent it is alive
            ctx.fillStyle = "#FF0000";
            ctx.fillRect(rows, cols, 1, 1);
          }
        }
      }
    }

    //this function will update the grid to show the new position of the pixels
    function updateGrid() {
      for (var rows = 1; rows < GRIDHEIGHT - 1; rows++) {
        for (var cols = 1; cols < GRIDWIDTH - 1; cols++) {
          var totalNeighbours = 0; //holds the total neighbours a cell has
          //calculations to add the neighbours
          totalNeighbours += gameGrid[rows-1][cols-1]; //top left
          totalNeighbours += gameGrid[rows-1][cols]; //top center
          totalNeighbours += gameGrid[rows-1][cols+1] //top right

          totalNeighbours += gameGrid[rows][cols-1] //middle left
          totalNeighbours += gameGrid[rows][cols+1] //middle right

          totalNeighbours += gameGrid[rows+1][cols-1] //bottom left
          totalNeighbours += gameGrid[rows+1][cols] //bottom center
          totalNeighbours += gameGrid[rows+1][cols+1] //bottom right

          //Game of life rules:

          //alive cell rules
          if (gameGrid[rows][cols] === 1) {
            switch(totalNeighbours) {
              //rule 1 any live cell with fewer than two live neighbours dies, as if by underpopulation
              case totalNeighbours < 2:
                newGameGrid[rows][cols] = 0;
                break;
              //rule 2 any live cell with two or three live neighbours lives on to the next generation
              case totalNeighbours == 2:
              case totalNeighbours == 3:
                newGameGrid[rows][cols] = 1;
                break;
              //rule 3 any live cell with more than three live neighbours dies, as if by overpopulation
              case totalNeighbours > 3:
                newGameGrid[rows][cols] = 0;
                break;
            }
          }
          //dead cell rule 4 any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction
          else if (gameGrid[rows][cols] === 0) {
            if (totalNeighbours == 3) {
              newGameGrid[rows][cols] = 1;
              }
            }
          }
        }
        //iterate through the rows and columns and gameGrid is set to the newGameGrid with the updated cells in the grid
        for (var rows = 0; rows < GRIDWIDTH; rows++) {
          for (var cols = 0; cols < GRIDHEIGHT; cols++) {
            gameGrid[rows][cols] = newGameGrid[rows][cols];
          }
        }
      }

      populateGrid();
      start();
      function start() {
        drawGrid();
        updateGrid();
        requestAnimationFrame(start);
      }

    })
The html is simple as follows:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Conway's Game Of Life</title>
    <script src="http://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="../js/index.js"></script>
    <link rel="stylesheet" href="../css/index.css">
    <link href="https://fonts.googleapis.com/css?family=Anton" rel="stylesheet">
  </head>
  <body>
    <div class="gameScreen">
      <div class="title">John Conway's Game Of Life</div>
      <canvas id="gameCanvas" width="400" height="400" style="border:1px solid #000000;"></canvas>
    </div>
  </body>
</html>

2 个答案:

答案 0 :(得分:0)

另请参见

this answer

我认为您应该将两次渲染之间经过的时间与速度因子结合起来使用。

例如,您可以定义希望主角在一秒钟内移动200px。然后-使用渲染之间的经过时间,您可以计算出要将他移动多远(模型代码):

var distance_to_move = 200px * time_since_last_render / 1000;
setNewPosition();
render();

因此,如果自上次渲染以来经过的时间是500毫秒,那么在此渲染中,您会将他移动100像素。

答案 1 :(得分:0)

这是使用requestAnimationFrame的方式:传递给它的函数接受一个参数,即时间戳。您可以使用它来计算经过的时间。在此示例中,该步长设置为2s(2000ms)。请注意,您需要以requestAnimationFrame(start);而不是start();开头:

$(function(event) {

    var GRIDWIDTH = 400;
    var GRIDHEIGHT = 400;
    var gameGrid = createGrid(GRIDWIDTH); //Grid used to display the grid
    var newGameGrid = createGrid(GRIDWIDTH); //Grid used to update game state

    //creates the grid for the game, using an array of empty arrays
    function createGrid(rows) {
      var gridArray = []; //is the game grid
      //for loop that creates the grid array, each element is an empty array to create the multi-dimensional array
      for (var i = 0; i < rows; i++) {
        gridArray[i] = [];
      }
      return gridArray;
    }

    //will populate the grid randomly with alive and dead cells
    function populateGrid() {
      for (var rows = 0; rows < GRIDHEIGHT; rows++) { //goes across the rows within the grid
        for (var cols = 0; cols < GRIDWIDTH; cols++) { //goes across the columns within the grid
            var cell = Math.floor(Math.random() * 2 ); //the cells of the grid is either a 1 or 0 (alive or dead), chosen at random
            if (cell === 1) { //if the cell variable is the same as the integer value 1 and same type int then the element at gameGrid[rows][cols] is set to a 1 else a 0, this is done randomly.
            gameGrid[rows][cols] = 1;
          }
            else {
            gameGrid[rows][cols] = 0;
          }
        }
      }
    }

    //Will draw the game grid on the screen including cells
    function drawGrid() {
      // var gameCanvas = $("#gameCanvas"); //gets the gameCanvas element
      var ctx = $("#gameCanvas")[0].getContext("2d");
      ctx.clearRect(0, 0, 400, 400);
      //both loops go through the rows and columns respectively and draws the pixel in the grid in the specified colour
      for (var rows = 1; rows < GRIDHEIGHT; rows++) {
        for (var cols = 1; cols < GRIDWIDTH; cols++) {
          if (gameGrid[rows][cols] === 1) { //if the element is a 1 the pixel is coloured (red) to represent it is alive
            ctx.fillStyle = "#FF0000";
            ctx.fillRect(rows, cols, 1, 1);
          }
        }
      }
    }

    //this function will update the grid to show the new position of the pixels
    function updateGrid() {
      for (var rows = 1; rows < GRIDHEIGHT - 1; rows++) {
        for (var cols = 1; cols < GRIDWIDTH - 1; cols++) {
          var totalNeighbours = 0; //holds the total neighbours a cell has
          //calculations to add the neighbours
          totalNeighbours += gameGrid[rows-1][cols-1]; //top left
          totalNeighbours += gameGrid[rows-1][cols]; //top center
          totalNeighbours += gameGrid[rows-1][cols+1] //top right

          totalNeighbours += gameGrid[rows][cols-1] //middle left
          totalNeighbours += gameGrid[rows][cols+1] //middle right

          totalNeighbours += gameGrid[rows+1][cols-1] //bottom left
          totalNeighbours += gameGrid[rows+1][cols] //bottom center
          totalNeighbours += gameGrid[rows+1][cols+1] //bottom right

          //Game of life rules:

          //alive cell rules
          if (gameGrid[rows][cols] === 1) {
            switch(totalNeighbours) {
              //rule 1 any live cell with fewer than two live neighbours dies, as if by underpopulation
              case totalNeighbours < 2:
                newGameGrid[rows][cols] = 0;
                break;
              //rule 2 any live cell with two or three live neighbours lives on to the next generation
              case totalNeighbours == 2:
              case totalNeighbours == 3:
                newGameGrid[rows][cols] = 1;
                break;
              //rule 3 any live cell with more than three live neighbours dies, as if by overpopulation
              case totalNeighbours > 3:
                newGameGrid[rows][cols] = 0;
                break;
            }
          }
          //dead cell rule 4 any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction
          else if (gameGrid[rows][cols] === 0) {
            if (totalNeighbours == 3) {
              newGameGrid[rows][cols] = 1;
              }
            }
          }
        }
        //iterate through the rows and columns and gameGrid is set to the newGameGrid with the updated cells in the grid
        for (var rows = 0; rows < GRIDWIDTH; rows++) {
          for (var cols = 0; cols < GRIDHEIGHT; cols++) {
            gameGrid[rows][cols] = newGameGrid[rows][cols];
          }
        }
      }

      populateGrid();
      requestAnimationFrame(start);

      var startTime = null, stepInMs = 2000, drawCount = 0;
      function start(timestamp) {
          var progress;
          if (startTime === null){
              startTime = timestamp;
          }
          progress = timestamp - startTime;
          if (progress > stepInMs) {
              drawCount++;
              document.getElementById('drawCount').innerHTML = drawCount;
              startTime = timestamp;
              drawGrid();
              updateGrid();
          }
          requestAnimationFrame(start);
      }

    })
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Conway's Game Of Life</title>
    <script src="http://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="../js/index.js"></script>
    <link rel="stylesheet" href="../css/index.css">
    <link href="https://fonts.googleapis.com/css?family=Anton" rel="stylesheet">
  </head>
  <body>
    <div class="gameScreen">
      <div class="title">John Conway's Game Of Life - count: <span id="drawCount">0</span></div>
      <canvas id="gameCanvas" width="400" height="400" style="border:1px solid #000000;"></canvas>
    </div>
  </body>
</html>

编辑:添加了一个跨度以显示重新绘制,并带有一个计数来演示,您的代码中可能存在错误,因为它在重新绘制时仅更改了2次。.