2013-10-31 18 views
1

我想使用KineticJS將許多對象動畫到畫布上。我在每一幀都使用內置的移動方法。衆所周知,重繪圖層是一項昂貴的操作,可能會導致性能問題,所以我只在每個移動操作已經執行之後才調用layer.draw()。儘管如此,我生成的對象越多,表現越差,最終的結果就是動畫遲緩。KineticJS能夠使用移動方法執行許多對象的平滑動畫嗎?

爲了比較KineticJS性能與本地畫布,我準備了兩個相同的演示 - 在500x500的畫布中彈跳球。第一個是使用本地畫布。它只是清除每個框架上的畫布並畫出球。第二個使用KineticJS,創建圖像對象後,它使用移動方法移動它們。

很明顯,雖然本機演示與10,100和1000個球執行相同,但KineticJS演示的性能受球的數量影響很大。 1000,它只是無法使用。對這兩個示例都有很多優化,包括對動畫循環使用requestAnimationFrame或對KineticJS使用內置的Animation對象,但這些不會改變演示的性能。

所以這裏是兩個演示。首先,原生一個 - http://jsfiddle.net/uxsLN/1/

(function() { 

    window.addEventListener('load', loaded, false); 

    function loaded() { 
     img = new Image(); 
     img.onload = canvasApp; 
     img.src = 'ball.png'; 
    } 

    function canvasApp() { 

     var theCanvas = document.getElementById("canvas"); 
     var context = theCanvas.getContext("2d"); 

     function drawScreen() { 

      context.clearRect(0, 0, theCanvas.width, theCanvas.height); 

      context.strokeStyle = '#000000'; 
      context.strokeRect(1, 1, theCanvas.width - 2, theCanvas.height - 2); 

      context.fillStyle = "#000000"; 

      var ball; 
      for (var i = 0; i < balls.length; i++) { 
       ball = balls[i]; 
       ball.x += ball.xunits; 
       ball.y += ball.yunits; 
       context.drawImage(img, ball.x, ball.y); 
       if (ball.x + ball.radius * 2 > theCanvas.width || ball.x < 0) { 
        ball.angle = 180 - ball.angle; 
        updateBall(ball); 
       } else if (ball.y + ball.radius * 2 > theCanvas.height || ball.y < 0) { 
        ball.angle = 360 - ball.angle; 
        updateBall(ball); 
       } 
      } 
     } 

     function updateBall(ball) { 
      ball.radians = ball.angle * Math.PI/180; 
      ball.xunits = Math.cos(ball.radians) * ball.speed; 
      ball.yunits = Math.sin(ball.radians) * ball.speed; 
     } 

     var numBalls = 1000; 
     var maxSize = 8; 
     var minSize = 5; 
     var maxSpeed = maxSize + 5; 
     var balls = []; 
     var radius = 24; 

     for (var i = 0; i < numBalls; i++) { 

      var speed = maxSpeed - radius; 
      var angle = Math.floor(Math.random() * 360); 
      var radians = angle * Math.PI/180; 

      var ball = { 
       x : (theCanvas.width - radius)/2, 
       y : (theCanvas.height - radius)/2, 
       radius : radius, 
       speed : speed, 
       angle : angle, 
       xunits : Math.cos(radians) * speed, 
       yunits : Math.sin(radians) * speed 
      } 

      balls.push(ball); 
     } 

     function gameLoop() { 
      window.setTimeout(gameLoop, 20); 
      drawScreen() 
     } 
     gameLoop(); 
    } 

})(); 

接下來,KineticJS - http://jsfiddle.net/MNpUX/

(function() { 

    window.addEventListener('load', loaded, false); 

    function loaded() { 
     img = new Image(); 
     img.onload = canvasApp; 
     img.src = 'ball.png'; 
    } 

    function canvasApp() { 

     var stage = new Kinetic.Stage({ 
      container : 'container', 
      width : 500, 
      height : 500 
     }); 

     var layer = new Kinetic.Layer(); 

     stage.add(layer); 

     rect = new Kinetic.Rect({ 
      x : 0, 
      y : 0, 
      width : stage.getWidth(), 
      height : stage.getHeight(), 
      fill : '#EEEEEE', 
      stroke : 'black' 
     }); 

     layer.add(rect); 

     function drawScreen() { 

      var ball; 
      for (var i = 0; i < balls.length; i++) { 
       ball = balls[i]; 
       ball.obj.move(ball.xunits, ball.yunits); 
       if (ball.obj.getX() + ball.radius * 2 > stage.getWidth() || ball.obj.getX() < 0) { 
        ball.angle = 180 - ball.angle; 
        updateBall(ball); 
       } else if (ball.obj.getY() + ball.radius * 2 > stage.getHeight() || ball.obj.getY() < 0) { 
        ball.angle = 360 - ball.angle; 
        updateBall(ball); 
       } 
      } 
      layer.draw(); 
     } 

     function updateBall(ball) { 
      ball.radians = ball.angle * Math.PI/180; 
      ball.xunits = Math.cos(ball.radians) * ball.speed; 
      ball.yunits = Math.sin(ball.radians) * ball.speed; 
     } 

     var numBalls = 1000; 
     var maxSize = 8; 
     var minSize = 5; 
     var maxSpeed = maxSize + 5; 
     var balls = []; 
     var radius = 24; 
     for (var i = 0; i < numBalls; i++) { 
      var speed = maxSpeed - radius; 
      var angle = Math.floor(Math.random() * 360); 
      var radians = angle * Math.PI/180; 
      var obj = new Kinetic.Image({ 
       image : img, 
       x : (stage.getWidth() - radius)/2, 
       y : (stage.getHeight() - radius)/2 
      }); 
      layer.add(obj); 
      var ball = { 
       radius : radius, 
       speed : speed, 
       angle : angle, 
       xunits : Math.cos(radians) * speed, 
       yunits : Math.sin(radians) * speed, 
       obj : obj 
      }; 
      balls.push(ball); 
     } 

     function gameLoop() { 
      window.setTimeout(gameLoop, 20); 
      drawScreen() 
     } 
     gameLoop(); 
    } 

})(); 

所以,問題是 - 我懷念一些事情KineticJS或者它只是沒有內置這樣的目的是什麼?

回答

2

您可以通過獲得一些速度:

  • 談到聆聽過在舞臺上。
  • 使用layer.drawScene代替layer.draw。 (drawScene不會重新繪製命中的場景)。
  • 將球數減少到500(效果看起來幾乎相同)。

如果您的設計允許,請使用自定義Kinetic.Shape以「更接近金屬」。

Kinetic.Shape爲您提供了一個可以運行本機環境命令的包裝環境。

使用形狀,你會得到更好的結果,因爲只有1個對象被管理。

這裏的代碼和一個小提琴:http://jsfiddle.net/m1erickson/AVJyr/

<!DOCTYPE html> 
<html> 
    <head> 
    <meta charset="utf-8"> 
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script> 
    <script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.7.2.min.js"></script> 
<style> 
body{padding:20px;} 
#container{ 
    border:solid 1px #ccc; 
    margin-top: 10px; 
    width:500px; 
    height:500px; 
} 
</style>   
<script> 
$(function(){ 

     var stage = new Kinetic.Stage({ 
      container: 'container', 
      width: 500, 
      height: 500, 
      listening:false 
     }); 
     var layer = new Kinetic.Layer(); 
     stage.add(layer); 

     // 
     var cw=stage.getWidth(); 
     var ch=stage.getHeight(); 
     var numBalls = 1000; 
     var maxSize = 8; 
     var minSize = 5; 
     var maxSpeed = maxSize + 5; 
     var balls = []; 
     var radius = 24; 
     // this is a custom Kinetic.Shape 
     var shape; 


     for (var i = 0; i < numBalls; i++) { 
      var speed = maxSpeed - radius; 
      var angle = Math.floor(Math.random() * 360); 
      var radians = angle * Math.PI/180; 
      var ball = { 
      x : (cw-radius)/2, 
      y : (ch-radius)/2, 
      radius : radius, 
      speed : speed, 
      angle : angle, 
      xunits : Math.cos(radians) * speed, 
      yunits : Math.sin(radians) * speed 
      } 
      balls.push(ball); 
     } 

     // load the ball image and create the Kinetic.Shape 
     img = new Image(); 
     img.onload=function(){ 

      shape=new Kinetic.Shape({ 
       x: 0, 
       y: 0, 
       width:500, 
       height:500, 
       draggable: true, 
       drawFunc: function(context) { 
        context.beginPath(); 
        var ball; 
        for (var i = 0; i < balls.length; i++) { 
        ball = balls[i]; 
        ball.x += ball.xunits; 
        ball.y += ball.yunits; 
        context.drawImage(img, ball.x, ball.y); 
        if (ball.x+ball.radius*2>cw || ball.x<0) { 
         ball.angle = 180 - ball.angle; 
        } else if (ball.y+ball.radius*2>ch || ball.y<0) { 
         ball.angle = 360 - ball.angle; 
        } 
        ball.radians = ball.angle * Math.PI/180; 
        ball.xunits = Math.cos(ball.radians) * ball.speed; 
        ball.yunits = Math.sin(ball.radians) * ball.speed; 
        } 
        context.fillStrokeShape(this); 
       }, 
      }); 
      layer.add(shape); 

      // GO! 
      gameLoop(); 
     } 
     img.src = 'http://users-cs.au.dk/mic/dIntProg/e12/uge/4/Projekter/bouncingballs/assignment/ball.png'; 

     // RAF used to repeatedly redraw the custom shape 
     function gameLoop(){ 
      window.requestAnimationFrame(gameLoop); 
      layer.clear(); 
      shape.draw(); 
     } 


}); // end $(function(){}); 

</script>  
</head> 

<body> 
    <div id="container"></div> 
</body> 
</html> 
+0

所以你建議,以避免移動方法,並使用天然情況下這樣的目的呢?這可以真正解決性能問題,但同時你會失去大部分的KineticJS功能和抽象。考慮到彈跳球的例子,你不再能夠與每個球互動,就好像它們是「真實」的對象一樣。 –

+0

您的設計要求決定了您應用的解決方案。請記住,由於每個球是一個獨立的對象,您仍然可以與它們交互(命中測試,調整屬性等)。最後,您需要權衡一下:本地畫布速度與Kinetic輕鬆對象管理。祝你的項目好運! – markE

+1

+1。我還想指出,KineticJS將支持一種新的高性能模式,不包括節點嵌套,轉換,事件處理,事件觸發等。對於使用簡單用戶的人來說,這將是理想的,並且將會完成接近原生渲染速度。這將作爲v5.1.0發佈 –