HTML5 Canvas实战

案例1:七巧板

将一系列坐标点及颜色存储在数组中,使用Canvas来绘制图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas七巧板</title>
</head>
<body>
<canvas id="canvas" style="border:1px solid #ddd;margin:50px auto" >
当前浏览器不支持Canvas
</canvas>

<script>
var TG =[
{p:[{x:0,y:0},{x:800,y:0},{x:400,y:400}],color:"#caff67"},
{p:[{x:0,y:0},{x:400,y:400},{x:0,y:800}],color:"#67becf"},
{p:[{x:800,y:0},{x:800,y:400},{x:600,y:600},{x:600,y:200}],color:"#ef3d61"},
{p:[{x:600,y:200},{x:600,y:600},{x:400,y:400}],color:"#f9f51a"},
{p:[{x:400,y:400},{x:600,y:600},{x:400,y:800},{x:200,y:600}],color:"#a594c0"},
{p:[{x:200,y:600},{x:400,y:800},{x:0,y:800}],color:"#fa8ecc"},
{p:[{x:800,y:400},{x:800,y:800},{x:400,y:800}],color:"#f6ca29"}
];
window.onload=function(){
var canvas=document.getElementById("canvas");
canvas.width=800;
canvas.height=800;
var context=canvas.getContext("2d");
for (var i = 0; i < TG.length; i++) {
draw(TG[i],context);
}
function draw(piece,ctx){
ctx.beginPath();
ctx.moveTo(piece.p[0].x,piece.p[0].y);
for (var i = 0; i < piece.p.length; i++) {
ctx.lineTo(piece.p[i].x,piece.p[i].y);
ctx.fillStyle=piece.color;
ctx.fill();
}
}
}
</script>

</body>
</html>

案例2:简易涂鸦板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas实现简易涂鸦板</title>
<style>
*{
margin: 0;
padding: 0;
}
body{
margin: 50px auto;
text-align: center;
}
</style>

</head>
<body>
<canvas id="canvas" width="500" height="500" style="box-shadow:0 5px 40px black"></canvas>

<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d')

canvas.onmousedown = function (e) {
var ev = e||window.event;
var x = ev.clientX - canvas.offsetLeft;
var y = ev.clientY - canvas.offsetTop;

context.beginPath();
context.moveTo(x,y);

canvas.onmousemove = function (e) {
var ev = e||window.event;
var x = ev.clientX - canvas.offsetLeft;
var y = ev.clientY - canvas.offsetTop;
console.log(x)
context.lineWidth = 5;
context.strokeStyle = "red";

context.lineTo(x,y);
context.stroke()
}
canvas.onmouseup = function () {
canvas.onmousemove = null;
}
}
</script>

</body>
</html>

案例3:运动小球

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas小球运动</title>
</head>
<body>
<canvas id="canvas" style="border:1px solid #ddd;margin:50px auto;">
当前浏览器不支持Canvas
</canvas>
<script>
var ball={x:512,y:100,r:20,g:2,vx:-4,vy:0,color:"#oo5588"};
window.onload=function(){
var canvas=document.getElementById("canvas");
canvas.width=800;
canvas.height=600;
var context=canvas.getContext("2d");

setInterval(function(){
render(context);
update();
},50)
}
function update(){
ball.x+=ball.vx;
ball.y+=ball.vy;
ball.vy+=ball.g;
// 碰撞检测
if (ball.y>600-ball.r) {
ball.y=600-ball.r;
ball.vy=-ball.vy*0.6;
} else if(ball.x==0){
ball.x=512;
ball.vy=-ball.vy;
ball.y=100;
}
}
function render(ctx){
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); //清除上次小球位置
ctx.fillStyle=ball.color;
ctx.beginPath();
ctx.arc(ball.x,ball.y,ball.r,0,2*Math.PI);
ctx.closePath();
ctx.fill();
}
</script>

</body>
</html>

案例4:倒计时电子钟

用二维数组存储阿拉伯数字0-9,将时间用Canvas绘制出来,’0’表示无图案,’1’表示小圆。同时绘制小球运动效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
digit =
[
[
[0,0,1,1,1,0,0],
[0,1,1,0,1,1,0],
[1,1,0,0,0,1,1],
[1,1,0,0,0,1,1],
[1,1,0,0,0,1,1],
[1,1,0,0,0,1,1],
[1,1,0,0,0,1,1],
[1,1,0,0,0,1,1],
[0,1,1,0,1,1,0],
[0,0,1,1,1,0,0]
],//0
[
[0,0,0,1,1,0,0],
[0,1,1,1,1,0,0],
[0,0,0,1,1,0,0],
[0,0,0,1,1,0,0],
[0,0,0,1,1,0,0],
[0,0,0,1,1,0,0],
[0,0,0,1,1,0,0],
[0,0,0,1,1,0,0],
[0,0,0,1,1,0,0],
[1,1,1,1,1,1,1]
],//1
...

绘制数字:
设圆的半径为R,圆与圆之间的间距为2px,所以圆所在的正方形格子的边长为2(R+1)。
其中x表示起始的横坐标,y表示起始的纵坐标,i表示行数,j表示列数,因此:
第(i,j)个圆的圆心位置:
centerx : x + j
2 (R+1) + (R+1);
centery : y + i
2 * (R+1) + (R+1)
9

简单倒计时电子钟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
var WINDOW_WIDTH = 1024;
var WINDOW_HEIGHT = 768;
var RADIUS = 8;
var MARGIN_TOP = 60;
var MARGIN_LEFT = 30;

// const endTime = new Date(2016,6,13,18,47,52);

var endTime =new Date();
endTime.setTime(endTime.getTime()+3600*1000);

var curShowTimeSeconds = 0

window.onload = function(){

var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
canvas.width = WINDOW_WIDTH;
canvas.height = WINDOW_HEIGHT;
curShowTimeSeconds = getCurrentShowTimeSeconds()

setInterval(function(){
render(context);
update();
},50)
}

function getCurrentShowTimeSeconds() {
var curTime = new Date();
var ret = endTime.getTime() - curTime.getTime();
ret = Math.round( ret/1000 )

return ret >= 0 ? ret : 0;
}

function update(){
var nextShowTimeSeconds=getCurrentShowTimeSeconds();
var nextHours = parseInt( nextShowTimeSeconds / 3600);
var nextMinutes = parseInt( (nextShowTimeSeconds - nextHours * 3600)/60 );
var nextSeconds = nextShowTimeSeconds % 60;

var CurHours = parseInt( curShowTimeSeconds / 3600);
var CurMinutes = parseInt( (curShowTimeSeconds - CurHours * 3600)/60 );
var CurSeconds = curShowTimeSeconds % 60;

if (nextSeconds!=CurSeconds) {
curShowTimeSeconds=nextShowTimeSeconds;
}
}

function render( cxt ){
cxt.clearRect(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);
var hours = parseInt( curShowTimeSeconds / 3600);
var minutes = parseInt( (curShowTimeSeconds - hours * 3600)/60 )
var seconds = curShowTimeSeconds % 60

renderDigit( MARGIN_LEFT , MARGIN_TOP , parseInt(hours/10) , cxt )
renderDigit( MARGIN_LEFT + 15*(RADIUS+1) , MARGIN_TOP , parseInt(hours%10) , cxt )
renderDigit( MARGIN_LEFT + 30*(RADIUS + 1) , MARGIN_TOP , 10 , cxt )
renderDigit( MARGIN_LEFT + 39*(RADIUS+1) , MARGIN_TOP , parseInt(minutes/10) , cxt);
renderDigit( MARGIN_LEFT + 54*(RADIUS+1) , MARGIN_TOP , parseInt(minutes%10) , cxt);
renderDigit( MARGIN_LEFT + 69*(RADIUS+1) , MARGIN_TOP , 10 , cxt);
renderDigit( MARGIN_LEFT + 78*(RADIUS+1) , MARGIN_TOP , parseInt(seconds/10) , cxt);
renderDigit( MARGIN_LEFT + 93*(RADIUS+1) , MARGIN_TOP , parseInt(seconds%10) , cxt);
}

function renderDigit( x , y , num , cxt ){

cxt.fillStyle = "rgb(0,102,153)";

for( var i = 0 ; i < digit[num].length ; i ++ )
for(var j = 0 ; j < digit[num][i].length ; j ++ )
if( digit[num][i][j] == 1 ){
cxt.beginPath();
cxt.arc( x+j*2*(RADIUS+1)+(RADIUS+1) , y+i*2*(RADIUS+1)+(RADIUS+1) , RADIUS , 0 , 2*Math.PI )
cxt.closePath()

cxt.fill()
}
}

倒计时电子钟(小球运动)

在上面案例的基础上,加上小球运动图案的绘制。
部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
var balls=[];
window.onload=function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
canvas.width=Window_width;
canvas.height=Window_height;
curShowTimeSeconds = getCurrentShowTimeSeconds();

setInterval(function(){
render(ctx);
update();
},50)

}
function update(){
...
if (nextSeconds!=CurSeconds) {
if (parseInt(CurHours/10)!=parseInt(nextHours/10)) {
addBalls(Margin_left,Margin_top,parseInt(CurHours/10));
}
if (parseInt(CurHours%10)!=parseInt(nextHours%10)) {
addBalls(Margin_left+15*(Radius+1),Margin_top,parseInt(CurHours%10));
}
if( parseInt(CurMinutes/10) != parseInt(nextMinutes/10) ){
addBalls( Margin_left + 39*(Radius+1) , Margin_top , parseInt(CurMinutes/10) );
}
if( parseInt(CurMinutes%10) != parseInt(nextMinutes%10) ){
addBalls( Margin_left + 54*(Radius+1) , Margin_top , parseInt(CurMinutes%10) );
}
if( parseInt(CurSeconds/10) != parseInt(nextSeconds/10) ){
addBalls( Margin_left + 78*(Radius+1) , Margin_top , parseInt(CurSeconds/10) );
}
if( parseInt(CurSeconds%10) != parseInt(nextSeconds%10) ){
addBalls( Margin_left + 93*(Radius+1) , Margin_top , parseInt(CurSeconds%10) );
}
curShowTimeSeconds=nextShowTimeSeconds;
}
updateBalls();
}

function updateBalls(){
for (var i = 0; i < balls.length; i++) {
balls[i].x+=balls[i].vx;
balls[i].y+=balls[i].vy;
balls[i].vy+=balls[i].g;

if (balls[i].y>=Window_height-Radius) {
balls[i].y=Window_height-Radius;
balls[i].vy=-balls[i].vy*0.75;
}
}
}

function addBalls(x,y,num){
for( var i = 0 ; i < digit[num].length ; i ++ ){
for( var j = 0 ; j < digit[num][i].length ; j ++ ){
if( digit[num][i][j] == 1 ){
var aBall = {
x:x+j*2*(Radius+1)+(Radius+1),
y:y+i*2*(Radius+1)+(Radius+1),
g:1.5+Math.random(),
vx:Math.pow( -1 , Math.ceil( Math.random()*1000 ) ) * 4,
vy:-5,
color: colors[ Math.floor( Math.random()*colors.length ) ]
}

balls.push( aBall )
}
}
}
}

function render(ctx){
ctx.clearRect(0,0,Window_width,Window_height);
...
for( var i = 0 ; i < balls.length ; i ++ ){
ctx.fillStyle=balls[i].color;

ctx.beginPath();
ctx.arc( balls[i].x , balls[i].y , Radius , 0 , 2*Math.PI , true );
ctx.closePath();

ctx.fill();
}
}

倒计时电子钟(小球运动)-性能优化

1、屏幕自适应:

1
2
3
4
5
6
7
8
9
10
11
var Window_width=1200;
var Window_height=800;
var Radius=8;
var Margin_top=60;
var Margin_left=30;
window.onload=function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
canvas.width=Window_width;
canvas.height=Window_height;
}

将上面代码改为如下代码,同时设置html文件中的html,body,canvas标签的样式:style="height:100%"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
window.onload=function(){
Window_width = document.body.clientWidth;
Window_height = document.body.clientHeight;

Margin_left = Math.round(Window_width /10);
Radius = Math.round(Window_width * 4 / 5 / 108)-1;

Margin_top = Math.round(Window_height /5);

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
canvas.width=Window_width;
canvas.height=Window_height;
}

2、性能优化:
小球会持续增多,会导致一段时间后页面卡顿,因此,需要对小球更新函数进行修改,当小球溢出Canvas画布范围时,删除小球,从而使Canvas画布里留存适量的小球数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function updateBalls(){
for (var i = 0; i < balls.length; i++) {
balls[i].x+=balls[i].vx;
balls[i].y+=balls[i].vy;
balls[i].vy+=balls[i].g;

if (balls[i].y>=Window_height-Radius) {
balls[i].y=Window_height-Radius;
balls[i].vy=-balls[i].vy*0.75;
}
}

// 性能优化:限制画布里的小球,使其不一直增加。
/*var cnt=0;
for (var i = 0; i < balls.length; i++) {
if (balls[i].x+Radius>0 && balls[i].x-Radius<Window_width) {
balls[cnt++]=balls[i];
}
}
while(balls.length>Math.min(300,cnt)){
balls.pop(); //从末尾删除小球。
}
*/

for (var i = 0; i < balls.length; i++) {
if (balls[i].x>canvas.width||balls[i].x<-Radius) {
balls.splice(i,1)
};
}
}

电子钟

将上面的倒计时变成时钟正常显示,主要是修改时间代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var endTime =new Date();
endTime.setTime(endTime.getTime()+3600*1000);
window.onload=function(){
curShowTimeSeconds = getCurrentShowTimeSeconds();
}
function getCurrentShowTimeSeconds(){
var curTime=new Date();
var ret = endTime.getTime() - curTime.getTime();
ret = Math.round( ret/1000 )

return ret >= 0 ? ret : 0;
}
//将上面的代码变成如下代码:
window.onload=function(){
curShowTimeSeconds = getCurrentShowTimeSeconds();
}
function getCurrentShowTimeSeconds(){
var curTime=new Date();
var ret=curTime.getHours()*3600+curTime.getMinutes()*60+curTime.getSeconds();
//到现在共计多少秒。
return ret;
}

坚持原创技术分享,您的支持将鼓励我继续创作!