我试图了解如何使javascript动画顺利运行,我一直在这里阅读一些答案,我发现了一些我不理解的东西。
以下是问题Smooth javascript animation
的链接在大多数选票的答案中,它说“这就是为什么通常将位置/帧基于自动画开始以来经过的时间量(使用新的Date()。getTime())的好主意。而不是每帧移动/更改固定数量。“
有人能告诉我一个非常简单的例子,它使用了这个答案的方法,然后解释你如何控制动画的速度?
答案 0 :(得分:3)
通常使用动画,您有三个组件
这些在循环中运行,称为动画循环。典型的动画循环可能如下所示(我将在下面详细解释所有函数):
function animate() {
update(); // Executes all game logic and updates your world
draw(); // Draws all of the animated elements onto your drawing context
timer(); // Controls the timing of when animate will be called again
};
animate(); // Start animating
动画循环是动画内容的主要流控制器。基本上,一遍又一遍地调用动画循环中的代码。动画功能的每次执行构成一个帧。在一个框架中,您的世界将在屏幕上更新并重新绘制。动画函数运行的频率称为帧速率,由计时器控制。
您还需要一个对绘图上下文的引用,该上下文将用于保存您希望设置动画的元素,也称为精灵:
// Create a variable with a reference to our drawing context
// Note this is not the same as using a canvas element
var canvas = document.getElementById("canvas");
更新负责每帧更新一次您希望设置动画的项目的状态。举一个简单的例子,假设你有一个包含三辆汽车的阵列,每辆汽车都有一个x和y位置以及一个速度。在每个框架上,您需要更新汽车的位置以反映其应根据速度行进的距离。
我们的汽车阵列,用于生成汽车的代码可能如下所示:
// A method to create new cars
var Car = new Car(x, y, vx, vy) {
this.className = "car"; // The CSS class name we want to give the car
this.x = x || 0; // The x position of the car
this.y = y || 0; // The y position of the car
this.vx = vx || 0; // the x component of the car's velocity
this.vy = vy || 0 // the y component of the car's velocity
};
// A function that can be called to update the position of the car on each frame
Car.prototype.drive = function () {
this.x += this.vx;
this.y += this.vy;
// Return an html string that represents our car sprite, with correct x and y
// positions
return "<div class='"
+ this.className
+ "' style='left:"
+ this.x
+ "px; top:"
+ this.y
+ "px;'></div>";
};
// Create a variable to hold our cars
var cars = [
new Car(10, 10, 5, 3),
new Car(50, 22, 1, 0),
new Car(9, 33, 20, 10)
];
当我们调用更新时,我们将要调用每辆车的驱动方法,以便在屏幕上移动汽车精灵。此驱动方法将返回表示精灵的html字符串,包括它的当前位置。我们希望将此字符串附加到一个变量,该变量可用于设置canvas div的内部HTML:
// An empty string that will be used to contain the innerHTML for our canvas
var htmlStr = "";
// Update the position of each car
function update() {
// Clear the canvas content
htmlStr = "";
for (var i = 0, len = cars.length; i < len; i++) {
// Add the car sprite to the html string to be rendered
htmlStr += cars[i].drive();
}
};
当更新函数输出我们的精灵时,我们需要在画布上实际绘制元素。为了做到这一点,我们使用canvas变量的innerHTML方法,将其传递给htmlStr,其中包含用于表示所有sprite的标记。这将要求浏览器解析文本并在屏幕上吐出DOM元素:
function draw() {
// Parse the text containing our sprites and render them on the DOM tree
canvas.innerHTML = htmlStr;
};
你可以说有更好的方法可以做到这一点,而且可能有。但是,为了保持简单,我保持简单。这也不是最高效的方法,因为DOM API非常慢。如果您查看此应用程序的系统资源使用情况,其中大部分将被innerHTML调用占用。如果您想要更快的绘图上下文,则应使用HTML 5画布。
最后,你需要一些反复调用动画的方法。你可以通过几种方式做到这一点。首先,有一个很好的旧setTimeout:
setTimeout(animate, 0);
这指示浏览器在延迟0ms后调用动画。在实践中,动画将在延迟0ms后永远不会执行。大多数浏览器中setTimeout的最小分辨率大约为15ms,但由于UI线程在javascript中的工作方式,即使这样也无法保证。
更好的解决方案是使用requestAnimationFrame,它基本上告诉浏览器你正在做动画。浏览器将为您做一堆很好的优化。但是,浏览器并不完全支持此功能。您应该将此解决方案用于跨浏览器的requestAnimationFrame polyfill:
http://paulirish.com/2011/requestanimationframe-for-smart-animating/
然后你可以使用:
requestAnimFrame(animate);
最后,您完成的程序将如下所示:
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
// Create a variable with a reference to our drawing context
// Note this is not the same as using a canvas element
var canvas = document.getElementById("canvas");
// A method to create new cars
var Car = new Car(x, y, vx, vy) {
this.className = "car"; // The CSS class name we want to give the car
this.x = x || 0; // The x position of the car
this.y = y || 0; // The y position of the car
this.vx = vx || 0; // the x component of the car's velocity
this.vy = vy || 0 // the y component of the car's velocity
};
// A function that can be called to update the position of the car on each frame
Car.prototype.drive = function () {
this.x += this.vx;
this.y += this.vy;
// Return an html string that represents our car sprite, with correct x and y positions
return "<div class='"
+ this.className
+ "' style='left:"
+ this.x
+ "px; top:"
+ this.y
+ "px;'></div>";
};
// Create a variable to hold our cars
var cars = [
new Car(10, 10, 5, 3),
new Car(50, 22, 1, 0),
new Car(9, 33, 20, 10)
];
// An empty string that will be used to contain the innerHTML for our canvas
var htmlStr = "";
// Update the position of each car
function update() {
// Clear the canvas content
htmlStr = "";
for (var i = 0, len = cars.length; i < len; i++) {
// Add the car sprite to the html string to be rendered
htmlStr += cars[i].drive();
}
};
function draw() {
// Parse the text containing our sprites and render them on the DOM tree
canvas.innerHTML = htmlStr;
};
function animate() {
update(); // Executes all game logic and updates your world
draw(); // Draws all of the animated elements onto your drawing context
requestAnimFrame(animate); // Controls the timing of when animate will be called again
};
animate(); // Start animating
我有很多优化,调整,抽象和其他很多优点,我没有进入这里。有很多方法可以实现更新和绘制,并且都有优点和缺点。
然而,您将要编写的每个动画都将使用某种动画循环,它们都具有这种基本架构。希望这能说明javascript中动画的基础知识。
答案 1 :(得分:0)
这篇文章描述了制作动画的最佳方式:http://paulirish.com/2011/requestanimationframe-for-smart-animating/
那将以60fps或尽可能快的速度运行。
一旦你知道了,那么你可以决定你想要动画拍摄的时间以及物体移动的距离,然后计算出每帧移动的距离。
当然,对于流畅的动画,你应该尽可能使用CSS Transitions - 看看http://css3.bradshawenterprises.com。
答案 2 :(得分:0)
快速回答是setTimeout
并不能保证在您调用回调后 n 毫秒执行回调。它只是保证它会在 n 毫秒之后执行。 John Resig在this piece中介绍了其中的一部分内容。
出于这个原因,您需要检查动画回调实际执行的时间,而不是您使用setTimeout
计划它的时间。
答案 3 :(得分:0)
我写了一篇博客文章,演示了使用画布和精灵表的“基于时间”动画的概念。
该示例为精灵设置动画并考虑经过的时间,以确保它在不同的帧速率下保持一致。通过稍微更改代码,您可以轻松地将其应用于在页面周围移动元素。与时间和动画本身相关的概念基本保持不变。
http://codeutopia.net/blog/2009/08/21/using-canvas-to-do-bitmap-sprite-animation-in-javascript/
(我总是觉得垃圾邮件留下的答案基本上只是链接到我的博客,即使链接实际上完全相关:D)
答案 4 :(得分:0)
以这种方式看待它:你有一些想要移动的物体。假设你给它5秒钟从A
移动到B
,你希望它以30fps的速度完成。这意味着您必须更新对象的位置150次,并且每次更新最多为1/30 = 0.0333秒。
如果您正在使用“自上次调整后的时间”,并且您的代码需要0.05秒才能执行,那么您将不会使用30fps。但是,如果你在动画开始的时候关闭它,那么你的代码需要多长时间并不重要 - 当你的更新代码触发时,它会计算并设置对象的位置应该在动物的那个阶段,无论实际显示了多少帧。
答案 5 :(得分:0)
基本思路如下:
这是一个非常简单的代码示例。我通常不用全局变量编码,但这是展示技术的最简单方法。
var duration = 1000; // in ms
var startTime; // in ms
var startX = 0;
var endX = 500;
// 0 means try to get as many frames as possible
// > 1: Remember that this is not guaranteed to run as often as requested
var refreshInterval = 0;
var div = document.getElementById('anim');
function updatePosition() {
var now = (new Date()).getTime();
var msSinceStart = now - startTime;
var percentageOfProgress = msSinceStart / duration;
var newX = (endX - startX) * percentageOfProgress;
div.style.left = Math.min(newX, endX) + "px";
if (window.console) {
console.log('Animation Frame - percentageOfProgress: ' + percentageOfProgress + ' newX = ' + newX);
}
if (newX < endX) {
scheduleRepaint();
}
}
function scheduleRepaint() {
setTimeout(updatePosition, refreshInterval);
}
div.onclick = function() {
startTime = (new Date()).getTime();
scheduleRepaint();
}