组件
<template>
<div
style="display: flex; height: 342px; width: 760px; border: 1px solid #000"
>
<div
style="position: relative; height: 100%; width: 608px; min-width: 608px"
>
<canvas
id="mycanvas"
ref="mycanvas"
:width="canvasWidth"
:height="canvasHeight"
@mousedown="canvasDown($event)"
@mousemove="canvasMove($event)"
@mouseup="canvasUp($event)"
@dblclick="doubleclick()"
></canvas>
</div>
<div
style="
display: flex;
flex-direction: column;
padding: 10px;
border: 1px solid #cbd2f7;
"
>
<el-button
size="mini"
style="margin-left: 0; margin-top: 10px"
@click="beginDraw = !beginDraw"
:type="beginDraw == false ? 'primary' : ''"
>{{ beginDraw ? "结束绘制" : "开始绘制" }}</el-button
>
<el-button
size="mini"
@click="clearAll"
style="margin-left: 0; margin-top: 10px"
>清空绘制区域</el-button
>
<div>
<el-select
size="mini"
placeholder="请选择绘制类型"
v-model="roiType"
@change="clearAll"
style="margin-top: 10px"
>
<el-option label="区域" value="1" v-if="myRoiType == 1"></el-option>
<el-option label="线条" value="2" v-if="myRoiType == 2"></el-option>
<el-option
label="单区域+单向单拌线"
value="3"
v-if="myRoiType == 3"
></el-option>
</el-select>
<el-select
size="mini"
placeholder="请选择线条方向类型"
v-model="directionType"
v-show="roiType != '1'"
:disabled="directionTypeDisabled"
style="margin-top: 10px"
@change="clearAll"
>
<el-option
v-for="item in directionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<div style="display: flex">
<el-button
style="width: 50%; margin-top: 10px"
size="mini"
:type="drawType == 1 ? 'primary' : ''"
@click="drawType = 1"
v-show="roiType == 3"
>区域</el-button
>
<el-button
size="mini"
:type="drawType == 2 ? 'primary' : ''"
@click="drawType = 2"
v-show="roiType == 3"
style="width: 50%; margin-top: 10px"
>直线</el-button
>
</div>
<!-- </el-button-group> -->
</div>
<div class="staticLabels">
<div
class="staticLabels_item"
v-for="(item, index) in allDrawList"
@mouseenter="canvasMouseenter(item)"
@mouseleave="canvasMouseleave()"
v-if="(index >= 0 && !isMultiLine) || (index > 0 && isMultiLine)"
>
<span class="staticLabels_item_span">
{{ item.areaName }}
</span>
<i
class="el-icon-close"
@click="canvasDeleteOne(item)"
style="cursor: pointer"
title="删除"
></i>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
canvasKey: {
type: String,
default: () => "first",
},
// 画图数据
myDrawList: {
type: Array,
default: () => [],
},
myRoiType: {
type: String,
default: () => "1",
},
},
data() {
return {
isMultiLine: false, //是否多拌线
localMyDrawList: [],
finalArr: [],
roiType: "1",
roiTypeOptions: [
{
value: "1",
label: "区域",
},
{
value: "2",
label: "线条",
},
{
value: "3",
label: "单区域+单向单拌线",
},
],
directionTypeDisabled: false,
directionType: "1",
directionTypeOptions: [
{
value: "1",
label: "单向单拌线",
},
{
value: "2",
label: "双向单拌线",
},
{
value: "3",
label: "单向多拌线",
},
{
value: "4",
label: "双向多拌线",
},
],
//
canvasId: 1,
drawType: "1", // 绘制类型 1-多边形 2-线条
arrowType: "1", // 箭头类型 1-单向 2-双向
all_line_coordinates: [
// {
// canvasId: 1,
// areaName: '直线1',
// directionType:1,
// points_coordinates: [{ cor_x: 100, cor_y: 100 }],
// }
], //所有线条的信息
MaxAreaPointsNum: 10, //多边形的最大顶点数
MaxLinePointsNum: 5, //线条的最大顶点数
MaxAreaNum: 10, //多边形的最大数量
MaxLineNum: 10, //线条的最大数量
//
isdraw: false, //是否在画图形
ctx: null, //canvas对象
coordinates: [], //当前图形的坐标信息
all_coordinates: [
// {
// canvasId: 1,
// areaName: '区域1',
// points_coordinates: [{ cor_x: 100, cor_y: 100 }],
// },
], //所有多边形的信息
isdrag: false, //是否拖拽点
isdragType: 1, //拖拽类型 1-拖动多边形 2-拖动线条
drag_index: [-1, -1], // 拖拽索引
beginDraw: false, //开始作画
colorList: [
// "rgba(88,87,86,.4)",
// "rgba(252,230,202,.4)",
// "rgba(0,199,140,.4)",
// "rgba(227,23,13,.4)",
// "rgba(153,51,250,.4)",
// "rgba(199,97,20,.4)",
// "rgba(250,240,230,.4)",
// "rgba(188,143,143,.4)",
// "rgba(0,255,0,.4)",
// "rgba(244,164,95,.4)",
// "rgba(128,42,42,.4)",
// "rgba(64,224,205,.4)",
// "rgba(237,145,33,.4)",
// "rgba(34,139,34,.4)",
// "rgba(255,125,64,.4)",
// "rgba(107,142,35,.4)",
// "rgba(227,207,87,.4)",
// "rgba(3,168,158,.4)",
// "rgba(255,255,255,.4)",
// "rgba(255,255,0,.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(16, 128, 219, 0.4)",
"rgba(255,0,0,.6)", //上色
],
canvasWidth: 608, //画布宽度
canvasHeight: 342, //画布高度
//
cor_index: 0, //当前多边形的索引 未开发
isFirst: true,
};
},
watch: {
canvasKey: {
handler(val) {
this.clearAll();
this.showMyDrawList();
},
deep: true,
immediate: true,
},
myRoiType: {
handler(val) {
if (val) {
this.clearAll();
this.roiType = val;
// this.showMyDrawList();
}
},
// deep:true,
immediate: true,
},
// 区域类型:1-区域;2-线条;3-区域+单向线条
roiType: {
handler(val) {
if (val) {
this.MaxAreaNum = 10;
this.MaxLineNum = 10;
this.directionTypeDisabled = false;
if (val == 1) {
this.drawType = 1;
} else if (val == 2) {
this.drawType = 2;
this.MaxLineNum = 1;
}
// 3-单区域+单向线条
else if (val == 3) {
this.MaxAreaNum = 1;
this.MaxLineNum = 1;
this.arrowType = 1;
this.directionType = "1";
this.directionTypeDisabled = true;
}
}
this.handleMultiLine();
},
deep: true,
immediate: true,
},
//线条方向类型:1-单向单拌线,2-双向单拌线,3-单向多拌线,4-双向多拌线
directionType: {
handler(val) {
if (val) {
// 1-单向单拌线
if (val == 1) {
this.arrowType = 1;
this.MaxLineNum = 1;
}
// 2-双向单拌线
else if (val == 2) {
this.arrowType = 2;
this.MaxLineNum = 1;
}
// 3-单向多拌线
else if (val == 3) {
this.arrowType = 1;
this.MaxLineNum = 2;
}
// 4-双向多拌线
else if (val == 4) {
this.arrowType = 2;
this.MaxLineNum = 2;
}
}
this.handleMultiLine();
},
deep: true,
// immediate: true,
},
allDrawList: {
handler(val) {
// 处理数据-------------------------------------------------
var guardAreas = [];
val &&
val.length > 0 &&
val.forEach((item, index) => {
var areaName = item.areaName;
// 线段
if (item.directionType) {
var directionType = item.directionType;
var linePoints = [];
item.points_coordinates &&
item.points_coordinates.length > 0 &&
item.points_coordinates.forEach((item2, index2) => {
linePoints.push({
x: (item2.cor_x / this.canvasWidth).toFixed(4),
y: (item2.cor_y / this.canvasHeight).toFixed(4),
});
});
var obj = {
areaName: areaName,
directionType: directionType,
linePoints: linePoints,
};
guardAreas.push(obj);
}
// 区域
else {
var directionType = item.directionType;
var points = [];
item.points_coordinates &&
item.points_coordinates.length > 0 &&
item.points_coordinates.forEach((item2, index2) => {
points.push({
x: (item2.cor_x / this.canvasWidth).toFixed(4),
y: (item2.cor_y / this.canvasHeight).toFixed(4),
});
});
// 闭合区域 补起点
if (
points[0].x !== points[points.length - 1].x ||
points[0].y !== points[points.length - 1].y
) {
points.push({
x: points[0].x,
y: points[0].y,
});
}
var obj = {
areaName: areaName,
points: points,
};
guardAreas.push(obj);
}
});
var finalObj = {
videoCode: this.canvasKey,
guardAreas: guardAreas,
};
// console.log('回传数据',finalObj);
var myDrawList = JSON.parse(JSON.stringify(this.localMyDrawList));
var index = myDrawList.findIndex(
(v) => v.videoCode == finalObj.videoCode
);
if (index != -1) {
myDrawList.splice(index, 1, finalObj);
} else {
myDrawList.push(finalObj);
}
// this.$emit("drawListChange", myDrawList);
// console.log("数据回传", myDrawList);
this.finalArr = [].concat(myDrawList);
this.localMyDrawList = [].concat(myDrawList);
// 处理数据----------------------------------------------
if (val.length == 0) {
this.canvasId = 1;
}
},
deep: true,
immediate: true,
},
myDrawList: {
handler(val) {
this.clearAll();
if (val) {
this.localMyDrawList = [].concat(val);
this.showMyDrawList();
}
},
deep: true,
immediate: true,
},
MaxLineNum: {
handler(val) {
this.handleMultiLine();
},
// deep: true,
immediate: true,
},
},
computed: {
allDrawList: {
get() {
return this.all_coordinates.concat(this.all_line_coordinates);
},
},
},
mounted() {
this.initDraw();
document.getElementById("mycanvas").oncontextmenu = function (e) {
e.preventDefault(); //阻止默认右键菜单
};
},
methods: {
handleMultiLine() {
if (this.MaxLineNum == 2 && this.roiType == 2) {
this.isMultiLine = true;
} else {
this.isMultiLine = false;
}
},
// 回显
showMyDrawList() {
var val = this.localMyDrawList || [];
if (this.canvasKey) {
// console.log("画图回显 localMyDrawList", val);
val.forEach((itemOut, index) => {
if (itemOut.videoCode == this.canvasKey) {
// console.log("当前", itemOut, itemOut.guardAreas);
itemOut.guardAreas &&
itemOut.guardAreas.length &&
itemOut.guardAreas.forEach((item, index) => {
// 线段
if (item.directionType) {
this.directionType = item.directionType;
// console.log("回显切换", item.directionType);
if (item.directionType == 1 || item.directionType == 3)
this.arrowType = 1;
else this.arrowType = 2;
var areaName = item.areaName;
var directionType = item.directionType;
var canvasId = Number(item.areaName.substring(2));
var points_coordinates = [];
item.linePoints.forEach((points, index) => {
// 回显的时候不需要最后一个点,因为他跟第一个点重合
if (
index != 0 &&
points.x == item.linePoints[0].x &&
points.y == item.linePoints[0].y
) {
return;
}
points_coordinates.push({
// cor_x: (points.x * this.canvasWidth).toFixed(0),
// cor_y: (points.y * this.canvasHeight).toFixed(0),
cor_x: Math.ceil(points.x * this.canvasWidth),
cor_y: Math.ceil(points.y * this.canvasHeight),
});
});
var obj = {
canvasId: canvasId,
areaName: areaName,
directionType: directionType,
points_coordinates: points_coordinates,
};
this.canvasId = ++canvasId;
this.all_line_coordinates.push(obj);
}
// 区域
else {
var areaName = item.areaName;
var canvasId = Number(item.areaName.substring(2));
var points_coordinates = [];
item.points.forEach((points, index) => {
// 回显的时候不需要最后一个点,因为他跟第一个点重合
if (
index != 0 &&
points.x == item.points[0].x &&
points.y == item.points[0].y
) {
return;
}
points_coordinates.push({
// cor_x: (points.x * this.canvasWidth).toFixed(0),
// cor_y: (points.y * this.canvasHeight).toFixed(0),
cor_x: Math.ceil(points.x * this.canvasWidth),
cor_y: Math.ceil(points.y * this.canvasHeight),
});
});
var obj = {
canvasId: canvasId,
areaName: areaName,
points_coordinates: points_coordinates,
};
this.canvasId = ++canvasId;
this.all_coordinates.push(obj);
}
});
this.$nextTick(() => {
// console.log("回显函数");
this.drawAll();
});
}
});
}
},
getData() {
return this.finalArr;
},
// 删除一个图形
canvasDeleteOne(data) {
if (this.isMultiLine) {
this.all_line_coordinates = [];
this.drawAll();
} else {
if (data.directionType) {
var index = this.all_line_coordinates.findIndex(
(v) => v.canvasId == data.canvasId
);
if (index != -1) this.all_line_coordinates.splice(index, 1);
} else {
var index = this.all_coordinates.findIndex(
(v) => v.canvasId == data.canvasId
);
if (index != -1) this.all_coordinates.splice(index, 1);
}
this.drawAll();
}
},
// 鼠标悬浮,给指定图形上色
canvasMouseenter(data) {
this.drawAll(data);
},
// 离开取消上色
canvasMouseleave() {
this.drawAll();
},
// 绘制所有图形
drawAll(data) {
// console.log(this.all_coordinates, this.all_line_coordinates);
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
if (
this.all_coordinates.length != 0 ||
this.all_line_coordinates.length != 0
) {
this.drawlines(data);
this.drawcircles();
this.fillarea(data);
}
},
// ----------------------------------------------------------
// 显示箭头
showArrow(x1, y1, x2, y2) {
var xm = (x1 + x2) / 2;
var ym = (y1 + y2) / 2;
var d = 30; //箭头长度 距离
if (this.arrowType == 1) {
var { x, y } = this.getArrowPoint(x1, y1, x2, y2, d);
this.drawArrow(this.ctx, xm, ym, x, y, 30, 10, 1, "#f36");
} else {
var { x3, y3, x4, y4 } = this.getArrowPoint(x1, y1, x2, y2, d);
this.drawArrow(this.ctx, xm, ym, x3, y3, 30, 10, 1, "#f36");
this.drawArrow(this.ctx, xm, ym, x4, y4, 30, 10, 1, "#f36");
}
},
// 获取箭头坐标点
getArrowPoint(x1, y1, x2, y2, d) {
var x0 = (x1 + x2) / 2;
var y0 = (y1 + y2) / 2;
var k1 = (y2 - y1) / (x2 - x1);
var k2 = -1 / k1;
var num = Math.sqrt(d ** 2 / (1 + k2 ** 2));
var x3 = num + x0;
var y3 = k2 * (x3 - x0) + y0;
var x4 = 0 - num + x0;
var y4 = k2 * (x4 - x0) + y0;
// 平行于x轴
if (y2 == y1) {
x3 = x0;
x4 = x0;
y3 = y0 + d;
y4 = y0 - d;
}
// 平行于y轴
else if (x2 == x1) {
x3 = x0 + d;
x4 = x0 - d;
y3 = y0;
y4 = y0;
}
x3 = Math.floor(x3);
y3 = Math.floor(y3);
x4 = Math.floor(x4);
y4 = Math.floor(y4);
var x, y;
// 返回向上
if (x2 > x1) {
y = y3 < y0 ? y3 : y4;
x = y == y3 ? x3 : x4;
}
// 返回向下
else {
y = y3 < y0 ? y4 : y3;
x = y == y3 ? x3 : x4;
}
if (this.arrowType == 1) {
return { x, y };
} else {
return { x3, y3, x4, y4 };
}
},
// ----------------------------------------------------------
/**
* @param dot {{x,y}} 需要判断的点
* @param coordinates {{x,y}[]} 多边形点坐标的数组,为保证图形能够闭合,起点和终点必须相等。
* 比如三角形需要四个点表示,第一个点和最后一个点必须相同。
*/
// 判断点是否点击图形
judge(dot, coordinates) {
var x = dot.x,
y = dot.y;
var crossNum = 0;
// 点在线段的左侧数目
var leftCount = 0;
// 点在线段的右侧数目
var rightCount = 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) {
leftCount++;
crossNum++;
}
// 从右侧贯穿
if (end.y < start.y && y >= end.y && y <= start.y) {
rightCount++;
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) {
crossNum++;
if (k >= 0) leftCount++;
else rightCount++;
}
if (end.x < start.x && x0 >= end.x && x0 <= start.x) {
crossNum++;
if (k >= 0) rightCount++;
else leftCount++;
}
}
return leftCount - rightCount !== 0;
},
// 判断点是否在直线上
judgeLine(x1, y1, x2, y2, x, y) {
var crossProduct = (x2 - x1) * (y - y1) - (y2 - y1) * (x - x1);
// 如果不等于0,说明不共线,直接返回false
if (crossProduct !== 0) {
return false;
}
// 否则,检查c点是否在ab线段的范围内
return (
Math.min(x1, x2) <= x &&
x <= Math.max(x1, x2) &&
Math.min(y1, y2) <= y &&
y <= Math.max(y1, y2)
);
},
initDraw() {
//初始化画布对象
const canvas = document.querySelector("#mycanvas");
this.ctx = canvas.getContext("2d");
// this.ctx.strokeStyle = "rgb(0, 195, 155)";
this.ctx.strokeStyle = "#f36";
},
clearAll() {
console.log("clearAll");
this.all_coordinates = [];
this.all_line_coordinates = [];
this.coordinates = [];
this.isdraw = false;
this.canvasId = 1;
this.$nextTick(() => {
this.ctx &&
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
});
},
// 判断是否在拖拽点
isdragpoint(x, y) {
if (
this.all_coordinates.length == 0 &&
this.all_line_coordinates.length == 0
) {
return false;
}
for (var i = 0; i < this.all_coordinates.length; i++) {
for (
var j = 0;
j < this.all_coordinates[i].points_coordinates.length;
j++
) {
var px = this.all_coordinates[i].points_coordinates[j].cor_x;
var py = this.all_coordinates[i].points_coordinates[j].cor_y;
// 允许偏移量5
if (Math.abs(x - px) <= 5 && Math.abs(y - py) <= 5) {
this.drag_index[0] = i;
this.drag_index[1] = j;
this.isdragType = 1;
return true;
}
}
}
for (var i = 0; i < this.all_line_coordinates.length; i++) {
for (
var j = 0;
j < this.all_line_coordinates[i].points_coordinates.length;
j++
) {
var px = this.all_line_coordinates[i].points_coordinates[j].cor_x;
var py = this.all_line_coordinates[i].points_coordinates[j].cor_y;
// 允许偏移量5
if (Math.abs(x - px) <= 5 && Math.abs(y - py) <= 5) {
this.drag_index[0] = i;
this.drag_index[1] = j;
this.isdragType = 2;
return true;
}
}
}
return false;
},
// 鼠标按下事件
canvasDown(e) {
var x = e.offsetX;
var y = e.offsetY;
// 鼠标右键
if (e.button == 2) {
if (!this.beginDraw) {
return;
}
if (this.coordinates.length) {
var last_x = this.coordinates[this.coordinates.length - 1].cor_x;
var last_y = this.coordinates[this.coordinates.length - 1].cor_y;
if (last_x == x && last_y == y) {
// this.$message.error("不能重复点击");
// 同一个点 不记录
this.doubleclick();
return; //同步放开 二选一
} else {
this.coordinates.push({ cor_x: x, cor_y: y });
this.doubleclick();
return;
}
}
// 点击顶点
if (this.isdragpoint(x, y)) {
// 开启弹窗
console.log("开启弹窗");
if (this.isdragType == 1) {
this.all_coordinates.splice(this.drag_index[0], 1);
} else {
this.all_line_coordinates.splice(this.drag_index[0], 1);
}
this.drawAll();
return;
}
// 点击图像
if (this.all_coordinates.length) {
let dot = { x: x, y: y };
var flag = false;
var arr = JSON.parse(
JSON.stringify(
this.all_coordinates.map((v) => v.points_coordinates)
)
);
for (var i = 0; i < arr.length; i++) {
arr[i].push(arr[i][0]);
arr[i].forEach((item) => {
(item.x = item.cor_x), (item.y = item.cor_y);
});
if (this.judge(dot, arr[i])) {
flag = true;
console.log("点击到了多边形上", i);
this.all_coordinates.splice(i, 1);
this.drawAll();
break;
}
}
if (flag) {
return;
}
}
// 点击直线 人手很难做到
if (this.all_line_coordinates.length) {
var flag = false;
for (var i = 0; i < this.all_line_coordinates.length; i++) {
for (
var j = 0;
j < this.all_line_coordinates[i].points_coordinates.length - 1;
j++
) {
var x1 = this.all_line_coordinates[i].points_coordinates[j].cor_x;
var y1 = this.all_line_coordinates[i].points_coordinates[j].cor_y;
var x2 =
this.all_line_coordinates[i].points_coordinates[j + 1].cor_x;
var y2 =
this.all_line_coordinates[i].points_coordinates[j + 1].cor_y;
if (this.judgeLine(x1, y1, x2, y2, x, y)) {
//判断是否点击到了线上
console.log("点击到了线上", i);
this.all_line_coordinates.splice(i, 1);
this.drawAll();
flag = true;
break;
}
}
}
if (flag) {
return;
}
}
}
// 鼠标左键
else if (e.button == 0) {
if (this.isdragpoint(x, y)) {
this.isdrag = true; //开启拖拽
return;
}
if (!this.beginDraw) {
return;
}
// 同一个点 不记录
if (this.coordinates.length) {
var last_x = this.coordinates[this.coordinates.length - 1].cor_x;
var last_y = this.coordinates[this.coordinates.length - 1].cor_y;
if (last_x == x && last_y == y) {
// this.$message.error("不能重复点击");
return; //同步放开 二选一
}
}
//获取鼠标按下的坐标,放入数组中
if (this.drawType == 1) {
if (this.all_coordinates.length == this.MaxAreaNum) {
this.$message.error("最多只能画" + this.MaxAreaNum + "个多边形");
return;
}
if (this.coordinates.length + 1 == this.MaxAreaPointsNum) {
this.$message.error(
"多边形最多只能画" + this.MaxAreaPointsNum + "个点"
);
this.coordinates.push({ cor_x: x, cor_y: y });
// this.coordinates.push({ cor_x: x, cor_y: y });
this.doubleclick();
return;
}
} else if (this.drawType == 2) {
if (this.all_line_coordinates.length == this.MaxLineNum) {
this.$message.error("最多只能画" + this.MaxLineNum + "条线条");
return;
}
if (this.coordinates.length + 1 == this.MaxLinePointsNum) {
this.$message.error(
"线条最多只能画" + this.MaxLinePointsNum + "个点"
);
this.coordinates.push({ cor_x: x, cor_y: y });
// this.coordinates.push({ cor_x: x, cor_y: y });
this.doubleclick();
return;
}
}
this.coordinates.push({ cor_x: x, cor_y: y });
this.isdraw = true; //正在画多边形
}
},
// 鼠标松开事件
canvasUp(e) {
// if (!this.beginDraw) {
// return;
// }
if (this.isdrag) {
this.isdrag = false; //关闭拖拽点状态
}
this.drag_index = [-1, -1];
this.drawcircle(); //松开画点
},
//鼠标移动事件
canvasMove(e) {
//没开始画或者结束画之后不进行操作
var x = e.offsetX;
var y = e.offsetY;
const canvas = document.querySelector("#mycanvas");
if (this.isdragpoint(x, y)) {
canvas.style.cursor = "pointer";
} else {
canvas.style.cursor = "default";
}
if (this.isdrag) {
if (this.isdragType == 1) {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.all_coordinates[this.drag_index[0]].points_coordinates[
this.drag_index[1]
].cor_x = x;
this.all_coordinates[this.drag_index[0]].points_coordinates[
this.drag_index[1]
].cor_y = y;
this.drawlines();
this.drawcircles();
this.fillarea();
} else {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
this.all_line_coordinates[this.drag_index[0]].points_coordinates[
this.drag_index[1]
].cor_x = x;
this.all_line_coordinates[this.drag_index[0]].points_coordinates[
this.drag_index[1]
].cor_y = y;
this.drawlines();
this.drawcircles();
this.fillarea();
}
}
if (!this.beginDraw) {
return;
}
if (this.coordinates.length == 0 || !this.isdraw) {
return 0;
}
this.drawAll();
this.drawline(); //把之前的点连线
this.drawcircle();
//获取上一个点
var last_x = this.coordinates[this.coordinates.length - 1].cor_x;
var last_y = this.coordinates[this.coordinates.length - 1].cor_y;
if (this.drawType == "2") {
// 显示箭头
this.showArrow(last_x, last_y, x, y);
}
//获取鼠标移动时的点,画线,实现线段跟踪效果。
this.ctx.beginPath();
this.ctx.moveTo(last_x, last_y);
this.ctx.lineTo(x, y);
if (this.drawType == "1") {
// 连接起点 更直观
var x0 = this.coordinates[0].cor_x;
var y0 = this.coordinates[0].cor_y;
this.ctx.lineTo(x0, y0);
}
this.ctx.stroke();
this.ctx.closePath();
},
// 鼠标双击事件
doubleclick() {
if (!this.beginDraw) {
return;
}
// this.coordinates.pop(); // 同步注释 二选一
this.isdraw = false;
if (this.drawType == 1) {
// 限制图形最少三个点
if (this.coordinates.length < 3) {
this.$message.error("绘制区域请至少绘制三个点");
this.coordinates = [];
this.drawAll();
return;
}
this.all_coordinates.push({
canvasId: this.canvasId,
areaName: "区域" + this.canvasId++,
points_coordinates: this.coordinates,
});
} else {
// 限制图形最少两个点
if (this.coordinates.length < 2) {
this.$message.error("绘制直线请至少绘制两个点");
this.coordinates = [];
this.drawAll();
return;
}
var name = "";
if (this.isMultiLine) {
name = "区域1";
} else {
name = "直线" + this.canvasId++;
}
this.all_line_coordinates.push({
canvasId: this.canvasId,
// areaName: "直线" + this.canvasId++,
areaName: name,
directionType: this.directionType,
points_coordinates: this.coordinates,
});
}
this.drawAll();
this.ctx.closePath();
// console.log(this.coordinates);
this.coordinates = [];
// 外部调用智能切换
if (this.roiType == 3) {
if (this.drawType == 1 && this.all_line_coordinates.length == 0) {
this.drawType = 2;
} else if (this.drawType == 2 && this.all_coordinates.length == 0) {
this.drawType = 1;
}
}
if (!this.isMultiLine) this.beginDraw = false;
if (this.isMultiLine && this.all_line_coordinates.length == 2)
this.beginDraw = false;
},
rightClick(e) {
console.log("右键", e);
},
drawlines(data) {
//把所有多边形画出来
for (var i = 0; i < this.all_coordinates.length; i++) {
this.ctx.strokeStyle = "#f36";
var cors = this.all_coordinates[i].points_coordinates;
//前后坐标连线
for (var j = 0; j < cors.length - 1; j++) {
this.ctx.beginPath();
var x0 = cors[j].cor_x;
var y0 = cors[j].cor_y;
var x1 = cors[j + 1].cor_x;
var y1 = cors[j + 1].cor_y;
this.ctx.moveTo(x0, y0);
this.ctx.lineTo(x1, y1);
this.ctx.stroke();
this.ctx.closePath();
}
//最后一个与第一个连线
var begin_x = cors[0].cor_x;
var begin_y = cors[0].cor_y;
var end_x = cors[cors.length - 1].cor_x;
var end_y = cors[cors.length - 1].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(begin_x, begin_y);
this.ctx.lineTo(end_x, end_y);
this.ctx.stroke();
this.ctx.closePath();
}
//把所有线段画出来
for (var i = 0; i < this.all_line_coordinates.length; i++) {
this.ctx.strokeStyle = "#f36";
// 悬浮上色
// this.ctx.strokeStyle = this.colorList[(data.canvasId - 1) % 20];
if (data && this.all_line_coordinates[i].canvasId == data.canvasId) {
// console.log("线段上色", i, data);
// this.ctx.strokeStyle = this.colorList[20];
this.ctx.strokeStyle = "white";
}
// 悬浮上色
var cors = this.all_line_coordinates[i].points_coordinates;
//前后坐标连线
for (var j = 0; j < cors.length - 1; j++) {
this.ctx.beginPath();
var x0 = cors[j].cor_x;
var y0 = cors[j].cor_y;
var x1 = cors[j + 1].cor_x;
var y1 = cors[j + 1].cor_y;
this.showArrow(x0, y0, x1, y1);
this.ctx.moveTo(x0, y0);
this.ctx.lineTo(x1, y1);
this.ctx.stroke();
this.ctx.closePath();
}
}
},
drawline() {
this.ctx.strokeStyle = "#f36";
//把当前绘制的多边形之前的坐标线段绘制出来
for (var i = 0; i < this.coordinates.length - 1; i++) {
this.ctx.beginPath();
var x0 = this.coordinates[i].cor_x;
var y0 = this.coordinates[i].cor_y;
var x1 = this.coordinates[i + 1].cor_x;
var y1 = this.coordinates[i + 1].cor_y;
if (this.drawType == 2) {
this.showArrow(x0, y0, x1, y1);
}
this.ctx.moveTo(x0, y0);
this.ctx.lineTo(x1, y1);
this.ctx.stroke();
this.ctx.closePath();
}
},
drawcircle() {
//为当前的多边形端点画圆
this.ctx.fillStyle = "rgb(0, 195, 155)";
for (var i = 0; i < this.coordinates.length; i++) {
var x = this.coordinates[i].cor_x;
var y = this.coordinates[i].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(x, y);
this.ctx.arc(x, y, 5, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
}
},
drawcircles() {
//为所有的多边形端点画圆
this.ctx.fillStyle = "rgb(0, 195, 155)";
for (var i = 0; i < this.all_coordinates.length; i++) {
var cors = this.all_coordinates[i].points_coordinates;
for (var j = 0; j < cors.length; j++) {
var x = cors[j].cor_x;
var y = cors[j].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(x, y);
this.ctx.arc(x, y, 5, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
}
}
for (var i = 0; i < this.all_line_coordinates.length; i++) {
var cors = this.all_line_coordinates[i].points_coordinates;
for (var j = 0; j < cors.length; j++) {
var x = cors[j].cor_x;
var y = cors[j].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(x, y);
this.ctx.arc(x, y, 5, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
}
}
},
fillarea(data) {
// this.ctx.fillStyle = "rgba(0, 195, 155,0.4)";
for (var i = 0; i < this.all_coordinates.length; i++) {
this.ctx.fillStyle = "rgba(16, 128, 219, 0.4)";
// 悬浮上色
// this.ctx.fillStyle = this.colorList[(data.canvasId - 1) % 20];
if (data && this.all_coordinates[i].canvasId == data.canvasId) {
this.ctx.fillStyle = this.colorList[20];
}
// 悬浮上色
var cors = this.all_coordinates[i].points_coordinates;
var x0 = cors[0].cor_x;
var y0 = cors[0].cor_y;
this.ctx.beginPath();
this.ctx.moveTo(x0, y0);
for (var j = 1; j < cors.length; j++) {
var x = cors[j].cor_x;
var y = cors[j].cor_y;
this.ctx.lineTo(x, y);
}
this.ctx.fill();
this.ctx.closePath();
}
},
/*
ctx:Canvas绘图环境
fromX, fromY:起点坐标(也可以换成p1,只不过它是一个数组)
toX, toY:终点坐标 (也可以换成p2,只不过它是一个数组)
theta:三角斜边一直线夹角
headlen:三角斜边长度
width:箭头线宽度
color:箭头颜色
*/
// 绘制箭头
drawArrow(ctx, fromX, fromY, toX, toY, theta, headlen, width, color) {
theta = typeof theta != "undefined" ? theta : 30;
headlen = typeof theta != "undefined" ? headlen : 10;
width = typeof width != "undefined" ? width : 1;
color = typeof color != "color" ? color : "#000";
// 计算各角度和对应的P2,P3坐标
var angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
angle1 = ((angle + theta) * Math.PI) / 180,
angle2 = ((angle - theta) * Math.PI) / 180,
topX = headlen * Math.cos(angle1),
topY = headlen * Math.sin(angle1),
botX = headlen * Math.cos(angle2),
botY = headlen * Math.sin(angle2);
// ctx.save();
ctx.beginPath();
var arrowX = fromX - topX,
arrowY = fromY - topY;
ctx.moveTo(arrowX, arrowY);
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
arrowX = toX + topX;
arrowY = toY + topY;
ctx.moveTo(arrowX, arrowY);
ctx.lineTo(toX, toY);
arrowX = toX + botX;
arrowY = toY + botY;
ctx.lineTo(arrowX, arrowY);
// ctx.strokeStyle = color;
ctx.lineWidth = width;
ctx.stroke();
// ctx.restore();
},
},
};
</script>
<style lang="scss" scoped>
#mycanvas {
/* border: 1px solid red; */
// background-color: #2d303b;
}
.staticLabels {
flex-grow: 1;
overflow: auto;
margin-top: 10px;
background: rgba(247, 250, 255, 0.8);
border-radius: 2px;
border: 1px solid #eaecf6;
padding-left: 5px;
font-size: 12px;
line-height: 24px;
.staticLabels_item {
float: left;
height: 24px;
background: #ecf3fe;
border-radius: 2px;
border: 1px solid #2c7be5;
padding: 0px 4px;
margin-right: 6px;
margin-top: 5px;
user-select: none;
}
}
</style>
使用
<template>
<div>
<Canvas
ref="drawArea"
:canvasKey="canvasKey"
:myDrawList="myDrawList"
:myRoiType="myRoiType"
/>
<el-select v-model="canvasKey" style="width: 100px" size="mini">
<el-option value="first" label="数据1"></el-option>
<el-option value="second" label="数据2"></el-option>
<el-option value="third" label="数据3"></el-option>
</el-select>
<el-select
v-model="myRoiType"
style="width: 100px; margin: 10px"
size="mini"
>
<el-option value="1" label="区域"></el-option>
<el-option value="2" label="线条"></el-option>
<el-option value="3" label="区域加线条"></el-option>
</el-select>
<el-button type="primary" @click="saveFunc" size="mini">保存</el-button>
</div>
</template>
<script>
import Cookies from "js-cookie";
import Canvas from "./canvas.vue";
export default {
data() {
return {
canvasKey: "first",
myDrawList: [],
myRoiType: "1",
};
},
components: { Canvas },
created() {
this.myDrawList = JSON.parse(Cookies.get("myDrawList") || "[]");
console.log("取值", this.myDrawList);
},
methods: {
saveFunc() {
var arr = this.$refs.drawArea.getData();
Cookies.set("myDrawList", JSON.stringify(arr));
console.log("保存", arr);
},
},
};
</script>
<style scoped></style>