Canvas实现基本画图
JS操作DOM很慢,改用canvas
canvas是用 width 和 height 属性设置宽高的,用CSS设置会拉伸模糊
为了适配手机,需判断是否是触屏
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>画板</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById("canvas");
// 获取文档宽度和高度,设置canvas的html属性
canvas.width = document.documentElement.clientWidth;
canvas.height = document.documentElement.clientHeight;
const ctx = canvas.getContext("2d");
let lineWidth = 6
ctx.lineWidth = lineWidth; // 设置画线宽度
ctx.lineCap = "round"; // 防止转折处断开
let painting = false;
let last = [0, 0];
// 画线函数
function drawLine(lastX, lastY, newX, newY) {
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(newX, newY);
ctx.stroke();
}
// 画圆
function drawCircle(x, y, radius) {
ctx.beginPath()
ctx.arc(x, y, radius, 0, 2 * Math.PI, true)
ctx.fill()
}
// 检测是否支持触屏
const isTouchDevice = "ontouchstart" in document.documentElement;
if (isTouchDevice) {
canvas.ontouchstart = (e) => {
const x = e.touches[0].clientX;
const y = e.touches[0].clientY;
last = [x, y];
drawCircle(x, y, lineWidth / 2)
};
canvas.ontouchmove = (e) => {
const x = e.touches[0].clientX;
const y = e.touches[0].clientY;
drawLine(last[0], last[1], x, y);
last = [x, y];
};
} else {
canvas.onmousedown = (e) => {
painting = true;
last = [e.clientX, e.clientY];
drawCircle(e.clientX, e.clientY, lineWidth / 2)
};
canvas.onmousemove = (e) => {
if (painting === true) {
drawLine(last[0], last[1], e.clientX, e.clientY);
last = [e.clientX, e.clientY];
}
};
canvas.onmouseup = () => {
painting = false;
};
}
</script>
</body>
</html>
添加橡皮擦
使用 clearRect API 实现橡皮擦功能
let eraserEnabled = false
canvas.onmousedown = (e) => {
painting = true
last = [e.clientX, e.clientY]
if (eraserEnabled) {
ctx.clearRect(e.clientX - 10, e.clientY - 10, 20, 20)
} else {
drawCircle(e.clientX, e.clientY, lineWidth / 2)
}
}
canvas.onmousemove = (e) => {
if (painting === true) {
if (eraserEnabled) {
ctx.clearRect(e.clientX - 10, e.clientY - 10, 20, 20)
} else {
drawLine(last[0], last[1], e.clientX, e.clientY)
last = [e.clientX, e.clientY]
}
}
}
添加颜色选择功能
<ul class="colorPicker">
<li class="black active"></li>
<li class="blue"></li>
<li class="red"></li>
<li class="green"></li>
<li class="yellow"></li>
</ul>
利用事件委托,将事件监听放在父元素上
const colors = {
black: '#000',
red: '#ff1a40',
blue: '#1a8cff',
green: '#2bd965',
yellow: '#ffdd33',
}
const colorPicker = document.querySelector('.colorPicker')
colorPicker.addEventListener('click', (e) => {
if (e.target !== e.currentTarget) {
const oldPicked = document.querySelector('.colorPicker > li.active')
if (oldPicked) oldPicked.classList.remove('active')
e.target.classList.add('active')
const classname = e.target.className.replace('active', '').trim()
const pickedColor = colors[classname]
ctx.strokeStyle = pickedColor
ctx.fillStyle = pickedColor
}
})
清空画板
clearBtn.onclick = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
保存成图片
canvas默认背景是透明的,先写个初始化背景函数, 在程序开始以及清空画布时调用
let pickedColor = '#000'
const initBG = () => {
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = pickedColor
}
保存成图片的方法
save.onclick = () => {
const url = canvas.toDataURL('image/jpeg')
const a = document.createElement('a')
document.body.append(a)
a.href = url
a.download = 'my-drawing'
a.target = '_blank'
a.click()
}
修改画笔粗细
通过 ctx.lineWidth 属性修改画笔粗细
通过 e.target.nodeName 判断点击的是什么元素
该功能更多的是CSS样式的编写,但也没什么难度,就不贴上代码了
const thicknessHash = {
1: 4,
2: 7,
3: 10,
4: 13,
5: 16,
}
const thicknessCircle = document.querySelector(
'.thickness > .circle-wrapper > .circle'
)
const thicknessNumber = document.querySelector('.thickness > .number')
const thicknessPicker = document.querySelector('.thickness-picker > ul')
thicknessPicker.onclick = (e) => {
if (e.target.nodeName === 'LI') {
const oldPicked = document.querySelector(
'.thickness-picker > ul > li.active'
)
oldPicked.classList.remove('active')
e.target.classList.add('active')
const num = e.target.innerText
ctx.lineWidth = thicknessHash[num]
thicknessCircle.style.width = thicknessHash[num] + 'px'
thicknessCircle.style.height = thicknessHash[num] + 'px'
thicknessNumber.innerText = num
}
}
总结
整个 Canvas画板 都用原生JS编写,实现了画笔颜色选取,画笔粗细切换,橡皮擦,一键清空画布以及保存图片等功能,项目中用的关键技术有:Canvas API, DOM原生API,事件委托,flex布局,iconfont svg图标,项目使用了 parcel 编译和打包。
预览链接
源代码