vue canvas 区域绘制有绘制多边形,包括拖拽、重置、撤销、判断线段重合、判断点是否在多边形内
先上效果图(gif 图, 应该是可以看到的吧)对了,解释一下,我这个里面多排序的部分,是因为划线你肯定要有顺序,因为我从接口拿到的顺序是打乱的,所以我按照升序 整理了一下。现在绘制的下面之所以是白色的,是因为录gif的时候 我监控没打开,所以监控没显示出来,用图片也是可以的。我是一个白菜,里面还是有部分搞不太懂,建议可以看一下参考文献第一个链接,问问大佬,然后我就直接上代码了,我写了大量的注释,要
·
先上效果图
(gif 图, 应该是可以看到的吧)
忘记解释了,
现在绘制的下面之所以是白色的,是因为录gif的时候 我监控没打开,所以监控没显示出来,用图片也是可以的
层级是这样的,
然后我就直接上代码了,我写了大量的注释,要是看不懂的,我再解释
我是一个白菜,里面还是有部分搞不太懂,建议可以看一下参考文献第一个链接,问问大佬,
这个是DialogAreaDash.vue的
<template>
<div>
<el-dialog
title="区域划线"
:close-on-click-modal="false"
:visible.sync="dialogFormVisible"
:width="D_DIALOG_SIZE_BIG"
>
<div
style="
width: 100%;
height: 570px;
display: flex;
justify-content: space-around;
"
>
<div class="operation" style="width: 400px">
<div class="intrusion">
<div style="line-height: 30px">入侵区域数量</div>
<el-select
v-model="intrusion_num"
@change="intrusionNumChange"
placeholder="请选择"
>
<el-option
v-for="item in intrusion_options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</div>
<div
class="intrusion_item"
v-show="operation_arr.length != 0"
v-for="(item, index) in operation_arr"
:key="index"
>
<div class="item_title">{{ "入侵区域" + item.order_num }}</div>
<div class="item_row">
<span style="line-height: 35px">入侵时间(秒)</span>
<div class="block">
<el-slider v-model="item.timeThreshold" :max="10" show-input>
</el-slider>
</div>
</div>
<div class="item_row">
<span style="line-height: 35px">灵敏度</span>
<div class="block">
<el-slider v-model="item.sensitivityLevel" show-input>
</el-slider>
</div>
</div>
</div>
</div>
<div class="areaDash">
<canvasDraw
ref="canvasDrawRef"
style="height: 100%; width: 100%"
@finish="handleDrawFinish"
:single="true"
:form="form"
:limit="4"
/>
<div class="dialog-footer" style="margin-top: 10px">
<el-select
v-model="Draw_item"
@change="DrawNumChange"
placeholder="请选择"
class="DrawStyle"
value-key="order_num"
>
<el-option
v-for="item in operation_arr"
:key="item.order_num"
:label="'入侵区域' + item.order_num"
:value="item"
>
</el-option>
</el-select>
<el-button plain v-show="isopen" @click="DrawArea(false)"
>绘制区域</el-button
>
<el-button plain v-show="!isopen" @click="DrawArea(true)"
>取消</el-button
>
<el-button plain v-show="!isopen" @click="resetForm"
>重 置</el-button
>
<el-button type="primary" v-show="!isopen" @click="submitForm"
>确 定</el-button
>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="configRegion">配置区域围栏</el-button>
<el-button @click="dialogFormVisible = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import config from "../config";
import dialogControlorMixin from "@vcom/mixins/dialogControlorMixin";
import entMixin from "@vcom/mixins/entMixin";
import canvasDraw from "./components/index.vue";
export default {
name: "DialogAreaDash",
mixins: [dialogControlorMixin, entMixin],
components: { canvasDraw },
data() {
return {
config: config,
form: {},
formLabelWidth: "120px",
intrusion_num: 0,
intrusion_options: [
{ label: 1, value: 1 },
{ label: 2, value: 2 },
{ label: 3, value: 3 },
{ label: 4, value: 4 },
],
operation_arr: [],
isopen: true, // 为true时画板不可点击
Draw_item: {}, // 当前选中的绘画区域
endPoint: [], //绘画结束时的点位
};
},
methods: {
// 注册表单数据
async init() {
if (this.$refs["canvasDrawRef"]) {
this.$refs["canvasDrawRef"].reset();
this.$Bus.$emit("CHANGE_OPEN", true);
}
// 不可直接关联store
this.form = this._.cloneDeep(
this.$store.state[config.MODULE_NAME].dialogFormData
);
// 发起请求
const res = await this.$api.ballVideo2.queryFences({
device_uuid: this.form.uuid,
});
if (res.data && res.data.length != 0) {
let allData = res.data;
allData.sort(function (a, b) {
// a - b 是升序
return a.order_num - b.order_num;
});
for (let i = 0; i < allData.length; i++) {
if (allData[allData.length - 1 - i].is_enabled == 1) {
// 倒序表明应用了几个
for (let j = 0; j < allData.length - i; j++) {
this.operation_arr.push({
timeThreshold: allData[j].timeThreshold,
sensitivityLevel: allData[j].sensitivityLevel,
order_num: allData[j].order_num,
is_enabled: allData[j].is_enabled,
fenceDetailDTOS:allData[j].fenceDetailDTOS.map(e=>{
return {
order_num: e.order_num,
x: ( +e.x)/2,
y: ( +e.y)/2,
}
})
});
}
this.operation_arr.forEach((item) => {
item.is_enabled == 1;
item.fenceDetailDTOS.sort(function (a, b) {
// a - b 是升序
return a.order_num - b.order_num;
});
this.$Bus.$emit("START_DRAWING", item);
});
this.intrusion_num = allData.length - i;
break;
}
}
}
},
DrawArea(e) {
// 绘制区域
if (e == false) {
if (!(this.Draw_item && this.Draw_item.order_num)) {
this.$alert("请选择入侵区域", "提示", {
confirmButtonText: "确定",
});
return;
}
this.endPoint = this.Draw_item.fenceDetailDTOS;
}
this.isopen = e;
this.$Bus.$emit("CHANGE_OPEN", e);
// 取消
if (e == true) {
this.$refs["canvasDrawRef"].reset();
this.$Bus.$emit("CHANGE_OPEN", e);
this.$Bus.$emit("START_DRAWING", this.Draw_item);
}
},
// 重置
resetForm() {
this.$refs["canvasDrawRef"].reset();
},
// 确定
submitForm() {
console.log(this.endPoint);
this.isopen = true;
this.$Bus.$emit("CHANGE_OPEN", true);
this.endPoint.forEach((ele, index) => {
ele.order_num = index + 1;
});
this.operation_arr[this.Draw_item.order_num - 1].fenceDetailDTOS =
this.endPoint;
this.$Bus.$emit(
"START_DRAWING",
this.operation_arr[this.Draw_item.order_num - 1]
);
},
// 最后的确定,配置区域围栏
async configRegion() {
if (this.operation_arr.length == 0) {
this.$alert("至少配置一个区域", "提示", {
confirmButtonText: "确定",
});
return;
}
this.operation_arr.forEach((item) => {
item.fenceDetailDTOS.forEach((ele) => {
ele.x = ele.x * 2;
ele.y = ele.y * 2;
});
});
console.log(this.operation_arr);
let sendData = {
device_uuid: this.form.uuid,
fenceDTOS: this.operation_arr,
};
// 发起请求
const res = await this.$api.ballVideo2.configFences(sendData);
this.dialogFormVisible = false;
},
// 绘画最后一笔结束时的数据
handleDrawFinish(e) {
console.log(this.endPoint);
this.endPoint = e;
},
// 选中 绘画的入侵区域 的改变
DrawNumChange(item) {
if (item) {
let drawArr = item;
console.log(drawArr);
this.$refs["canvasDrawRef"].reset();
this.endPoint = [];
this.isopen = true;
this.$Bus.$emit("CHANGE_OPEN", true);
if (drawArr.fenceDetailDTOS && drawArr.fenceDetailDTOS.length != 0) {
// drawArr.fenceDetailDTOS.sort(function (a, b) {
// // a - b 是升序
// return a.order_num - b.order_num;
// });
this.$Bus.$emit("START_DRAWING", drawArr);
}
}
},
// 入侵区域数量的改动
intrusionNumChange(num) {
if (this.operation_arr.length >= num) {
this.operation_arr.length = num;
} else {
for (let i = this.operation_arr.length; i < num; i++) {
this.operation_arr.push({
order_num: +i + 1,
is_enabled: 1,
timeThreshold: 1,
sensitivityLevel: 100,
fenceDetailDTOS: [
{ order_num: 1, x: 375.5, y: 48.703125 },
{ order_num: 2, x: 376.5, y: 353.703125 },
{ order_num: 3, x: 54.5, y: 351.703125 },
{ order_num: 4, x: 53.5, y: 49.703125 },
],
});
}
}
this.$refs["canvasDrawRef"].reset();
this.$Bus.$emit("CHANGE_OPEN", true);
for (let i = 0; i < this.operation_arr.length; i++) {
this.$Bus.$emit("START_DRAWING", this.operation_arr[i]);
}
this.Draw_item = {}
},
// 页面销毁
copDestroy() {
this.isopen = true;
this.operation_arr = [];
this.Draw_item = {};
this.intrusion_num = 0;
},
},
beforeDestroy() {
this.$Bus.$off("CHANGE_OPEN");
this.$Bus.$off("START_DRAWING");
},
};
</script>
<style>
.intrusion {
display: flex;
justify-content: space-between;
}
.item_title {
background-color: aliceblue;
margin-top: 10px;
}
.item_row {
display: flex;
justify-content: space-between;
}
.block .show-input {
width: 55% !important;
margin-right: 250px !important;
}
.block .el-slider__input {
width: 100px !important;
}
.block .el-slider__input .el-input {
width: 100% !important;
}
.areaDash {
width: 500px;
height: 500px;
}
.DrawStyle {
width: 150px !important;
}
</style>
对了,解释一下,我这个里面多排序的部分,是因为划线你肯定要有顺序,因为我从接口拿到的顺序是打乱的,所以我按照升序 整理了一下。
index的
<template>
<div>
<div class="canvas-box solid" ref="canvasBox">
<!-- <img
src="@/assets/bg1.png"
style="
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
object-fit: fill;
"
/> -->
<video
v-if="videoUrl"
style="
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
object-fit: fill;
"
:src="videoUrl"
:autoplay="true"
></video>
<!-- :controls="true" -->
<!--用来和鼠标进行交互操作的canvas-->
<canvas ref="canvas"></canvas>
<!--存储已生成的点线,避免被清空-->
<canvas
ref="canvasSave"
@click="handleCanvasSaveClick"
@mousemove="handleCanvasSaveMove"
@mouseup="handleCanvasMouseUp"
@mousedown="handleCanvasMouseDown"
></canvas>
<div
style="width: 100%; height: 100%; position: absolute; top: 0; left: 0"
v-show="open && single"
></div>
</div>
</div>
</template>
<script>
import FlvCop from "@/viewsCommon/components/FlvCop.vue";
export default {
name: "DialogAreaDashIndex",
data() {
return {
coincidentDistance: 10, // 重合距离
oIndex: -1, //判断鼠标是否移动到起始点处,-1为否,1为是
pointArr: [], //存放坐标的数组
MovePointArr: [], //存放 拖拽移动时 坐标的数组
pointX: "",
pointY: "",
ctxSave: "",
canSave: "",
ctx: "",
can: "",
lineColor: "rgba(64, 158, 255, 1)",
open: true,
completedDraw: false, // 是否已经完成了绘图
clickMove: false, //表明 正在拖拽的状态
videoUrl: "",
};
},
components: { FlvCop },
props: {
// background:{
// type: [String],
// default: "",
// },
single: {
type: [Boolean],
default: true,
},
form: {
type: [Object],
default: {},
},
limit: {
type: [Number],
default: 4,
},
},
watch: {
form: {
handler: function () {
this.getVideo();
},
deep: true,
},
},
beforeMount() {
this.$Bus.$on("CHANGE_OPEN", (e) => {
this.open = e;
if (e == false) {
// 开始绘制
this.completedDraw = true;
}
});
this.$Bus.$on("START_DRAWING", (area) => {
this.ctx.beginPath();
this.ctx.moveTo(area.fenceDetailDTOS[0].x, area.fenceDetailDTOS[0].y);
for (var i = 1; i < area.fenceDetailDTOS.length; i++) {
this.ctx.lineTo(area.fenceDetailDTOS[i].x, area.fenceDetailDTOS[i].y);
}
this.ctx.lineTo(area.fenceDetailDTOS[0].x, area.fenceDetailDTOS[0].y);
this.ctx.fillStyle = "rgba(161,195,255,1)"; //填充颜色
this.ctx.fill(); //填充
this.ctx.stroke(); //绘制
this.ctx.font = "20px 宋体";
this.ctx.fillStyle = "rgb(255,165,0)";
let heavyHeart = this.centerPoint(area.fenceDetailDTOS);
// y向下为正
this.ctx.fillText(
"入侵区域 " + area.order_num,
heavyHeart.x - 40,
heavyHeart.y
// 随便找两个不相邻的点的中点好了
// (area.fenceDetailDTOS[0].x + area.fenceDetailDTOS[2].x) / 2,
// (area.fenceDetailDTOS[0].y + area.fenceDetailDTOS[2].y) / 2
);
this.pointArr = area.fenceDetailDTOS
});
},
methods: {
// 计算向量叉乘
crossMul(v1, v2) {
return v1.x * v2.y - v1.y * v2.x;
},
// 判断两条线段是否相交
checkCross(p1, p2, p3, p4) {
let v1 = { x: p1.x - p3.x, y: p1.y - p3.y },
v2 = { x: p2.x - p3.x, y: p2.y - p3.y },
v3 = { x: p4.x - p3.x, y: p4.y - p3.y },
v = this.crossMul(v1, v3) * this.crossMul(v2, v3);
v1 = { x: p3.x - p1.x, y: p3.y - p1.y };
v2 = { x: p4.x - p1.x, y: p4.y - p1.y };
v3 = { x: p2.x - p1.x, y: p2.y - p1.y };
return v <= 0 && this.crossMul(v1, v3) * this.crossMul(v2, v3) <= 0
? true
: false;
},
// 判断点是否在多边形内
checkPP(point, polygon) {
let p1, p2, p3, p4;
p1 = point;
p2 = { x: -100, y: point.y };
let count = 0;
// 对每条边都和射线作对比
for (let i = 0; i < polygon.length - 1; i++) {
p3 = polygon[i];
p4 = polygon[i + 1];
if (this.checkCross(p1, p2, p3, p4) == true) {
count++;
}
}
p3 = polygon[polygon.length - 1];
p4 = polygon[0];
if (this.checkCross(p1, p2, p3, p4) == true) {
count++;
}
// 如果为偶数 说明在多边形外面,如果为奇数,说明在多边形内部
return count % 2 == 0 ? false : true;
},
// 通过多边形的各点位获得重心
centerPoint(pointArr) {
let X = 0,
Y = 0;
for (let i = 0; i < pointArr.length; i++) {
X = X + +pointArr[i].x;
Y = Y + +pointArr[i].y;
}
X = X / pointArr.length;
Y = Y / pointArr.length;
return { x: X, y: Y };
},
// 监控
async getVideo() {
// 发起请求
// const res = await this.$api.ballVideo2.live({
// // device_uuid: "937adeb238944162b23961d0d216c962",
// device_uuid: this.form.uuid,
// });
// if (res && res.data) {
// this.videoUrl = res.data.fmp4;
// }
},
initCanvas() {
this.can = this.$refs["canvas"];
this.can.width = this.$refs["canvasBox"].clientWidth;
this.can.height = this.$refs["canvasBox"].clientHeight;
// console.log(can.width, can.height);
this.ctx = this.can.getContext("2d");
this.ctx.strokeStyle = this.lineColor; //线条颜色
this.ctx.fillStyle = "rgba(64, 158, 255,0.1)"; //填充颜色
this.ctx.lineWidth = 4; //线条粗细
this.ctx.globalAlpha = 0.7; //半透明
this.canSave = this.$refs["canvasSave"];
this.canSave.width = this.$refs["canvasBox"].clientWidth;
this.canSave.height = this.$refs["canvasBox"].clientHeight;
this.ctxSave = this.canSave.getContext("2d");
this.ctxSave.strokeStyle = this.lineColor; //线条颜色
this.ctxSave.fillStyle = "rgba(64, 158, 255,0.1)"; //填充颜色
this.ctxSave.lineWidth = 4; //线条粗细
this.ctxSave.globalAlpha = 0.7; //半透明
// console.log(can);
// console.log(canSave);
},
handleCanvasMouseUp() {
if (this.clickMove) {
this.clickMove = false;
this.pointArr = this.MovePointArr;
this.$emit("finish", this.pointArr);
}
},
handleCanvasMouseDown(e) {
if (e && e.layerX) {
if (
this.completedDraw &&
this.checkPP(
{
x: e.offsetX == undefined ? e.layerX : e.offsetX,
y: e.offsetY == undefined ? e.layerY : e.offsetY,
},
this.pointArr
)
) {
this.clickMove = true;
this.pointX = e.offsetX;
this.pointY = e.offsetY;
}
}
},
handleCanvasSaveClick(e) {
if (this.completedDraw) {
return;
}
if (e.layerX) {
this.pointX = e.offsetX == undefined ? e.layerX : e.offsetX;
this.pointY = e.offsetY == undefined ? e.layerY : e.offsetY;
var piX, piY;
if (this.oIndex > 0 && this.pointArr.length > 0) {
piX = this.pointArr[0].x;
piY = this.pointArr[0].y;
//画点
this.makearc(
this.ctx,
piX,
piY,
this.GetRandomNum(2, 2),
0,
180,
this.lineColor
);
this.pointArr.push({ x: piX, y: piY });
if (this.isExistIntersection()) {
this.$alert("存在交叉线段,请重新绘制", "提示", {
confirmButtonText: "确定",
});
this.revoke();
return;
}
this.canvasSave(this.pointArr); //保存点线同步到另一个canvas
this.saveCanvas(); //生成画布
} else {
piX = this.pointX;
piY = this.pointY;
this.makearc(
this.ctx,
piX,
piY,
this.GetRandomNum(2, 2),
0,
180,
this.lineColor
);
this.pointArr.push({ x: piX, y: piY });
if (this.isExistIntersection()) {
this.$alert("存在交叉线段,请重新绘制", "提示", {
confirmButtonText: "确定",
});
this.revoke();
return;
}
this.canvasSave(this.pointArr); //保存点线同步到另一个canvas
}
}
},
handleCanvasSaveMove(e) {
if (this.completedDraw) {
// 分成两个if是因为完成绘图后鼠标移动不产生线条
if (this.clickMove) {
let flag = true;
// 多边形不可出画板
for (let i = 0; i < this.pointArr.length; i++) {
if (
this.pointArr[i].x + e.offsetX - this.pointX >= this.can.width ||
this.pointArr[i].x + e.offsetX - this.pointX <= 0 ||
this.pointArr[i].y + e.offsetY - this.pointY >= this.can.height ||
this.pointArr[i].y + e.offsetY - this.pointY <= 0
) {
flag = false;
this.handleCanvasMouseUp()
}
}
if (flag == false) return;
// 表示点击之后移动
this.MovePointArr = this.pointArr.map((ele) => {
return {
x: ele.x + e.offsetX - this.pointX,
y: ele.y + e.offsetY - this.pointY,
};
});
// // 清空画布
this.ctx.clearRect(0, 0, this.can.width, this.can.height);
this.ctxSave.clearRect(0, 0, this.canSave.width, this.canSave.height);
// /*开始绘制*/
this.ctx.beginPath();
this.ctx.moveTo(this.MovePointArr[0].x, this.MovePointArr[0].y);
if (this.MovePointArr.length > 1) {
for (var i = 1; i < this.MovePointArr.length; i++) {
this.ctx.lineTo(this.MovePointArr[i].x, this.MovePointArr[i].y);
}
}
this.ctx.lineTo(this.MovePointArr[0].x, this.MovePointArr[0].y);
this.ctx.fillStyle = "rgba(161,195,255,1)"; //填充颜色
this.ctx.fill(); //填充
this.ctx.stroke(); //绘制
}
return;
}
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(4, 4),
0,
180,
this.lineColor
);
if (this.pointArr.length > 0) {
if (
this.pointX > this.pointArr[0].x - this.coincidentDistance &&
this.pointX < this.pointArr[0].x + this.coincidentDistance &&
this.pointY > this.pointArr[0].y - this.coincidentDistance &&
this.pointY < this.pointArr[0].y + this.coincidentDistance
) {
if (this.pointArr.length > 1) {
piX = this.pointArr[0].x;
piY = this.pointArr[0].y;
// console.log(piX, piY);
this.ctx.clearRect(0, 0, this.can.width, this.can.height);
this.makearc(
this.ctx,
piX,
piY,
this.GetRandomNum(4, 4),
0,
180,
this.lineColor
);
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(161,195,255,1)"; //填充颜色
this.ctx.fill(); //填充
this.ctx.stroke(); //绘制
}
}
},
// 存储已生成的点线
canvasSave(pointArr) {
this.ctxSave.clearRect(0, 0, this.ctxSave.width, this.ctxSave.height);
this.ctxSave.beginPath();
if (pointArr.length > 1) {
this.ctxSave.moveTo(pointArr[0].x, pointArr[0].y);
for (var i = 1; i < pointArr.length; i++) {
this.ctxSave.lineTo(pointArr[i].x, pointArr[i].y);
this.ctxSave.fillStyle = "rgba(161,195,255,1)"; //填充颜色
//ctxSave.fill();
this.ctxSave.stroke(); //绘制
}
this.ctxSave.closePath();
}
},
/*生成画布 结束绘画*/
saveCanvas() {
if (this.pointArr.length <= 3) {
this.$alert("检查区域至少为三边形,请重新绘制", "提示", {
confirmButtonText: "确定",
});
this.revoke();
return;
}
this.ctx.clearRect(0, 0, this.can.width, this.can.height);
this.ctxSave.closePath(); //结束路径状态,结束当前路径,如果是一个未封闭的图形,会自动将首尾相连封闭起来
this.ctxSave.fill(); //填充
this.ctxSave.stroke(); //绘制
this.completedDraw = true;
this.$emit("finish", this.pointArr.slice(1));
},
/*验证canvas画布是否为空函数*/
isCanvasBlank(canvas) {
var blank = document.createElement("canvas"); //创建一个空canvas对象
blank.width = canvas.width;
blank.height = canvas.height;
return canvas.toDataURL() == blank.toDataURL(); //为空 返回true
},
/*canvas生成圆点*/
GetRandomNum(Min, Max) {
var Range = Max - Min;
var Rand = Math.random();
return Min + Math.round(Rand * Range);
},
makearc(ctx, x, y, r, s, e, color) {
ctx.clearRect(0, 0, 199, 202); //清空画布
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x, y, r, s, e);
ctx.fill();
},
reset() {
this.ctx.clearRect(0, 0, this.can.width, this.can.height);
this.ctxSave.clearRect(0, 0, this.canSave.width, this.canSave.height);
this.pointArr = [];
this.completedDraw = false;
this.open = false;
},
// 撤销上一步
revoke() {
if (this.open && this.single) {
this.reset();
}
this.ctx.clearRect(0, 0, this.can.width, this.can.height);
this.ctxSave.clearRect(0, 0, this.canSave.width, this.canSave.height);
this.pointArr.pop();
this.pointArr.map((item) => {
let _e = {
layerX: item.x,
layerY: item.y,
};
this.handleCanvasSaveMove(_e);
});
},
isExistIntersection() {
let result = false;
for (let i = 0; i < this.pointArr.length - 1; i++) {
let a = this.pointArr[i];
let b = this.pointArr[i + 1];
for (let j = 0; j < this.pointArr.length - 1; j++) {
if (i == j) {
continue;
}
let c = this.pointArr[j];
let d = this.pointArr[j + 1];
result = this.isIntersection(a, b, c, d);
if (result) {
return result;
}
// console.log(result)
}
}
return result;
},
//判断两个线段是否相交
isIntersection(a, b, c, d) {
/** 1 解线性方程组, 求线段交点. **/
// 如果分母为0 则平行或共线, 不相交
var denominator = (b.y - a.y) * (d.x - c.x) - (a.x - b.x) * (c.y - d.y);
if (denominator == 0) {
return false;
}
// 线段所在直线的交点坐标 (x , y)
var x =
((b.x - a.x) * (d.x - c.x) * (c.y - a.y) +
(b.y - a.y) * (d.x - c.x) * a.x -
(d.y - c.y) * (b.x - a.x) * c.x) /
denominator;
var y =
-(
(b.y - a.y) * (d.y - c.y) * (c.x - a.x) +
(b.x - a.x) * (d.y - c.y) * a.y -
(d.x - c.x) * (b.y - a.y) * c.y
) / denominator;
/** 2 判断交点是否在两条线段上 **/
if (
// 交点在线段1上
(x - a.x) * (x - b.x) < 0 &&
(y - a.y) * (y - b.y) < 0 &&
// 且交点也在线段2上
(x - c.x) * (x - d.x) < 0 &&
(y - c.y) * (y - d.y) < 0
) {
// // 返回交点p
// return {
// x: x,
// y: y,
// };
return true;
}
//否则不相交
return false;
},
},
async mounted() {
this.initCanvas();
this.getVideo();
},
beforeDestroy() {
this.$Bus.$off("CHANGE_OPEN");
this.$Bus.$off("START_DRAWING");
this.videoUrl = "";
},
};
</script>
<style lang="scss" scoped>
.canvas-box {
position: relative;
width: 100%;
height: 100%;
> canvas {
position: absolute;
top: 0;
left: 0;
// width: 1000px;
// height: 800px;
width: 100%;
height: 100%;
}
}
</style>
参考文献:
Vue3 区域绘制,有撤销、重置和判断线段重合功能_瞎跑的uice的博客-CSDN博客
这篇文章,我是综合了文献里的各种方法,以及自己修改 添加一部分
感觉这篇文章不太好,侵权啥的,私信我,立刻删
更多推荐
已为社区贡献1条内容
所有评论(0)