准备工作
能画丁老头的画板
Canvas教程MDN
创建项目目录,并初始化git
面向谷歌编程
google搜js create div mdn
document.createElement('div')
google搜js insert div into page mdn
1
|
<canvas id='canvas' width="100" height="100"></canvas>
|
1
2
3
|
let div = document.createElement('div')
let canvas = document.getElementById("canvas");
canvas.appendChild(div)
|
google搜js get body mdn
代码
让canvas占满屏幕,并且没有滚动条
增加样式
div.style.xxx
点出现在鼠标中间,而不是右下角,对称,偶数
由方变圆,变实心
点击事件改成鼠标移动事件
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
|
canvas.onclick = (e) => {
//console.log()调试大法
console.log(e.clientX, e.clientY)
//创建div 在内存里,并不在页面(文档里)
let div = document.createElement('div')
div.id = 'canvas'
//绝对定位
div.style.position = 'absolute'
div.style.left = e.clientX + 'px'
div.style.top = e.clientY + 'px'
// div.style.border = '1px solid red'
div.style.width = '6px'
div.style.height = '6px'
//点出现在鼠标中间,而不是右下角
div.style.marginLeft = '-3px'
div.style.marginTop = '-3px'
//由方变圆
div.style.borderRadius = '50%'
div.style.backgroundColor = 'black'
//添加已在内存中创建的节点
canvas.appendChild(div)
}
|
这样会卡 JS操作DOM 低效 跨线程通信
用Canvas
画点
<canvas></canvas>
默认的display:inline
,类似于<img>
改为display:block
干掉影响总宽高的滚动条加上width: 100vw; height: 100vh;
但会导致canvas
模糊,所以不行
<canvas></canvas>
的宽高需在一开始就确定
可以在<canvas></canvas>
上写宽高,但会被CSS覆盖,同<img>
1
2
|
canvas.width = document.body.clientWidth
canvas.height = document.body.clientHeight
|
但body 默认是由子元素决定,未撑开
所以要直接获取文档的高度
1
2
3
|
var canvas = document.getElementById("canvas");
canvas.width = document.documentElement.clientWidth;
canvas.height = document.documentElement.clientHeight;
|
这就让canvas占满屏幕,并且没有滚动条
画点
1
2
3
4
5
6
7
8
9
|
//样式
var ctx = canvas.getContext("2d")
ctx.fillStyle = "orange";
canvas.onclick = (e) => {
console.log(e.clientX)
console.log(e.clientY)
ctx.fillRect(e.clientX-5, e.clientY-5, 10, 10)
}
|
e.clientX-5
让宽度减一半,使其在鼠标中心
onclick
改成onmousemove
没有新建任何节点,只是在画像素点,不需要操作DOM
加开关,标记,按下,抬起鼠标左键的开关记号
let painting = false
加动作,鼠标按下
1
2
3
|
canvas.onmousedown = () => {
painting = true
}
|
加判断,开关开启,动作执行
1
2
3
4
5
6
7
8
9
|
canvas.onmousemove = (e) => {
if (painting === true) {
console.log(e.clientX)
console.log(e.clientY)
ctx.fillRect(e.clientX - 5, e.clientY - 5, 10, 10)
} else {
console.log('什么都不做')
}
}
|
加关闭触发动作
1
2
3
|
canvas.onmouseup = () => {
painting = false
}
|
锯齿太严重,换圆形试试
1
2
3
4
|
ctx.beginPath();
ctx.arc(e.clientX, e.clientY, 10, 0, 2 * Math.PI);
ctx.fill()
ctx.stroke();
|
一些注意事项
浏览器会调用
canvas.onmousemove(事件相关信息)
,传参e
单位
<canvas></canvas>
是HTML标签的单位同<img>
1
|
<canvas id='canvas' width="100" height="100"></canvas>
|
100vh
是CSS单位
canvas.width
也是HTML的属性单位,而canvas.style.width
的单位是CSS样式的单位,即
1
|
<canvas id='canvas' width="100" height="100" style= 'width:100vh'></canvas>
|
手机适配
google搜js detect touch support
,得判断代码
1
2
|
var isTouchDevice = 'ontouchstart' in document.documentElement;
console.log(isTouchDevice);
|
写判断
1
2
3
4
5
|
if (isTouchDevice === true) {
// console.log('目前什么也不写');
} else {
//把PC端的代码扔进来
}
|
把PC端的代码扔进else{}
里后,开始写手机端代码
1
2
3
4
|
//监听触屏事件
canvas.ontouchmove = (e) => {
console.log(e)
}
|
第一根手指
1
2
3
4
5
6
7
|
//监听触屏事件
canvas.ontouchmove = (e) => {
// console.log(e.touches[0]);//获取第一个手指
let x = e.touches[0].clientX;
let y = e.touches[0].clientY;
console.log(x, y)
}
|
后面加上PC端抄来的代码,改吧改吧
1
2
3
4
|
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
|
发现不用监听手指抬起,比PC端更简单,特殊app内置浏览器会有向下划同步向下滚动的bug,以后处理
画线
回到PC页面,由点画线,解决不连贯的问题
从画三角,抽象一个画线的函数
1
2
3
4
5
6
7
8
9
10
|
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.strokeStyle = 'none';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(500, 500);
ctx.lineTo(0, 500);
ctx.closePath();
ctx.stroke();
|
画线的函数
1
2
3
4
5
6
|
function drawLine(x1, x2, y1, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
|
需要记录上一次的位置,每当按下鼠标后,就记录一次,修改canvas.onmousedown
事件
1
2
3
4
|
canvas.onmousedown = (e) =>{
painting = true;
last = [e.clientX, e.clientY];
}
|
修改canvas.onmousemove
事件
1
|
drawLine(last[0], last[1], e.clientX, e.clientY);
|
画完,需要每次更新last = [e.clientX, e.clientY]
1
2
|
drawLine(last[0], last[1], e.clientX, e.clientY);
last = [e.clientX, e.clientY];//实时更新当前位置
|
流程小结
- 在最外面声明
last
变量
- 再当鼠标按下时,赋值此时的点的位置坐标给
last
- 在鼠标动的时候,以
last
和移动后点坐标的四个值,画线
- 更新当前点的坐标
其他细节
- 线型变粗
ctx.lineWidth
- 线转弯处的节点有缺口,搜
js line end round
,得ctx.lineCap
,使其圆润
- 实验放大
ctx.lineWidth = 4; drawLine(0,0,300,300); drawLine(300,300,500,200);
,突然变向
别忘了改手机端
- 手机没有
onmousedown
事件,取而代之的是ontouchstart
事件
- 相对应的还有
ontouchstart
和ontouchend
- 坐标位置变为
let x = e.touches[0].clientX; let y = e.touches[0].clientY;
- 同样要记录上次位置,和更新上次位置
部署到 GitHub 上
我的项目地址
Canvas画板(第一版)预览请点击
总结
事物规律,常识抽象到代码
思维方式到了,直到搜什么
用console.log()
验证
不断试错
2020版前端体系课【方方】之【JS全解】Canvas 实践—画图板
方方的部署结果