Canvas 图形的绘制与常见交互

canvas 构图基础

矩形
三角形
圆形
多边形
构图的两种方法
Path2D
1
2
3
4
5
6
7
8
9
10
11

let canvas = document.getElementsByTagName('canvas')[0];
if (canvas.getContext) {
let ctx = canvas.getContext('2d');

// 描绘路径
let rectangle = new Path2D();
rectangle.rect(10,10,100,100);

ctx.stroke(rectangle); // 以路径形式绘制
}

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>

canvas{
background:#9AA4FF;
}

</style>
</head>
<body>
<h3>canvas画布:随鼠标移动的图像</h3>

<canvas id="HuaBu">
您的浏览器不支持canvas画布
</canvas>

<script>

var w=600;
var h=400;

HuaBu.width=w; //画布的宽
HuaBu.height=h; //画布的宽

//创建画布
var ctx=HuaBu.getContext("2d");
var peo=new Image();
peo.src="imgs/peo.png";

peo.onload=function () {

//为canvas元素绑定事件监听:让图像画在鼠标移动位置
HuaBu.onmousemove=function (e) {

var x=e.offsetX;
var y=e.offsetY;

var peoWidth=peo.width;
var peoHeight=peo.height;

console.log(x+"--"+y);

ctx.clearRect(0,0,w,h); //清除画布
ctx.drawImage(peo,x-peoWidth/2,y-peoHeight/2); //画图像



};
}

</script>
</body>
</html>

涂鸦
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>

canvas{
background:#9AA4FF;
}

</style>
</head>
<body>
<h3>canvas画布:随鼠标移动的图像</h3>

<canvas id="HuaBu">
您的浏览器不支持canvas画布
</canvas>

<script>

var w=800;
var h=800;

HuaBu.width=w; //画布的宽
HuaBu.height=h; //画布的宽

//创建画布
var ctx=HuaBu.getContext("2d");

// var peo=new Image();
// peo.src="./img/01.png";

let rectangle = new Path2D();
rectangle.rect(10,10,100,100);

// ctx.stroke(rectangle); // 以路径形式绘制

// rectangle.onload=function () {

HuaBu.addEventListener('mousedown', function() {
//为canvas元素绑定事件监听:让图像画在鼠标移动位置
move()
}, false);

HuaBu.addEventListener('mouseup',function() {
HuaBu.onmousemove=function (e) {

// ctx.clearRect(0,0,w,h); //清除画布
};
},false);


function move() {
HuaBu.onmousemove=function (e) {

var x=e.offsetX;
var y=e.offsetY;

// var peoWidth=peo.width;
// var peoHeight=peo.height;

// console.log(x+"--"+y);

// ctx.clearRect(0,0,w,h); //清除画布
// ctx.drawImage(peo,x-peoWidth/8,y-peoHeight/8); //画图像
ctx.beginPath();
ctx.rect(x,y,50,50)
ctx.stroke();
};
}

</script>


</body>
</html>

移动的方块
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>

canvas{
background:#9AA4FF;
}

</style>
</head>
<body>
<h3>canvas画布:随鼠标移动的图像</h3>

<canvas id="HuaBu">
您的浏览器不支持canvas画布
</canvas>

<script>

var w=800;
var h=800;

HuaBu.width=w; //画布的宽
HuaBu.height=h; //画布的宽

//创建画布
var ctx=HuaBu.getContext("2d");

HuaBu.addEventListener('mousedown', function() {
//为canvas元素绑定事件监听:让图像画在鼠标移动位置
move()
}, false);

HuaBu.addEventListener('mouseup',function() {
HuaBu.onmousemove=function (e) {

// ctx.clearRect(0,0,w,h); //清除画布
};
},false);


function move() {
HuaBu.onmousemove=function (e) {

var x=e.offsetX;
var y=e.offsetY;

ctx.clearRect(0,0,w,h); //清除画布
ctx.beginPath();
ctx.rect(x,y,50,50)
ctx.stroke();
};
}

</script>


</body>
</html>

运动的图片
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
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<!-- TemplateBeginEditable name="doctitle" -->
<title>我的图片</title>
</head>
<body>
<canvas id="Map" width="2000" height="5000" style="background:gray;"></canvas>
<script>
// 设置绘图环境
var myMap = document.getElementById("Map");
var cxt = myMap.getContext('2d');
// 设置图像位置初始位置的变量
var x = 20;
var y = 20;
// 创建绘图对象,并且画出来
var img = new Image();
img.src = "img/01.png";
draw();
function draw() {
cxt.clearRect(0, 0, 2000, 5000);
x += 1;
y += 5;
cxt.drawImage(img, x, y, 80, 80);
console.log('2222');
}
// window.setInterval(draw, 10);

window.setTimeout(function() {
draw();
setTimeout(arguments.callee, 10);
},100);
</script>
</body>
</html>

canvas 与 vue 的结合使用
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<template>
<div class="project-progress">
<RouterNav />
<div class="project-progress-body">
<div id="project-progress-canvas-container">
<canvas id="project-progress-canvas" width="800" height="600" @wheel="canvasScale" v-dialogDrag></canvas>
</div>
</div>
</div>
</template>
<script>
import RouterNav from '@/components/RouterNav'
import canvas from './canvas.js'
export default {
name: 'ProjectProgress',
components: {
RouterNav
},
data() {
return {
canvasX: 0,
canvasY: 0,
scale: 1
}
},
directives: {
dialogDrag: {
bind(el, binding, vnode, oldVnode) {
el.onselectstart = new Function("return false");

let moveDown = e => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - el.offsetLeft;
const disY = e.clientY - el.offsetTop;
// 记录上次基准点位置
const lastX = vnode.context.canvasX;
const lastY = vnode.context.canvasY;

document.onmousemove = function(e) {
// 通过事件委托,计算移动的距离
const l = e.clientX - el.offsetLeft - disX;
const t = e.clientY - el.offsetTop - disY;

const scale = vnode.context.scale;

// 移动后修改基准点
vnode.context.canvasX = lastX - l / scale ;
vnode.context.canvasY = lastY + t / scale ;

// 移动当前元素
vnode.context.draw();
};

document.onmouseup = function(e) {
document.onmousemove = null;
document.onmouseup = null;
};
};
el.onmousedown = moveDown;
}
}
},
methods: {
resizeFunc() {
var can = document.getElementById('project-progress-canvas');
var canvasContainer = document.getElementsByClassName("project-progress")[0];
if (can) {
can.height = canvasContainer.offsetHeight;
can.width = canvasContainer.offsetWidth;
this.draw();
}
},
draw() {
canvas('project-progress-canvas', this.canvasX, this.canvasY, this.scale);
},
canvasScale(e) {

if (e.wheelDeltaY > 0) {
this.scale *= 1.07
} else {
this.scale *= 0.93
}

this.draw();
}
},
mounted() {
//初始化时设置canvas大小和容器一致;
this.resizeFunc();
//新增window的resize事件,保证canvas始终和容器大小一致;
window.addEventListener('resize', this.resizeFunc);


},
beforeRouteLeave(to, from, next) {

window.removeEventListener('resize', this.resizeFunc);
next();

}
}

</script>
<style scoped>
.project-progress {
height: 100%;
display: flex;
width: 100%;
overflow: hidden;
}

.project-progress-body {
height: calc(100% - 80px);
font-size: 0;
display: flex;
width:100%;
}

#project-progress-canvas-container {
height: 100%;
width: 100%;
font-size: 0;
display: inline-block;
}

</style>


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

export default function(id, canvasX, canvasY, scale) {

var can = document.getElementById(id);
var ctx = can.getContext('2d');

var w = can.width;
var h = can.height;

ctx.setTransform(scale, 0, 0, scale, w / 2, h / 2);
//绘制背景
ctx.fillStyle = '#E8E8E8';
ctx.fillRect(-w / 2, -h / 2, w, h);
ctx.fillRect(-w / 2 / scale, -h / 2 / scale, w / scale, h / scale);


ctx.fillStyle = 'red';
ctx.beginPath();


ctx.fillRect(-330 - canvasX, canvasY - 110, 400, 200);


}


点击生成图块,并追加状态信息

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.bkg {
position: absolute;
z-index: 0;
background-image: url('./img/02.png');
background-repeat: none;
background-size: 100% 100%;
overflow-x: scroll;
overflow-y:auto;
top: 2rem;
}
#mycanvas {
top: 2rem;
position: absolute;
z-index: 1;
}
.clearSelect {
float: right;
/* margin-right: 35rem; */
}
canvas {
border: 1px solid green;
}
</style>
</head>
<body>
<button class="clearSelect" id="clearSelect">清除选中</button>
<button class="clearSelect" id="click-select">点击选择</button>
<button class="clearSelect" id="null-select">释放点选</button>
<div class="bkg">
</div>
<canvas id="mycanvas" ></canvas>
</body>
<script type="text/javascript">

let w = 3810;
let h = 900;
let blue = 'blue';

let arrObj = [];
let count = 0;

mycanvas.width = w;
mycanvas.height = h;

let bkg = document.getElementsByClassName('bkg')[0];
bkg.style.width = w+'px';
bkg.style.height = h+'px';


let ctx = mycanvas.getContext('2d');

reset();
restore();
// 初始化
function reset() {
let val = localStorage.getItem('key');

if ( localStorage.length === 0) {
console.log('请先选点');
} else {
let arr = [];
arr = JSON.parse(val);
arrObj = arr;

for (let i = 0; i < arr.length; i++) {
for ( let h in arr[i]) {
ctx.beginPath();
ctx.fillStyle = blue;
ctx.rect(arr[i][h].x,arr[i][h].y,20,20);
ctx.fill();
}
}
}
}


// 点击选择

var clickSelect = document.getElementById('click-select');
var nullSelect = document.getElementById('null-select');

clickSelect.onclick = function () {
move();
};

// 释放点选
nullSelect.onclick = function () {
mycanvas.onclick = null;
restore()
}


// 清除画布
clearSelect.onclick = function() {
clearDraw();
}

function clearDraw() {
ctx.clearRect(0,0,w,h);
localStorage.clear();
arrObj = [];
count = 0;
}

// 普通状态

function restore() {

mycanvas.onclick = function(e) {
state(e,function(info) {
fillText({
x:info.point.x,
y:info.point.y,
ctx:ctx,
info:info.info
})
})

setTimeout (()=>{
ctx.clearRect(0,0,w,h);
reset();
},1000)
}

mycanvas.ondblclick = function (e) {

state(e,function(info) {

alert(`我是第${info.count}个执行点`);

});

};

}


function move() {

mycanvas.onclick = function (e) {

count++;
let info = textInfo();
let x = e.offsetX;
let y = e.offsetY;

ctx.beginPath();
ctx.fillStyle = blue;
ctx.rect(x,y,20,20);
ctx.fill();

console.log(x,y,'标记点');

let str = `{point:{x:${x},y:${y}},count:${count},info:'${info}'}`;

arrObj.push(eval('(' + str + ')')); // 点击过的点,存储进数组中

localStorage.setItem('key',JSON.stringify(arrObj)); ;

setTimeout(()=> {
console.log(`你选中的第${count}个标记点为${x},${y}`)
},100)
}
}

//填写数据

function textInfo() {
let text = prompt("请输入",'');
if (text !== null || text!== '') {
return text;
}
}

function fillText({ctx,info,x,y}) {
ctx.beginPath();
ctx.fillStyle = 'red';
ctx.font="20px Georgia";
ctx.fillText(info,x,y);
}

function state(e,cb) {
let x = e.offsetX;
let y = e.offsetY;

let pointX,pointY,info;

for ( let i = 0; i < arrObj.length;i++ ) {

info = arrObj[i];
pointX = arrObj[i].point.x;
pointY = arrObj[i].point.y;

if ( x >= pointX && x <= (pointX + 20) && y >= pointY && y <= (pointY + 20) ) {
cb(info);
} else {
console.log('你点击到了空白区域')
}
}
}


</script>
</html>