浅谈canvas和相关实战

涂鸦部分
  • 为了兼容性,使用canvas.getContext判断是否有该属性。
  • 使用canvas.getContext()获得画板执行上下文。
  • 给canvas添加鼠标按下事件。
    • 获取鼠标按下时,相对画布的x,y坐标。
    • 设置画笔颜色,以及笔的宽度(ctx.lineWidth);
    • 开始画线。
canvas的lineCap属性
  • lineCap属性是设置或返回线条末端线帽的样式,其中,“round”和“square”会使线条稍微变长。

  • context.lineCap="butt|round|square";

    描述
    butt 默认。向线条的每个末端添加平直的边缘。
    round 向线条的每个末端添加圆形线帽。
    square 向线条的每个末端添加正方形线帽。
globalCompositeOperation 属性
  • globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上。

    源图像 = 您打算放置到画布上的绘图。

    目标图像 = 您已经放置在画布上的绘图。

  • context.globalCompositeOperation="source-in";在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas{
box-shadow: 0 0 10px black;
margin: 0 auto;
display: block;
margin-top: 30px;
}
</style>
</head>
<body>
<canvas width="400" height="400"></canvas>
</body>
</html>
<script>
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'blue';
ctx.fillRect(20,20,100,100);

ctx.globalCompositeOperation = 'source-atop'
ctx.fillStyle = 'red';
ctx.fillRect(80,80,100,100);
</script>
请求关键帧运动
  • window.requestAnimationFrame(move);系统自动计算出什么时候执行该函数或方法。

  • 与setTimeout与setInterval一样设置定时运动,但window.requestAnimationFrame(move);更流畅,且丢帧情况比较少。

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">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>涂鸦</title>
<style>
html,
body,
canvas {
margin: 0;
padding: 0;
}

canvas {
background-color: gray;
}
</style>
</head>

<body>
<canvas id="canvas" width="400" height="400"></canvas>
<button onclick="clear()">清除</button>
</body>

</html>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// 画笔部分
canvas.onmousedown = function (e) {
var x, y;
x = e.clientX - canvas.offsetLeft;
y = e.clientY - canvas.offsetTop;
ctx.lineWidth = 20;
ctx.beginPath();
ctx.strokeStyle = 'blue';

ctx.moveTo(x, y);
canvas.onmousemove = function (e) {
ctx.lineTo(e.clientX - canvas.offsetLeft, e.clientY - canvas.offsetTop);
ctx.stroke();
}
canvas.onmouseup = function () {
canvas.onmousemove = '';
}
}
</script>
图像水平移动
ctx.drawImage()
  • drawImage()方法能在画布上绘制图像,画布或视频,也能够绘制图像的某些部分,以及增加或减少图像的尺寸。
  • js语法
  1. 在画布上定位图像:ctx.drawImage(img,x,y);
  2. 在画布上定位图像并规定图像的宽高:ctx.drawImage(img,x,y,width,height);
  3. 剪切图像,并在画布上定位被剪切的部分:ctx.drawImage(img,sx,sy,swidth,sheigt,x,y,width,height);
参数 描述
img 规定要使用的图像、画布或视频。
sx 可选。开始剪切的 x 坐标位置。
sy 可选。开始剪切的 y 坐标位置。
swidth 可选。被剪切图像的宽度。
sheight 可选。被剪切图像的高度。
x 在画布上放置图像的 x 坐标位置。
y 在画布上放置图像的 y 坐标位置。
width 可选。要使用的图像的宽度(伸展或缩小图像)。
height 可选。要使用的图像的高度(伸展或缩小图像)。
1
2
3
4
5
6
7
8
//一般需要先指定图片属性
var img = new Image();
img.src = '*.png';

//图片加载完完才开始绘制
img.onload = function(){
ctx.drawImage(img,x,y);
}
ctx.createPattern()
  • ctx.createPattern()方法是在指定的方向内重复指定的元素。元素可以是图片,视频或其他canvas元素。

  • 被重复的元素可用于绘制/填充矩形,圆形或线条等等。

  • ctx.createPattern(image,"repeat|repeat-x|repeat-y|no-repeat");

    参数 描述
    image 规定要使用的模式的图片、画布或视频元素。
    repeat 默认。该模式在水平和垂直方向重复。
    repeat-x 该模式只在水平方向重复。
    repeat-y 该模式只在垂直方向重复。
    no-repeat 该模式只显示一次(不重复)。
记录状态save()
  • 可以通过ctx.save()方法保存当前环境的状态,包括位置,画笔颜色等。

  • ctx.restore()则是还原之前画过的路径状态和属性。

    注意:save的存储方式类似于栈的存储结构;即第一次调用的状态是倒数第一次save的状态,第二次调用的状态是倒数第二次save的状态。

改变原点位置translate
  • translate() 方法重新映射画布上的 (0,0) 位置。

    注意:当您在 translate() 之后调用诸如 fillRect() 之类的方法时,值会添加到 x 和 y 坐标值上。

  • 在位置 (10,10) 处绘制一个矩形,将新的 (0,0) 位置设置为 (70,70)。再次绘制新的矩形(请注意现在矩形从位置 (80,80) 开始绘制)

项目

水平全景展示项目
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas{
box-shadow: 0 0 10px #123;
display: block;
margin: 0 auto;
margin-top: 25px;
}
</style>
</head>
<body>
<canvas width="800" height="400"></canvas>
</body>
</html>
<script>
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
// 创建图片
var img = new Image();
img.src = '5.png';
img.onload = function(){
drawImage();
}
// 图片绘制开始点
var x = 0;
function drawImage(){
// 记录状态
ctx.save();
ctx.clearRect(0,0,canvas.width,canvas.height);
// 图片绘制开始点向左移动
ctx.translate(-x,0);
// 图片将画布铺满
ctx.drawImage(img,0,0);
// 为实现无缝连接再画一张图片
ctx.drawImage(img,canvas.width,0);
x++;
// 当第一张图片溢出画布时,就将x置为0.
if(x >= canvas.width){
x = 0;
}
// 还原之前画过的路径状态和属性。
ctx.restore();
// arguments.callee等价于drawImage
// 自动定时调用函数
window.requestAnimationFrame(arguments.callee);
}
</script>
球碰撞项目
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
86
87
88
89
90
91
92
93
94
95
96
97
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,
body,
canvas {
margin: 0;
padding: 0;
}

canvas {
box-shadow: 0 0 10px black;
display: block;
margin: 0 auto;
margin-top: 30px;
}
</style>
</head>

<body>
<canvas width="600" height="400"></canvas>
</body>

</html>
<script>
// 获取canvas对象
var canvas = document.querySelector('canvas');
// 获取执行上下文
var ctx = canvas.getContext('2d');

// 建一个画⚪的类
function Circle(x, y, r, color, speed) {
this.x = x;
this.y = y;
this.r = r;
this.color = color;
this.speed = speed;
}

// 在原型上定义draw方法画圆
Circle.prototype.draw = function () {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill();
}

// 在原型上定义move移动
Circle.prototype.move = function(){
this.x += this.speed;
this.y += this.speed;
if(this.x < this.r || this.x + this.r > canvas.width){
this.speed *= -1;
}else if(this.x == this.r && this.y == this.r){
// 解决正好卡在对角的球
this.speed *= -1;
}
if(this.y < this.r || this.y + this.r > canvas.height){
this.speed *= -1;
}
}
var circle1 = new Circle(40,40, 20, 'red', 2);
var circle2 = new Circle(190,250, 20, 'blue', 2);
circle1.draw();
circle2.draw();
actions();
function actions(){
ctx.clearRect(0,0,canvas.width,canvas.height);
circle1.move();
circle2.move();
circle1.draw();
circle2.draw();
// 检测两个球是否相撞
if(isCircleHit(circle1,circle2)){
circle1.speed *= -1;
circle2.speed *= -1;
}
window.requestAnimationFrame(arguments.callee);
}
function isCircleHit(circle1,circle2){
var min1 = circle1.x;
var min2 = circle2.x;
var max1 = circle1.x + circle1.r;
var max2 = circle2.x + circle2.r;
var min = Math.min(min1.min2);
var max = Math.max(max1,max2);
if(min >= max){
return true;
}
return false;
}
</script>
刮刮卡项目

实现:1. 有一张图和一个画布。

  1. 给图片蒙上一张灰色蒙版,当鼠标按下时获取当前鼠标相对画布的坐标。
  2. 设置ctx.globalCompositeOperation 为 ‘destination-out’,在源图像(灰色蒙版)中显示目标图像(画笔)。只有源图像之内的目标图像部分会被显示,源图像是透明的。
  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">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,
body,
canvas {
margin: 0;
padding: 0;
}

canvas {
position: absolute;
left: 0;
}
</style>
</head>

<body>
<img src="5.png" alt="Image" width="300" height="400">
<canvas width="300" height="400"></canvas>
</body>
</html>
<script>
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'gray';
ctx.fillRect(0, 0, 300, 400);
ctx.globalCompositeOperation = 'destination-out';
ctx.lineWidth = 10;
ctx.lineCap = 'round';
canvas.onmousedown = function (e) {
ctx.beginPath();
ctx.moveTo(e.clientX, e.clientY);
canvas.onmousemove = function (e) {
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
}
canvas.onmouseup = function(e){
canvas.onmousemove = '';
}

}
</script>
文章目录
  1. 1. 涂鸦部分
  2. 2. canvas的lineCap属性
  3. 3. globalCompositeOperation 属性
  4. 4. 请求关键帧运动
  5. 5. 图像水平移动
  6. 6. ctx.drawImage()
  7. 7. ctx.createPattern()
  8. 8. 记录状态save()
  9. 9. 改变原点位置translate
  • 项目
    1. 1. 水平全景展示项目
    2. 2. 球碰撞项目
    3. 3. 刮刮卡项目
  • |