0
点赞
收藏
分享

微信扫一扫

Canvas 入门6 requestAnimationFrame实现动画

本文学习资源来自《HTML5 Canvas核心技术 图形、动画与游戏开发》

尽量不要使用setInterval和setTimeout来实现动画,而应该使用requestAnimationFrame()方法来让浏览器自行决定帧速率。

由于各个浏览器对requestAnimationFrame()的实现有所区别,首先封装一个兼容函数requestNextAnimationFrame

window.requestNextAnimationFrame =
(function () {
var originalWebkitRequestAnimationFrame = undefined,
wrapper = undefined,
callback = undefined,
geckoVersion = 0,
userAgent = navigator.userAgent,
index = 0,
self = this;

// Workaround for Chrome 10 bug where Chrome
// does not pass the time to the animation function

if (window.webkitRequestAnimationFrame) {
// Define the wrapper

wrapper = function (time) {
if (time === undefined) {
time = +new Date();
}
self.callback(time);
};

// Make the switch

originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame;

window.webkitRequestAnimationFrame = function (callback, element) {
self.callback = callback;

// Browser calls the wrapper and wrapper calls the callback

originalWebkitRequestAnimationFrame(wrapper, element);
}
}

// Workaround for Gecko 2.0, which has a bug in
// mozRequestAnimationFrame() that restricts animations
// to 30-40 fps.

if (window.mozRequestAnimationFrame) {
// Check the Gecko version. Gecko is used by browsers
// other than Firefox. Gecko 2.0 corresponds to
// Firefox 4.0.

index = userAgent.indexOf('rv:');

if (userAgent.indexOf('Gecko') != -1) {
geckoVersion = userAgent.substr(index + 3, 3);

if (geckoVersion === '2.0') {
// Forces the return statement to fall through
// to the setTimeout() function.

window.mozRequestAnimationFrame = undefined;
}
}
}

return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||

function (callback, element) {
var start,
finish;

window.setTimeout( function () {
start = +new Date();
callback(start);
finish = +new Date();

self.timeout = 1000 / 60 - (finish - start);

}, self.timeout);
};
}
)
();

示例: 使用requestAnimationFrame()方法来绘制动画

html

<head>
<title>Using requestAnimationFrame()</title>

<style>
body {
background: #dddddd;
}

#canvas {
background: #ffffff;
cursor: pointer;
margin-left: 10px;
margin-top: 10px;
-webkit-box-shadow: 3px 3px 6px rgba(0,0,0,0.5);
-moz-box-shadow: 3px 3px 6px rgba(0,0,0,0.5);
box-shadow: 3px 3px 6px rgba(0,0,0,0.5);
}

#controls {
margin-top: 10px;
margin-left: 15px;
}
</style>
</head>

<body>
<div id='controls'>
<input id='animateButton' type='button' value='Animate'/>
</div>

<canvas id='canvas' width='750' height='500'>
Canvas not supported
</canvas>

<script src='../../shared/js/requestNextAnimationFrame.js'></script>
<script src='example.js'></script>
</body>

js

var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
paused = true,
//定义三个球
discs = [
{
x: 150,
y: 250,
lastX: 150,
lastY: 250,
velocityX: -3.2,
velocityY: 3.5,
radius: 25,
innerColor: 'rgba(255,255,0,1)',
middleColor: 'rgba(255,255,0,0.7)',
outerColor: 'rgba(255,255,0,0.5)',
strokeStyle: 'gray',
},

{
x: 50,
y: 150,
lastX: 50,
lastY: 150,
velocityX: 2.2,
velocityY: 2.5,
radius: 25,
innerColor: 'rgba(100,145,230,1.0)',
middleColor: 'rgba(100,145,230,0.7)',
outerColor: 'rgba(100,145,230,0.5)',
strokeStyle: 'blue'
},

{
x: 150,
y: 75,
lastX: 150,
lastY: 75,
velocityX: 1.2,
velocityY: 1.5,
radius: 25,
innerColor: 'rgba(255,0,0,1.0)',
middleColor: 'rgba(255,0,0,0.7)',
outerColor: 'rgba(255,0,0,0.5)',
strokeStyle: 'orange'
},
],
numDiscs = discs.length,
animateButton = document.getElementById('animateButton');

// Functions.....................................................

function drawBackground() {
var STEP_Y = 12,
i = context.canvas.height;

context.strokeStyle = 'lightgray';
context.lineWidth = 0.5;

while(i > STEP_Y*4) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
i -= STEP_Y;
}

context.save();

context.strokeStyle = 'rgba(100,0,0,0.3)';
context.lineWidth = 1;

context.beginPath();

context.moveTo(35,0);
context.lineTo(35,context.canvas.height);
context.stroke();

context.restore();
}

function update() {
var disc = null;

for(var i=0; i < numDiscs; ++i) {
disc = discs[i];
//边缘检测
if (disc.x + disc.velocityX + disc.radius > context.canvas.width ||
disc.x + disc.velocityX - disc.radius < 0)
disc.velocityX = -disc.velocityX;

if (disc.y + disc.velocityY + disc.radius > context.canvas.height ||
disc.y + disc.velocityY - disc.radius < 0)
disc.velocityY= -disc.velocityY;

disc.x += disc.velocityX;
disc.y += disc.velocityY;
}
}

function draw() {
var disc = discs[i];

for(var i=0; i < numDiscs; ++i) {
disc = discs[i];

gradient = context.createRadialGradient(disc.x, disc.y, 0,
disc.x, disc.y, disc.radius);

gradient.addColorStop(0.3, disc.innerColor);
gradient.addColorStop(0.5, disc.middleColor);
gradient.addColorStop(1.0, disc.outerColor);

context.save();
context.beginPath();
context.arc(disc.x, disc.y, disc.radius, 0, Math.PI*2, false);
context.fillStyle = gradient;
context.strokeStyle = disc.strokeStyle;
context.fill();
context.stroke();
context.restore();
}
}

// 动画.....................................................
function animate(time) {
if (!paused) {
context.clearRect(0,0,canvas.width,canvas.height);
drawBackground();
update();
draw();

window.requestNextAnimationFrame(animate);
}
}

// 初始化................................................

context.font = '48px Helvetica';

animateButton.onclick = function (e) {
paused = paused ? false : true;
if (paused) {
animateButton.value = 'Animate';
}
else {
window.requestNextAnimationFrame(animate);
animateButton.value = 'Pause';
}
};

Canvas 入门6 requestAnimationFrame实现动画_函数

帧速率计算

动画图形的显示频率叫做“帧速率”frame rate。
通常来说,有必要计算一下帧速率。计算方式:

var lastTime = 0;
function calculateFps(){
var now=(+new Date),fps = 1000/(now-lastTime);
lastTime = now;
return fps;
}
function animate(time){
eraseBackground();
drawBackground();
update();
draw();
context.fillStyle='cornflowerblue';
context.fillText(calculateFps().toFixed()+' fps',20,60);
window.requestNextAnimationFrame(animate);
}
window.requestNextAnimationFrame(animate);

以不同的帧速率来执行各种任务

一些不重要的任务,可以以低速率来执行。下面示例中循环根据上一次更新fps数值的时间来判断当前是否已经过了一秒钟,如果是的话,就再次更新fps数值:

var lastFpsUpdateTime =0, lastFpsUpdate =0;
function animate(time){
var fps =0;
if(time==undefined){
time = +new Date;
}
if(!paused){
eraseBackground();
drawBackground();
update(time);
draw();
fps=calculateFps();
// 每秒更新frame rate值
if(now - lastFpsUpdateTime>1000){
lastFpsUpdateTime = now;
lastFpsUpdate = fps;
}
context.fillStyle = 'cornflowerblue';
context.fillText(lastFpsUpdate.toFixed() + ' fps',50,48);
}
}


举报

相关推荐

0 条评论