7.5_以扭曲后的帧速率播放动画
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>以扭曲后的帧速率播放动画</title>
<style>
body{
background: #cdcdcd;
}
.controls{
position: absolute;
left: 150px;
top: 10px;
font: 12px arial;
}
#canvas{
position: absolute;
left: 0;
top: 20px;
margin: 20px;
border: thin inset rgba(100,150,230,0.8);
background: #efefef;
}
#animateButton{
margin-left: 15px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<input id="animateButton" type="button" value="Animate" />
<canvas id="canvas" width="420" height="100" ></canvas>
<div id="motionControls" class="controls">
<div id="motionRadios">
<input type="radio" name="motion" id="linearRadio" checked="" />匀速
<input type="radio" name="motion" id="easeInRadio"/>缓入
<input type="radio" name="motion" id="easeOutRadio"/>缓出
<input type="radio" name="motion" id="easeInOutRadio"/>缓入缓出
</div>
</div>
</body>
<script>
var Sprite = function(name,painter,behaviors){
if(name !== undefined){ this.name = name; }
if(painter !== undefined){ this.painter = painter; }
this.top = 0;
this.left = 0;
this.width = 10;
this.height = 10;
this.velocityX = 0;
this.velocityY = 0;
this.visible = true;
this.animating = false;
this.behaviors = behaviors || [];
}
Sprite.prototype = {
paint:function(context){
if(this.painter !== undefined && this.visible){
this.painter.paint(this,context);
}
},
update:function(context,time){
for(var i=0;i<this.behaviors.length;i++){
this.behaviors[i].execute(this,context,time);
}
}
}
</script>
<script>
StopWatch = function(){};
StopWatch.prototype = {
startTime:0,
running:false,
elapsed:undefined,
start:function(){
this.startTime = +new Date();
this.running = true;
this.elapsed = undefined;
},
stop:function(){
this.elapsed = (+new Date()) - this.startTime;
this.running = false;
},
isRunning:function(){
return this.running;
},
getElapsedTime:function(){
if(this.isRunning()){
return (+new Date()) - this.startTime;
}else{
return this.elapsed;
}
},
reset:function(){
this.elapsed = 0;
}
};
AnimationTimer = function(duration,timeWarp){
if(duration !== undefined){
this.duration = duration;
};
if(timeWarp !== undefined){
this.timeWarp = timeWarp;
}
this.stopWatch = new StopWatch();
}
AnimationTimer.prototype = {
default_elastic_passes:3,
start:function(){
this.stopWatch.start();
},
stop:function(){
this.stopWatch.stop();
},
getElapsedTime:function(){
var elapsedTime = this.stopWatch.getElapsedTime();
var percentComplete = elapsedTime/this.duration;
if(!this.stopWatch.running){
return undefined;
};
if(this.timeWarp == undefined){
return elapsedTime;
};
return elapsedTime*(this.timeWarp(percentComplete)/percentComplete);
},
isRunning:function(){
return this.stopWatch.isRunning();
},
isOver:function(){
return this.stopWatch.getElapsedTime() > this.duration;
},
makeEaseIn:function(strength){
return function (percentComplete){
console.log('进入缓入动画了');
return Math.pow(percentComplete,strength*2)
}
},
makeEaseOut:function(strength){
return function(percentComplete){
console.log('进入缓出动画了');
return 1-Math.pow(1-percentComplete,strength*2);
};
},
makeEaseInOut:function(){
return function(percentComplete){
console.log('进入缓入缓出动画了');
return percentComplete - Math.sin(percentComplete*2*Math.PI)/(2*Math.PI);
};
},
makeElastic:function(passes){
var passes = passes || this.default_elastic_passes;
return function(percentComplete){
console.log('进入弹簧运动了');
return ((1-Math.cos(percentComplete*Math.PI*passes))*(1-percentComplete))+ percentComplete;
};
},
makeBounce:function(bounces){
var fn = this.makeElastic(bounces);
return function(percentComplete){
console.log('进入弹跳运动了');
percentComplete = fn(percentComplete);
return percentComplete <=1 ?percentComplete:2-percentComplete;
}
},
makeLinear:function(){
return function(percentComplete){
console.log('进入匀速运动了');
return percentComplete;
}
}
};
</script>
<script>
var SpriteSheetPainter = function(cells){
this.cells = cells || [];
this.cellIndex = 0;
}
SpriteSheetPainter.prototype = {
advance:function(){
if(this.cellIndex == (this.cells.length-1)){
this.cellIndex = 0;
}else{
this.cellIndex++;
}
},
paint:function(sprite,context){
var cell = this.cells[this.cellIndex];
context.drawImage(spritesheet,cell.x,cell.y,cell.w,cell.h,sprite.left,sprite.top,cell.w,cell.h);
}
}
</script>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var linearRadio = document.getElementById('linearRadio');
var easeInRadio = document.getElementById('easeInRadio');
var easeOutRadio = document.getElementById('easeOutRadio');
var easeInOutRadio = document.getElementById('easeInOutRadio');
var animateButton = document.getElementById('animateButton');
var spritesheet = new Image();
var runnerCells =[
{x:0,y:0,w:47,h:64},
{x:55,y:0,w:44,h:64},
{x:107,y:0,w:39,h:64},
{x:150,y:0,w:46,h:64},
{x:208,y:0,w:49,h:64},
{x:265,y:0,w:46,h:64},
{x:320,y:0,w:42,h:64},
{x:380,y:0,w:35,h:64},
{x:425,y:0,w:35,h:64}
];
var interval;
var lastAdvance = 0;
var sprite_left = canvas.width - runnerCells[0].w;
var sprite_top = 10;
var pageFlip_interval = 100;
var animation_duration = 3900;
var animationTimer = new AnimationTimer(animation_duration);
animationTimer.timeWarp = animationTimer.makeLinear(1);
var left = 1.5;
var right = canvas.width - runnerCells[0].w;
var tick_height = 8.5;
var baseline = canvas.height - 9.5;
var width = right - left;
var runInPlace = {
execute:function(sprite,context,time){
var elapsed = animationTimer.getElapsedTime();
if(lastAdvance ===0){
lastAdvance = elapsed;
}else if(lastAdvance!=0 && (elapsed - lastAdvance) >pageFlip_interval){
sprite.painter.advance();
lastAdvance = elapsed;
}
}
};
var moveRightToLeft = {
lastMove : 0,
reset:function(){
this.lastMove = 0;
},
execute:function(sprite,context,time){
var elapsed = animationTimer.getElapsedTime();
if(this.lastMove == 0){
console.log(elapsed+'lllll')
this.lastMove = elapsed;
}else{
sprite.left -= (elapsed - this.lastMove)/1000*sprite.velocityX;
this.lastMove = elapsed;
}
}
};
var sprite = new Sprite('runner',new SpriteSheetPainter(runnerCells),[moveRightToLeft,runInPlace])
spritesheet.src = 'img/running-sprite-sheet.png';
sprite.left = sprite_left;
sprite.top = sprite_top;
sprite.velocityX = 100;
drawAxis();
spritesheet.onload = function(){
sprite.paint(context);
}
animateButton.onclick = function(){
startAnimation();
};
linearRadio.onclick = function(){
animationTimer.timeWarp = animationTimer.makeLinear(1);
}
easeInRadio.onclick = function(){
animationTimer.timeWarp = animationTimer.makeEaseIn(1);
}
easeOutRadio.onclick = function(){
animationTimer.timeWarp = animationTimer.makeEaseOut(1);
}
easeInOutRadio.onclick = function(){
animationTimer.timeWarp = animationTimer.makeEaseInOut();
}
function startAnimation(){
animationTimer.start();
animateButton.style.display = 'none';
window.requestAnimationFrame(animate);
}
function endAnimation(){
animateButton.value = 'Animate';
animateButton.style.display = 'inline';
animationTimer.stop();
lastAdvance = 0;
sprite.painter.cellIndex =0;
sprite.left = sprite_left;
moveRightToLeft.reset();
}
function animate(time){
if(animationTimer.isRunning()){
context.clearRect(0,0,canvas.width,canvas.height);
sprite.update(context,time);
sprite.paint(context);
drawTimeLine()
drawAxis();
if(animationTimer.isOver()){
console.log('动画时间到了,已停止');
endAnimation();
}
window.requestAnimationFrame(animate);
}
}
function drawAxis(){
context.lineWidth = 0.5;
context.strokeStyle = 'cornflowerblue';
context.moveTo(left,baseline);
context.lineTo(right,baseline);
context.stroke();
for(var i =0 ;i<width;i+=width/20){
context.beginPath();
context.moveTo(left+i,baseline - tick_height/2);
context.lineTo(left+i,baseline+tick_height/2);
context.stroke();
}
for(var i =0 ;i<width;i+=width/4){
context.beginPath();
context.moveTo(left+i,baseline - tick_height);
context.lineTo(left+i,baseline+tick_height);
context.stroke();
}
context.beginPath();
context.moveTo(right,baseline - tick_height);
context.lineTo(right,baseline+tick_height);
context.stroke();
}
function drawTimeLine(){
var realElapsed = animationTimer.stopWatch.getElapsedTime()
var realPercent = realElapsed/animation_duration;
context.lineWidth = 0.5;
context.strokeStyle = 'rgba(0,0,255,0.5)';
context.beginPath();
context.moveTo(width - realPercent*width,0);
context.lineTo(width - realPercent*width,canvas.height);
context.stroke();
}
</script>
</html>