先贴图
1.先来两个canvas,一个存,一个画展示
<div class="box" style="width: 100%;height: 800px " v-if="showThis">
<!--划线canvas-->
<canvas id="canvas" width="1000px" height="600px" ref="canvas"></canvas>
<!--储存-->
<canvas
id="canvasSave"
width="1000px"
height="600px"
ref="canvasSave"
></canvas>
</div>
2.初始化canvas 并绑定移动点击事件
initCanvas() {
//初始化
//初始化
this.can = document.getElementById("canvas");
this.ctx = this.can.getContext("2d");
this.canSave = document.getElementById("canvasSave");
this.ctxSave = this.canSave.getContext("2d");
this.ctx.strokeStyle = "rgba(213,16,34,1)"; //线条颜色
this.ctx.lineWidth = 1; //线条粗细
this.ctxSave.strokeStyle = "rgba(213,16,34,1)"; //线条颜色
this.ctxSave.lineWidth = 1; //线条粗细
var that = this;
if (this.pointArr.length > 0) {
//画布回显
this.canvasSave1(this.hasSave); //保存点线同步到另一个canvas
this.saveCanvas(); //生成画布
}
this.can.addEventListener(
//点击
"click",
function(e) {
that.canClick(e);
},
false
);
this.can.addEventListener(
//移动
"mousemove",
function(e) {
that.canMove(e);
},
false
);
},
3.画线,画完判断是否重叠,覆盖之后储存展示
drawLine() {
//划线
if (this.oIndex > 0 && this.pointArr.length > 0) {//自动吸合
var piX = this.pointArr[0].x;
var piY = this.pointArr[0].y;
//画点
this.makearc(
this.ctx,
piX,
piY,
this.GetRandomNum(2, 2),
0,
180,
"rgba(213,16,34,1)"
);
this.pointArr.push({ x: piX, y: piY });
var checkResult = this.checkLineToLine(this.pointArr); //本次绘画线是否重合
if (checkResult) {
//无相交
var checkLineToSurfaceResult = this.checkLineToSurface([
this.pointArr[this.pointArr.length - 2],
this.pointArr[this.pointArr.length - 1]
]);
if (!checkLineToSurfaceResult) {
//与面相交
alert("与面相交");
this.pointArr = [];
this.ctx.clearRect(0, 0, this.ctx.width, this.ctx.height);
this.canvasSave(this.pointArr);
} else {
if (this.hasSave.length < 1) {
this.canvasSave(this.pointArr); //保存点线同步到另一个canvas
this.saveCanvas(); //生成画布
} else {
var checkCoverResult = this.checkCover(this.hasSave); //是否覆盖别的图形
if (checkCoverResult) {
alert("覆盖");
this.pointArr = [];
this.ctx.clearRect(0, 0, this.ctx.width, this.ctx.height);
this.canvasSave(this.pointArr);
} else {
this.canvasSave(this.pointArr); //保存点线同步到另一个canvas
this.saveCanvas(); //生成画布
}
}
}
} else {
alert("与线有交叉");
//有相交,有相交清除
this.pointArr = [];
this.ctx.clearRect(0, 0, this.ctx.width, this.ctx.height);
this.canvasSave(this.pointArr);
}
} else {
piX = this.pointX;
piY = this.pointY;
this.makearc(
this.ctx,
piX,
piY,
this.GetRandomNum(2, 2),
0,
180,
"rgba(213,16,34,1)"
);
this.pointArr.push({ x: piX, y: piY });
var checkResult = this.checkLineToLine(this.pointArr);
if (checkResult) {
//线无相交
if (this.pointArr.length < 2) {
//线与面是否相交
} else {
var checkLineToSurfaceResult = this.checkLineToSurface([
this.pointArr[this.pointArr.length - 2],
this.pointArr[this.pointArr.length - 1]
]);
if (!checkLineToSurfaceResult) {
//与面相交
alert("与面相交");
this.pointArr = [];
this.ctx.clearRect(0, 0, this.ctx.width, this.ctx.height);
this.canvasSave(this.pointArr);
}
}
} else {
alert("线有交叉");
//有相交,有相交清除
this.pointArr = [];
this.ctx.clearRect(0, 0, this.ctx.width, this.ctx.height);
this.canvasSave(this.pointArr);
}
}
},
4.点的时候判断是否在已画图形内,每次画成线判断是否交叉。
canMove(e) {
//跟随鼠标移动划线
if (e.offsetX || e.layerX) {
this.pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
this.pointY = e.offsetY == undefined ? e.layerY : e.offsetY;
var piX, piY;
/*清空画布*/
this.ctx.clearRect(0, 0, this.can.width, this.can.height);
/*鼠标下跟随的圆点*/
this.makearc(
this.ctx,
this.pointX,
this.pointY,
this.GetRandomNum(2, 2),
0,
180,
"rgba(213,16,34,1)"
);
if (this.pointArr.length > 0) {
if (
this.pointX > this.pointArr[0].x - 15 &&
this.pointX < this.pointArr[0].x + 15 &&
this.pointY > this.pointArr[0].y - 15 &&
this.pointY < this.pointArr[0].y + 15
) {
if (this.pointArr.length > 1) {
piX = this.pointArr[0].x;
piY = this.pointArr[0].y;
this.ctx.clearRect(0, 0, this.can.width, this.can.height);
this.makearc(
this.ctx,
piX,
piY,
this.GetRandomNum(2, 2),
0,
180,
"rgba(213,16,34,1)"
);
this.oIndex = 1;
}
} else {
piX = this.pointX;
piY = this.pointY;
this.oIndex = -1;
}
/*开始绘制*/
this.ctx.beginPath();
this.ctx.moveTo(this.pointArr[0].x, this.pointArr[0].y);
if (this.pointArr.length > 1) {
for (var i = 1; i < this.pointArr.length; i++) {
this.ctx.lineTo(this.pointArr[i].x, this.pointArr[i].y);
}
}
this.ctx.lineTo(piX, piY);
this.ctx.fillStyle = "rgba(213,16,34,1)"; //填充颜色
this.ctx.fill(); //填充
this.ctx.stroke(); //绘制
}
}
},
5.生成和回显
canvasSave1(pointArr) {
//回显
console.log(pointArr);
this.ctxSave.clearRect(0, 0, this.ctxSave.width, this.ctxSave.height);
this.ctxSave.beginPath();
if (pointArr.length > 1) {
// for (var i = 1; i < pointArr[0].length; i++) {
// this.ctxSave.lineTo(pointArr[0][i].x, pointArr[0][i].y);
// this.ctxSave.fillStyle = "rgba(213,16,34,1)"; //填充颜色
// //ctxSave.fill();
// this.ctxSave.stroke(); //绘制
// }
for (var i = 0; i < pointArr.length; i++) {
this.ctxSave.moveTo(pointArr[i][0].x, pointArr[i][0].y);
for (var a = 1; a < pointArr[i].length; a++) {
this.ctxSave.lineTo(pointArr[i][a].x, pointArr[i][a].y);
this.ctxSave.fillStyle = "rgba(213,16,34,1)"; //填充颜色
//ctxSave.fill();
this.ctxSave.stroke(); //绘制
}
}
this.ctxSave.closePath();
}
},
saveCanvas() {
/*生成画布 结束绘画*/ //双缓冲
this.ctx.clearRect(0, 0, this.can.width, this.can.height);
this.ctxSave.closePath(); //结束路径状态,结束当前路径,如果是一个未封闭的图形,会自动将首尾相连封闭起来
this.ctxSave.fill(); //填充
this.ctxSave.stroke(); //绘制
this.hasSave.push(this.pointArr); //已经画好的储存一份,判断面面相交
this.pointArr = [];
},
makearc(ctx, x, y, r, s, e, color) {
//清空画布,画
this.ctx.clearRect(0, 0, 199, 202); //清空画布
this.ctx.beginPath();
this.ctx.fillStyle = color;
this.ctx.arc(x, y, r, s, e);
this.ctx.fill();
},
6线交叉判断
checkLineToLine(pointArr) {
//线交叉 判断 ,
var arr = [];
var edgeArr = [];
var mixArr = [];
var resultArr = [];
for (var i = 1; i < pointArr.length; i++) {
var arr = [];
arr.push(pointArr[i - 1], pointArr[i]);
edgeArr.push(arr);
}
for (var i = 0; i < edgeArr.length; i++) {
for (var j = 0; j < edgeArr.length; j++) {
if (i != j) {
mixArr.push([edgeArr[i], edgeArr[j]]);
}
}
}
for (let index = 0; index < mixArr.length; index++) {
var result = this.segmentsIntr(
mixArr[index][0][0],
mixArr[index][0][1],
mixArr[index][1][0],
mixArr[index][1][1]
);
resultArr.push(result);
}
if (resultArr.findIndex(target => target === true) == -1) {
return true;
} else {
return false;
}
},
7.线是否和面交叉
checkLineToSurface(lineArr) {
console.log("lineArr");
console.log(lineArr);
//只需检查最新画的一条线是否交叉 //线和已有面是否交叉
// console.log(this.hasSave.length);
// console.log(lineArr);
var resultArr = [];
if (this.hasSave.length == 0) {
} else {
for (var j = 0; j < this.hasSave.length; j++) {
var edgeArr = [];
for (var i = 1; i < this.hasSave[j].length; i++) {
var arr = [];
arr.push(this.hasSave[j][i - 1], this.hasSave[j][i]);
edgeArr.push(arr);
}
for (var i = 0; i < edgeArr.length; i++) {
// console.log(edgeArr[i]);
var result = this.segmentsIntr(
lineArr[0],
lineArr[1],
edgeArr[i][0],
edgeArr[i][1]
);
resultArr.push(result);
}
}
// for (var i = 1; i < this.hasSave[0].length; i++) {
// var arr = [];
// arr.push(this.hasSave[0][i - 1], this.hasSave[0][i]);
// edgeArr.push(arr);
// }
// console.log(edgeArr);
}
if (resultArr.findIndex(target => target === true) == -1) {
return true;
} else {
return false;
}
},
8.线是否重合
segmentsIntr(a, b, c, d) {
//线是否重合
var area_abc = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);
var area_abd = (a.x - d.x) * (b.y - d.y) - (a.y - d.y) * (b.x - d.x);
// 面积符号相同则两点在线段同侧,不相交 (对点在线段上的情况,当作不相交处理);
if (area_abc * area_abd >= 0) {
return false;
}
// 三角形cda 面积的2倍
var area_cda = (c.x - a.x) * (d.y - a.y) - (c.y - a.y) * (d.x - a.x);
// 三角形cdb 面积的2倍
var area_cdb = area_cda + area_abc - area_abd;
if (area_cda * area_cdb >= 0) {
return false;
}
//计算交点坐标
var t = area_cda / (area_abd - area_abc);
var dx = t * (b.x - a.x),
dy = t * (b.y - a.y);
// { x: a.x + dx, y: a.y + dy }
return true;
},
9.面是否覆盖
checkCover() {
//检查面是否覆盖
var dot = [];
var resultArr = [];
console.log(this.hasSave.length);
for (let i = 0; i < this.hasSave.length; i++) {
for (let j = 0; j < this.hasSave[i].length; j++) {
dot.push(this.hasSave[i][j]);
}
}
for (let i = 0; i < dot.length; i++) {
resultArr.push(this.judge(dot[i], this.pointArr));
}
if (resultArr.findIndex(target => target === true) == -1) {
return false;
} else {
return true;
}
},
10.是否点击到面内
judge(dot, coordinates) {
//点击面,点是否在面内
var x = dot.x,
y = dot.y;
var crossNum = 0;
for (var i = 0; i < coordinates.length - 1; i++) {
var start = coordinates[i];
var end = coordinates[i + 1];
// 起点、终点斜率不存在的情况
if (start.x === end.x) {
// 因为射线向右水平,此处说明不相交
if (x > start.x) continue;
if (
(end.y > start.y && y >= start.y && y <= end.y) ||
(end.y < start.y && y >= end.y && y <= start.y)
) {
crossNum++;
}
continue;
}
// 斜率存在的情况,计算斜率
var k = (end.y - start.y) / (end.x - start.x);
// 交点的x坐标
var x0 = (y - start.y) / k + start.x;
// 因为射线向右水平,此处说明不相交
if (x > x0) continue;
if (
(end.x > start.x && x0 >= start.x && x0 <= end.x) ||
(end.x < start.x && x0 >= end.x && x0 <= start.x)
) {
crossNum++;
}
}
return crossNum % 2 === 1;
}
结语:这是功能开发之前自己写的小demo。实际开发还要根据canvas大小和实际图纸大小比例的坐标转化。核心功能就这么多。虽然不复杂,但是没做过的筒子们肯定摸不着头脑。给大家研究研究。demo比较丑注释很详细,凑合看