我正在尝试使用KineticJS将许多对象设置为画布动画。我在每一帧都使用内置的移动方法。众所周知,重新绘制图层是一项昂贵的操作,可能会导致性能问题,因此我只在每次执行移动操作后才调用layer.draw()。尽管如此,我制作的对象越多,性能越差,最终结果就是动画效果不佳。
为了比较KineticJS与原生画布的性能,我准备了两个同样做的演示 - 在500x500的画布上弹跳球。第一个是使用原生画布。它只是清除每个框架上的画布并绘制球。第二个使用KineticJS,一旦创建了图像对象,它就会使用move方法移动它们。
很明显,虽然本机演示使用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,或者它不是为了这个目的而建造的?
答案 0 :(得分:2)
你可以通过以下方式获得一点速度:
如果您的设计允许,请使用自定义Kinetic.Shape“更接近金属”。
Kinetic.Shape为您提供了一个包装上下文,您可以在其上运行本机上下文命令。
使用Shape,您将获得更好的结果,因为只有一个对象被管理。
这是代码和小提琴: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>