携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天
大家好,我是朝朝。
# 鼠标事件 | 项目实战 续上一篇的实战,这是另一个绘图需求,实现效果如图。
这次主要是用接触canvas。
相当于,以canvas为画布,以code为画笔,设置画笔的颜色、笔的粗细、走势,重绘操作为橡皮擦。
说的这么好听,实际上只实现了一点简单的功能😏。
先入个门。
<canvas id="canvas"></canvas>
const canvas = document.getElementById('canvas'); // 获取元素
const ctx = canvas.getContext('2d'); // 获取画笔
ctx.fillStyle = 'green'; // 设置颜色
ctx.fillRect(10, 10, 150, 100); // 设置落笔点和矩形宽高
会涉及到画笔不流畅的问题
功能点
- 在画板上画直线,起点有删除按钮
- 每一条先都可以删除
- 限制只能删除n条线
- 按钮点击清空所有线条
解决方案
- 使用一个数组保存所有线段的起始&结束的坐标
- 解决1-画上每一条直线的时候给起点画上圆圈和叉叉
- 解决2-鼠标点击时判断位置是不是已有线段起点坐标的一定区域上。如果是,在数组上删除改线条,重新绘制数组表示的线条
- 解决3-在鼠标点击时判断数组有是否已经有n条线段
- 解决4-清空画布,数组置空
代码实现
- html部分
<body>
<canvas width="800" height="800" style="border: 1px solid black;" id="canvas"></canvas>
<button onclick="onClear()">清空</button>
</body>
- 样式部分
body {
margin: 0;
width: 100%;
height: 1200px;
border: 1px solid black;
}
- js部分
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let lines = []; // 保存已经在画图上的线条
/**
* 删除线段
*/
function onClear() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
lines = [];
}
/**
* 画小箭头和删除按钮
*/
function drawArrow(point) {
// 画线
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(point.startP.x, point.startP.y);
ctx.lineTo(point.endP.x, point.endP.y);
ctx.stroke();
// 画圈
ctx.beginPath();
ctx.arc(point.startP.x, point.startP.y, 10, 0, 2*Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
// 画叉-1
ctx.strokeStyle = 'white';
ctx.beginPath();
ctx.moveTo(point.startP.x-5, point.startP.y+5);
ctx.lineTo(point.startP.x+5, point.startP.y-5);
ctx.stroke();
// 画叉-2
ctx.beginPath();
ctx.moveTo(point.startP.x+5, point.startP.y+5);
ctx.lineTo(point.startP.x-5, point.startP.y-5);
ctx.stroke();
}
- 事件部分
鼠标按下:记录起点位置 / 触发删除 / 线条超过n条
document.body.addEventListener('mousedown', (e) => {
let info = {
startP: {
x: e.pageX,
y: e.pageY
}
}
// 1、鼠标点击时,点在×上,删除该线
let index = lines.findIndex(item => {
const incline = Math.sqrt(Math.pow(info.startP.x - item.startP.x, 2) + Math.pow(info.startP.y - item.startP.y, 2));
return incline < 10;
});
if (index >= 0) {
lines.splice(index, 1);
ctx.clearRect(0, 0, canvas.width, canvas.height);
lines.forEach(item => drawArrow(item));
return;
}
if (info.startP.x > canvas.width || info.startP.y > canvas.height) {
return;
}
// 2、图画上的线条小于等于4
if (lines.length >= 4) {
alert('最多只能画四条');
return;
}
lines.push(info);
document.body.addEventListener('mousemove',moveFun);
document.body.addEventListener('mouseup', upFun);
})
鼠标移动:重绘 / 越界 / 画当前线条
function moveFun(e) {
const {x, y} = lines[lines.length - 1].startP;
// 重绘
ctx.clearRect(0,0,canvas.width,canvas.height);
for(let i=0; i<lines.length - 1; i++) {
drawArrow(lines[i]);
}
// 越界了处理
if (e.pageX <= 0 || e.pageX >= canvas.width || e.pageY <= 0 || e.pageY >= canvas.height) {
lines[lines.length-1] = {
...lines[lines.length-1],
endP: {
x: e.pageX,
y: e.pageY,
}
}
ctx.clearRect(0,0,canvas.width,canvas.height);
lines.forEach(item => {
drawArrow(item);
});
document.body.removeEventListener('mousemove', moveFun);
document.body.removeEventListener('mouseup', upFun);
return;
}
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo( e.pageX, e.pageY);
ctx.stroke();
}
鼠标松开:(记录松开的坐标 & 画所有的线段)/ 线段太短不画
function upFun(e) {
lines[lines.length-1] = {
...lines[lines.length-1],
endP: {
x: e.pageX,
y: e.pageY,
}
}
// 如果移动距离小于关闭按钮的半径,就不会画
const point = lines[lines.length-1];
const incline = Math.sqrt(Math.pow(point.startP.x - point.endP.x, 2) + Math.pow(point.startP.y - point.endP.y, 2));
if (incline <= 10) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
lines.pop();
lines.forEach(item => {
drawArrow(item);
})
} else {
drawArrow(lines[lines.length-1]);
}
document.body.removeEventListener('mousemove', moveFun);
document.body.removeEventListener('mouseup', upFun);
}
参考:# Canvas
那么,next hui see