【问题标题】:Body animation isn't smooth身体动画不流畅
【发布时间】:2015-11-28 16:37:52
【问题描述】:

以下代码中的“spaceShip”在按住任意箭头键开始时移动不顺畅。它移动了一步,冻结了一秒钟,然后“平稳地”移动​​。我怎样才能让它从一开始就顺利移动,而不是“冻结”?

我的代码:

<!doctype html>
<html>
<head>
    <meta http-content="Content-type" charset="utf-8">
    <title>Make body move smoothly</title>
    <style type="text/css">
        body {
        }

        canvas {
            margin: auto;
            position: absolute;
            left: 0;
            top: 0;
            right: 0;
            bottom: 0;
            border: 1px solid black;
        }

    </style>
</head>
<body>
<canvas id="canvas" width="400" height="600"></canvas>
<script type="text/javascript">

// Set up requestAnimationFrame and cancelAnimationFrame
(function() {
    var lastTime =0;
    var vendors=['ms', 'moz', 'webkit', 'o'];
    for(var x=0; x<vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame=window[vendors[x]+'RequestAnimationFrame'];
        window.cancelAnimationFrame =
        window[vendors[x]+ 'CancelAnimationFrame'] ||
        window[vendors[x] +'CancelRequestAnimationFrame'];
    }
    if (!window.requestAnimationFrame)
    window.requestAnimationFrame=function(callback, element) {
        var currTime =new Date().getTime();
        var timeToCall =Math.max(0, 16 - (currTime - lastTime));
        var id =window.setTimeout(function() { callback(currTime+timeToCall); },
        timeToCall);
        lastTime =currTime + timeToCall;
        return id;
    };
    if (!window.cancelAnimationFrame)
    window.cancelAnimationFrame=function(id) {
        clearTimeout(id);
    };
}());


    var canvas;
    var ctx;
    // ship data
    var shipPositionX = document.getElementById('canvas').width/2;
    var shipPositionY = document.getElementById('canvas').height - 30;
    var deltaShipPositionX = 10;
    var deltaShipPositionY = 10;

    function init() {
        canvas = document.getElementById('canvas');
        ctx = canvas.getContext('2d');

        draw();
    }

    function draw(){
        clear();
        createRectangleToCoverCanvas();
        createSpaceShip(shipPositionX, shipPositionY, 10);

        requestAnimationFrame(draw);
    }

    function clear(){
        ctx.clearRect(0, 0, document.getElementById('canvas').width, document.getElementById('canvas').height);
    }

    function createRectangleToCoverCanvas(){
        ctx.fillStyle = 'black';
        ctx.strokeStyle = 'black';
        ctx.beginPath();
        ctx.rect(0, 0, canvas.width, canvas.height);
        ctx.closePath();
        ctx.fill();
        ctx.stroke();
    }

    function createSpaceShip(x, y, radius) {
        ctx.fillStyle = 'white'
        ctx.strokeStyle = 'white'
        ctx.beginPath();
        ctx.rect(x, y, 20, 20);
        ctx.closePath();
        ctx.fill();
        ctx.stroke();
    }

    function moveSpaceShip(event){
        switch(event.keyCode){
            // left
            case 37:
                if(shipPositionX - deltaShipPositionX + 15 > 0){
                    shipPositionX -= deltaShipPositionX;
                }
            break;

            // up
            case 38:
                if(shipPositionY - deltaShipPositionY + 15 > 0){
                    shipPositionY -= deltaShipPositionY;
                }
            break;

            // right
            case 39:
                if(shipPositionX + deltaShipPositionX < document.getElementById('canvas').width){
                    shipPositionX += deltaShipPositionX;
                }
            break;

            //down
            case 40:
                if(shipPositionY + deltaShipPositionY < document.getElementById('canvas').height){
                    shipPositionY += deltaShipPositionY;
                }
            break;
        }

    }

    window.addEventListener('load', init);
    window.addEventListener('keydown', moveSpaceShip, true);
</script>
</body>
</html>

注意我的代码和这个例子的区别:http://atomicrobotdesign.com/blog_media/sprite_sheet/spritesheet.html

看看这个例子是如何流畅的,但我的“太空船”不是吗? 为什么会发生,我该如何解决?是不是因为示例使用了精灵(但这似乎没有多大意义)?

【问题讨论】:

    标签: javascript html animation canvas


    【解决方案1】:

    问题是您要等待每个keydown 事件来更新船舶位置。
    keydown 事件在再次触发之前有一个延迟:您在开始时遇到的延迟以及您在每次重绘时遇到的跳跃。

    这里的解决方案是在keydown 上触发移动并在keyup 上释放它。这样,您一按按钮,您的船就会平稳移动。

    // Im' assuming most of visitors here have recent browsers, so I removed the rAF polyfill for readibility
    
    // If you wrap it after the canvas element decalaration, you can already populate this variable, it will avoid that you make a lot of calls to document.getElementById()
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    // ship data
    var shipPositionX = canvas.width / 2;
    // Just for the snippet height
    var shipPositionY = 0;
    var deltaShipPositionX = 10;
    var deltaShipPositionY = 10;
    
    //Removed the init() function, since our elements are loaded.
    
    
    function draw() {
      clear();
      createRectangleToCoverCanvas();
      createSpaceShip(shipPositionX, shipPositionY, 10);
    }
    
    function clear() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
    }
    
    function createRectangleToCoverCanvas() {
      ctx.fillStyle = 'black';
      ctx.strokeStyle = 'black';
      ctx.beginPath();
      ctx.rect(0, 0, canvas.width, canvas.height);
      ctx.fill();
      ctx.stroke();
    }
    
    function createSpaceShip(x, y, radius) {
      ctx.fillStyle = 'white'
      ctx.strokeStyle = 'white'
      ctx.beginPath();
      ctx.rect(x, y, 20, 20);
      ctx.fill();
      ctx.stroke();
    }
    // instantiate a variable that will store our animationFrame id, so we can cancel it further
    var raf, 
    // the direction object, with an x and y values
        direction = {
          x: 0,
          y: 0
        };
    // we can set a speed variable
    var speed = 2.5;
    function triggerMoveSpaceShip(event) {
      switch (event.keyCode) {
        // left
        case 37:
          // update the direction object
          direction.x = -speed;
          // avoid the scroll in the snippet
          event.preventDefault();
          break;
    
          // up
        case 38:
          direction.y = -speed;
          event.preventDefault();
          break;
    
          // right
        case 39:
          direction.x = speed;
          event.preventDefault();
          break;
    
          //down
        case 40:
          direction.y = speed;
          event.preventDefault();
          break;
      }
      // if we haven't initiated the animation yet, and that our direction is not 0, then do it now
      if (!raf && (direction.x || direction.y)) moveSpaceShip();
    }
    
    function releaseMoveSpaceShip(event) {;
      switch (event.keyCode) {
        // left
        case 37:
          //reset this direction
          direction.x = 0;
          break;
    
          // up
        case 38:
          direction.y = 0;
          break;
    
          // right
        case 39:
          direction.x = 0;
          break;
    
          //down
        case 40:
          direction.y = 0;
          break;
      }
      if (!direction.x && !direction.y) {
        // if none of the directions is set, stop the animation
        cancelAnimationFrame(raf);
        raf = undefined;
      }
    }
    
    function moveSpaceShip() {
      // declare our animation function
      var move = function() {
        // update the positions without going out of the screen
        // Sorry, this is dirty...
        if(direction.x){
        	if(
    	    	(shipPositionX > 0 && shipPositionX < canvas.width-20) ||
    			(shipPositionX <= 0 && direction.x > 0) ||
    			(shipPositionX >= canvas.width-20 && direction.x < 0))
    				shipPositionX += direction.x;
    		}
    	if(direction.y){
        	if(
    	    	(shipPositionY > 0 && shipPositionY < canvas.height-20) ||
    			(shipPositionY <= 0 && direction.y > 0) ||
    			(shipPositionY >= canvas.width-20 && direction.y < 0))
    				shipPositionY += direction.y;
    		}
    
        // finally draw ou ship
        draw();
        // update our raf id
        raf = requestAnimationFrame(move);
      };
      // let's go !
      raf = requestAnimationFrame(move);
    }
    
    
    draw();
    
    window.addEventListener('keydown', triggerMoveSpaceShip, true);
    window.addEventListener('keyup', releaseMoveSpaceShip, true);
    canvas {
      margin: auto;
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      border: 1px solid black;
    }
    body{
       overflow: none;
      }
    &lt;canvas id="canvas" width="400" height="200"&gt;&lt;/canvas&gt;

    uncommented fiddle

    【讨论】:

    • 很好,还可以让船对角移动!谢谢
    • 只有一件事,你为什么使用 cancelAnimationFrame(raf) ?使用它而不是仅仅依靠清除画布和重绘有什么好处?
    • 它们的目的不同:cancelAnimationFrame 将停止动画 == 不会再调用 move 函数。如果你不这样做,你会毫无理由地继续画 30fps,因为没有什么新东西可以画了。 clearCanvas 会很好...清除画布上的图画。
    猜你喜欢
    • 2014-10-30
    • 2010-11-07
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 2018-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多