Canvas使用教程——动画基础(Basic animations)
来源:https://developer.mozilla.org/en/Canvas_tutorial/Basic_animations
Since we're using script to control canvas
elements it's also very easy to make (interactive) animations. Unfortunately the canvas element was never designed to be used in this way (unlike Flash) so there are limitations.
Probably the biggest limitation is that once a shape gets drawn it stays that way. If we need to move it we have to redraw it and everything that was drawn before it. It takes a lot of time to redraw complex frames and the performance depends highly on the speed of the computer it's running on.
Basic animation steps
These are the steps you need to take to draw a frame:
- Clear the canvas
Unless the shapes you'll be drawing fill the complete canvas (for instance a backdrop image), you need to clear any shapes that have been drawn previously. The easiest way to do this is using theclearRect
method. - Save the canvas state
If you're changing any setting (styles, transformations, etc) which affect the canvas state and want to make sure the original state is used each time a frame is drawn, you need to save it. - Draw animated shapes
The step where you do the actual frame rendering. - Restore the canvas state
If you've saved the state, restore it before drawing a new frame.
Controlling an animation
Shapes are drawn to the canvas by using the canvas methods directly or calling custom functions. In normal circumstances we only see these results appear on the canvas when the script finishes execution. For instance it isn't possible to do an animation from within a for
loop.
We need a way to execute our drawing functions over a period of time. There are two ways to control an animation like this. First there's the setInterval
and setTimeout
functions which can be used to call a specific function over a set period of time.
- setInterval(animateShape,500);
- setTimeout(animateShape,500);
setInterval(animateShape,500); setTimeout(animateShape,500);
If you don't want any user interaction it's best to use the setInterval
function which repeatedly executes the supplied code. In the example above the animateShape
function is executed every 500 miliseconds (half a second). The setTimeout
function only executes once after the set amount of time.
The second method we can use to control an animation is user input. If we wanted to make a game we could use keyboard or mouse events to control the animation. By setting eventListeners we catch any user interaction and execute our animation functions.
In the examples below I'm using the first method to control the animation. At the bottom of this page are some links to examples which use the second.
An animation example 1
In this example I'm going to animate a mini simulation of our solar system.
- var sun = new Image();
- var moon = new Image();
- var earth = new Image();
- function init(){
- sun.src = 'images/sun.png';
- moon.src = 'images/moon.png';
- earth.src = 'images/earth.png';
- setInterval(draw,100);
- }
- function draw() {
- var ctx = document.getElementById('canvas').getContext('2d');
- ctx.globalCompositeOperation = 'destination-over';
- ctx.clearRect(0,0,300,300); // clear canvas
- ctx.fillStyle = 'rgba(0,0,0,0.4)';
- ctx.strokeStyle = 'rgba(0,153,255,0.4)';
- ctx.save();
- ctx.translate(150,150);
- // Earth
- var time = new Date();
- ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );
- ctx.translate(105,0);
- ctx.fillRect(0,-12,50,24); // Shadow
- ctx.drawImage(earth,-12,-12);
- // Moon
- ctx.save();
- ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );
- ctx.translate(0,28.5);
- ctx.drawImage(moon,-3.5,-3.5);
- ctx.restore();
- ctx.restore();
- ctx.beginPath();
- ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit
- ctx.stroke();
- ctx.drawImage(sun,0,0,300,300);
- }
var sun = new Image(); var moon = new Image(); var earth = new Image(); function init(){ sun.src = 'images/sun.png'; moon.src = 'images/moon.png'; earth.src = 'images/earth.png'; setInterval(draw,100); } function draw() { var ctx = document.getElementById('canvas').getContext('2d'); ctx.globalCompositeOperation = 'destination-over'; ctx.clearRect(0,0,300,300); // clear canvas ctx.fillStyle = 'rgba(0,0,0,0.4)'; ctx.strokeStyle = 'rgba(0,153,255,0.4)'; ctx.save(); ctx.translate(150,150); // Earth var time = new Date(); ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() ); ctx.translate(105,0); ctx.fillRect(0,-12,50,24); // Shadow ctx.drawImage(earth,-12,-12); // Moon ctx.save(); ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() ); ctx.translate(0,28.5); ctx.drawImage(moon,-3.5,-3.5); ctx.restore(); ctx.restore(); ctx.beginPath(); ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit ctx.stroke(); ctx.drawImage(sun,0,0,300,300); }
Canvas sun.png
source image sun |
Canvas earth.png
source image earth |
Canvas moon.png
source image moon |
An animation example 2
- function init(){
- clock();
- setInterval(clock,1000);
- }
- function clock(){
- var now = new Date();
- var ctx = document.getElementById('canvas').getContext('2d');
- ctx.save();
- ctx.clearRect(0,0,150,150);
- ctx.translate(75,75);
- ctx.scale(0.4,0.4);
- ctx.rotate(-Math.PI/2);
- ctx.strokeStyle = "black";
- ctx.fillStyle = "white";
- ctx.lineWidth = 8;
- ctx.lineCap = "round";
- // Hour marks
- ctx.save();
- for (var i=0;i<12;i++){
- ctx.beginPath();
- ctx.rotate(Math.PI/6);
- ctx.moveTo(100,0);
- ctx.lineTo(120,0);
- ctx.stroke();
- }
- ctx.restore();
- // Minute marks
- ctx.save();
- ctx.lineWidth = 5;
- for (i=0;i<60;i++){
- if (i%5!=0) {
- ctx.beginPath();
- ctx.moveTo(117,0);
- ctx.lineTo(120,0);
- ctx.stroke();
- }
- ctx.rotate(Math.PI/30);
- }
- ctx.restore();
- var sec = now.getSeconds();
- var min = now.getMinutes();
- var hr = now.getHours();
- hr = hr>=12 ? hr-12 : hr;
- ctx.fillStyle = "black";
- // write Hours
- ctx.save();
- ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec )
- ctx.lineWidth = 14;
- ctx.beginPath();
- ctx.moveTo(-20,0);
- ctx.lineTo(80,0);
- ctx.stroke();
- ctx.restore();
- // write Minutes
- ctx.save();
- ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec )
- ctx.lineWidth = 10;
- ctx.beginPath();
- ctx.moveTo(-28,0);
- ctx.lineTo(112,0);
- ctx.stroke();
- ctx.restore();
- // Write seconds
- ctx.save();
- ctx.rotate(sec * Math.PI/30);
- ctx.strokeStyle = "#D40000";
- ctx.fillStyle = "#D40000";
- ctx.lineWidth = 6;
- ctx.beginPath();
- ctx.moveTo(-30,0);
- ctx.lineTo(83,0);
- ctx.stroke();
- ctx.beginPath();
- ctx.arc(0,0,10,0,Math.PI*2,true);
- ctx.fill();
- ctx.beginPath();
- ctx.arc(95,0,10,0,Math.PI*2,true);
- ctx.stroke();
- ctx.fillStyle = "#555";
- ctx.arc(0,0,3,0,Math.PI*2,true);
- ctx.fill();
- ctx.restore();
- ctx.beginPath();
- ctx.lineWidth = 14;
- ctx.strokeStyle = '#325FA2';
- ctx.arc(0,0,142,0,Math.PI*2,true);
- ctx.stroke();
- ctx.restore();
- }
function init(){ clock(); setInterval(clock,1000); } function clock(){ var now = new Date(); var ctx = document.getElementById('canvas').getContext('2d'); ctx.save(); ctx.clearRect(0,0,150,150); ctx.translate(75,75); ctx.scale(0.4,0.4); ctx.rotate(-Math.PI/2); ctx.strokeStyle = "black"; ctx.fillStyle = "white"; ctx.lineWidth = 8; ctx.lineCap = "round"; // Hour marks ctx.save(); for (var i=0;i<12;i++){ ctx.beginPath(); ctx.rotate(Math.PI/6); ctx.moveTo(100,0); ctx.lineTo(120,0); ctx.stroke(); } ctx.restore(); // Minute marks ctx.save(); ctx.lineWidth = 5; for (i=0;i<60;i++){ if (i%5!=0) { ctx.beginPath(); ctx.moveTo(117,0); ctx.lineTo(120,0); ctx.stroke(); } ctx.rotate(Math.PI/30); } ctx.restore(); var sec = now.getSeconds(); var min = now.getMinutes(); var hr = now.getHours(); hr = hr>=12 ? hr-12 : hr; ctx.fillStyle = "black"; // write Hours ctx.save(); ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec ) ctx.lineWidth = 14; ctx.beginPath(); ctx.moveTo(-20,0); ctx.lineTo(80,0); ctx.stroke(); ctx.restore(); // write Minutes ctx.save(); ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec ) ctx.lineWidth = 10; ctx.beginPath(); ctx.moveTo(-28,0); ctx.lineTo(112,0); ctx.stroke(); ctx.restore(); // Write seconds ctx.save(); ctx.rotate(sec * Math.PI/30); ctx.strokeStyle = "#D40000"; ctx.fillStyle = "#D40000"; ctx.lineWidth = 6; ctx.beginPath(); ctx.moveTo(-30,0); ctx.lineTo(83,0); ctx.stroke(); ctx.beginPath(); ctx.arc(0,0,10,0,Math.PI*2,true); ctx.fill(); ctx.beginPath(); ctx.arc(95,0,10,0,Math.PI*2,true); ctx.stroke(); ctx.fillStyle = "#555"; ctx.arc(0,0,3,0,Math.PI*2,true); ctx.fill(); ctx.restore(); ctx.beginPath(); ctx.lineWidth = 14; ctx.strokeStyle = '#325FA2'; ctx.arc(0,0,142,0,Math.PI*2,true); ctx.stroke(); ctx.restore(); }
An animation example 3
This is the code for a left-to-right looping panoramic image scroller. Make sure the image is larger than the canvas.
Using this file for this example
http://commons.wikimedia.org/wiki/File:Capitan_Meadows,_Yosemite_National_Park.jpg
- var img = new Image();
- //User Variables
- img.src = 'Capitan_Meadows,_Yosemite_National_Park.jpg';
- var CanvasXSize = 800;
- var CanvasYSize = 200;
- var speed = 30; //lower is faster
- var scale = 1.05;
- var y = -4.5; //vertical offset
- //End User Variables
- var dx = 0.75;
- var imgW = img.width*scale;
- var imgH = img.height*scale;
- var x = 0;
- if (imgW > CanvasXSize) { x = CanvasXSize-imgW; } // image larger than canvas
- var clearX;
- var clearY;
- if (imgW > CanvasXSize) { clearX = imgW; } // image larger than canvas
- else { clearX = CanvasXSize; }
- if (imgH > CanvasYSize) { clearY = imgH; } // image larger than canvas
- else { clearY = CanvasYSize; }
- var ctx;
- function init() {
- //Get Canvas Element
- ctx = document.getElementById('canvas').getContext('2d');
- //Set Refresh Rate
- return setInterval(draw, speed);
- }
- function draw() {
- //Clear Canvas
- ctx.clearRect(0,0,clearX,clearY);
- //If image is <= Canvas Size
- if (imgW <= CanvasXSize) {
- //reset, start from beginning
- if (x > (CanvasXSize)) { x = 0; }
- //draw aditional image
- if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-CanvasXSize+1,y,imgW,imgH); }
- }
- //If image is > Canvas Size
- else {
- //reset, start from beginning
- if (x > (CanvasXSize)) { x = CanvasXSize-imgW; }
- //draw aditional image
- if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-imgW+1,y,imgW,imgH); }
- }
- //draw image
- ctx.drawImage(img,x,y,imgW,imgH);
- //amount to move
- x += dx;
- }
var img = new Image(); //User Variables img.src = 'Capitan_Meadows,_Yosemite_National_Park.jpg'; var CanvasXSize = 800; var CanvasYSize = 200; var speed = 30; //lower is faster var scale = 1.05; var y = -4.5; //vertical offset //End User Variables var dx = 0.75; var imgW = img.width*scale; var imgH = img.height*scale; var x = 0; if (imgW > CanvasXSize) { x = CanvasXSize-imgW; } // image larger than canvas var clearX; var clearY; if (imgW > CanvasXSize) { clearX = imgW; } // image larger than canvas else { clearX = CanvasXSize; } if (imgH > CanvasYSize) { clearY = imgH; } // image larger than canvas else { clearY = CanvasYSize; } var ctx; function init() { //Get Canvas Element ctx = document.getElementById('canvas').getContext('2d'); //Set Refresh Rate return setInterval(draw, speed); } function draw() { //Clear Canvas ctx.clearRect(0,0,clearX,clearY); //If image is <= Canvas Size if (imgW <= CanvasXSize) { //reset, start from beginning if (x > (CanvasXSize)) { x = 0; } //draw aditional image if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-CanvasXSize+1,y,imgW,imgH); } } //If image is > Canvas Size else { //reset, start from beginning if (x > (CanvasXSize)) { x = CanvasXSize-imgW; } //draw aditional image if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-imgW+1,y,imgW,imgH); } } //draw image ctx.drawImage(img,x,y,imgW,imgH); //amount to move x += dx; }
html code. Canvas width and height should match the CanvasXSize, CanvasYSize.
- <body onload="init();">
- <canvas id="canvas" width="800" height="200"></canvas>
<body onload="init();"> <canvas id="canvas" width="800" height="200"></canvas>
Other examples
- Particle Fountain
Interactive particle emitter with physics that uses the mouse. - Canvascape
A 3D adventure game (first-person shooter). - http://www.gradius-js.com
A classic 2D Space shooter game. - A Basic RayCaster
A good example of how to do animations using keyboard controls. - canvas adventure
Also a nice example that uses keyboard controls. - An interactive Blob
Have fun with the blob. - Flying through a starfield
Fly through stars, circles or squares. - iGrapher
An example of charting stock market data.
加支付宝好友偷能量挖...