创建draw.js文件
/**
* 画布中绘制矩形
* 参数: cav-画布对象 list-矩形数组 i-选中矩形下标
**/
/* 操作执行方法分发 */
export function draw(cav, list, i) {
// 画布初始化
let ctx = cav.getContext('2d');
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;
// 变量初始化
let sX = 0; // 鼠标X坐标
let sY = 0; // 鼠标Y坐标
/*
*鼠标移动进行第一层判断, 区分情况: 无矩形, 已有矩形无选中, 已有选中矩形
*/
cav.onmousemove = function (em) {
sX = em.offsetX;
sY = em.offsetY;
let iem = undefined; // 鼠标移动时临时存储当前鼠标所在矩形的下标
if (list.length === 0) { // **** 无矩形 ****
// 绘制新矩形
newDraw(cav, ctx, list);
} else if (i === undefined) { // **** 已有矩形无选中 ****
// 判断鼠标位置
list.forEach(function (value, index, array) {
if (value.w > 0 && value.h > 0 && sX > value.x && sX < value.x + value.w && sY > value.y && sY < value.y + value.h) {
// 鼠标在右下方向生成的矩形中
iem = index;
judgeDraw(cav, ctx, list, iem);
}
if (value.w < 0 && value.h > 0 && sX < value.x && sX > value.x + value.w && sY > value.y && sY < value.y + value.h) {
// 鼠标在左下方向生成的矩形中
iem = index;
judgeDraw(cav, ctx, list, iem);
}
if (value.w > 0 && value.h < 0 && sX > value.x && sX < value.x + value.w && sY < value.y && sY > value.y + value.h) {
// 鼠标在右上方向生成的矩形中
iem = index;
judgeDraw(cav, ctx, list, iem);
}
if (value.w < 0 && value.h < 0 && sX < value.x && sX > value.x + value.w && sY < value.y && sY > value.y + value.h) {
// 鼠标在左上方向生成的矩形中
iem = index;
judgeDraw(cav, ctx, list, iem);
}
if (iem === undefined) {
// 鼠标不在矩形中
newDraw(cav, ctx, list);
}
})
} else { // **** 已有选中矩形 ****
// 判断鼠标位置
for (let index = 0; index < list.length; index++) {
let value = list[index];
if (sX < value.x + 5 && sX > value.x - 5 && sY < value.y + 5 && sY > value.y - 5) {
// *** 鼠标在起点角 ***
if (index === i) {
changeDraw(cav, ctx, list, i, 1);
break;
}
} else if (sX < value.x + value.w + 5 && sX > value.x + value.w - 5 && sY < value.y + 5 && sY > value.y - 5) {
// *** 鼠标在起点横向角 ***
if (index === i) {
changeDraw(cav, ctx, list, i, 2);
break;
}
} else if (sX < value.x + 5 && sX > value.x - 5 && sY < value.y + value.h + 5 && sY > value.y + value.h - 5) {
// *** 鼠标在起点纵向角 ***
if (index === i) {
changeDraw(cav, ctx, list, i, 3);
break;
}
} else if (sX < value.x + value.w + 5 && sX > value.x + value.w - 5 && sY < value.y + value.h + 5 && sY > value.y + value.h - 5) {
// *** 鼠标在终点角 ***
if (index === i) {
changeDraw(cav, ctx, list, i, 4);
break;
}
} else if (value.w > 0 && value.h > 0 && sX > value.x && sX < value.x + value.w && sY > value.y && sY < value.y + value.h) {
// *** 鼠标在右下方向生成的矩形中 ***
iem = index
judgeDraw(cav, ctx, list, index);
break;
} else if (value.w < 0 && value.h > 0 && sX < value.x && sX > value.x + value.w && sY > value.y && sY < value.y + value.h) {
// *** 鼠标在左下方向生成的矩形中 ***
iem = index
judgeDraw(cav, ctx, list, index);
break;
} else if (value.w > 0 && value.h < 0 && sX > value.x && sX < value.x + value.w && sY < value.y && sY > value.y + value.h) {
// *** 鼠标在右上方向生成的矩形中 ***
iem = index
judgeDraw(cav, ctx, list, index);
break;
} else if (value.w < 0 && value.h < 0 && sX < value.x && sX > value.x + value.w && sY < value.y && sY > value.y + value.h) {
// *** 鼠标在左上方向生成的矩形中 ***
iem = index
judgeDraw(cav, ctx, list, index);
break;
} else {
if (iem === undefined) {
// *** 鼠标不在矩形中 ***
newDraw(cav, ctx, list);
}
}
}
}
/* 鼠标移出画布区域时保存选中矩形下标(如有) */
cav.onmouseout = function (eo) {
if (i !== undefined) {
// 初始化
draw(cav, list, i);
}
};
}
};
/* 编辑矩形四个角 */
function changeDraw(cav, ctx, list, i, site) {
cav.style.cursor = 'pointer'
// site: 操作矩形角的位置, 1-起点 2-起点横向 3-起点纵向 4-终点
let mark = list[i];
/* 按下鼠标左键 */
cav.onmousedown = function (ed) {
// 保存鼠标落下位置的X, Y坐标, firefox中鼠标移动后ed.offsetX ed.offsetY会变成 0, 需要使用临时参数存储起来
let sX = ed.offsetX; // 起点X坐标
let sY = ed.offsetY; // 起点Y坐标
/* 移动鼠标 */
cav.onmousemove = function (em) {
// 计算绘制数据
let iframe = {}
switch (site) {
case 1:
iframe = {
x: em.offsetX,
y: em.offsetY,
w: mark.w - (em.offsetX - sX),
h: mark.h - (em.offsetY - sY)
}
break;
case 2:
iframe = {
x: mark.x,
y: mark.y + (em.offsetY - sY),
w: mark.w + (em.offsetX - sX),
h: mark.h - (em.offsetY - sY)
}
break;
case 3:
iframe = {
x: mark.x + (em.offsetX - sX),
y: mark.y,
w: mark.w - (em.offsetX - sX),
h: mark.h + (em.offsetY - sY)
}
break;
case 4:
iframe = {
x: mark.x,
y: mark.y,
w: mark.w + (em.offsetX - sX),
h: mark.h + (em.offsetY - sY)
}
break;
}
list.splice(i, 1, iframe);
// 重新绘制
reDraw(cav, ctx, list, i);
}
/* 鼠标离开矩形区 */
cav.onmouseout = function (eo) {
// 重新绘制
reDraw(cav, ctx, list);
// 初始化
draw(cav, list)
};
/* 监听键盘, 点击后可以控制删除, 由于移动矩形事件已经监听了onmousemove, 所以在移动矩形方法中仍有一次调用 */
delDraw(cav, ctx, list, i);
}
};
/* 绘制新矩形 */
function newDraw(cav, ctx, list) {
cav.style.cursor = 'crosshair'
// 初始化变量
let start = false; // 画框状态, false时不执行画框操作
let sX = 0; // 起点X坐标
let sY = 0; // 起点Y坐标
/* 按下鼠标左键 */
cav.onmousedown = function (ed) {
/* 使用变量 */
start = true;
sX = ed.offsetX;
sY = ed.offsetY;
/* 重置按键监听, 防止选中取消后仍可删除 */
delDraw(cav, ctx, list, null)
/* 鼠标移动 */
cav.onmousemove = function (em) {
if (start) {
// 重新绘制
reDraw(cav, ctx, list);
// 设置边框为虚线
ctx.beginPath();
ctx.setLineDash([8, 4]);
ctx.rect(sX, sY, em.offsetX - sX, em.offsetY - sY);
ctx.stroke();
}
}
/* 鼠标抬起 */
cav.onmouseup = function (eu) {
if (start && Math.abs(eu.offsetX - sX) > 10 && Math.abs(eu.offsetY - sY) > 10) {
// 改变矩形数组
let frame = {
x: sX, y: sY, w: eu.offsetX - sX, h: eu.offsetY - sY
};
list.push(frame);
// 重新绘制
reDraw(cav, ctx, list);
// 改变画框状态
start = false
// 初始化
draw(cav, list)
} else {
// 重新绘制
reDraw(cav, ctx, list);
// 改变画框状态
start = false
// 初始化
draw(cav, list)
}
};
/* 鼠标离开矩形区 */
cav.onmouseout = function (eo) {
if (start && Math.abs(eo.offsetX - sX) > 10 && Math.abs(eo.offsetY - sY) > 10) {
// 改变矩形数组
let frame = {
x: sX, y: sY, w: eo.offsetX - sX, h: eo.offsetY - sY
};
list.push(frame);
// 重新绘制
reDraw(cav, ctx, list);
// 改变画框状态
start = false;
// 初始化
draw(cav, list)
} else {
// 重新绘制
reDraw(cav, ctx, list);
// 改变画框状态
start = false
// 初始化
draw(cav, list)
}
};
}
};
/* 选中矩形, 重绘矩形, 并分发后续事件 */
function judgeDraw(cav, ctx, list, iem) {
cav.style.cursor = 'default'
// 初始化变量
let sX = 0; // 起点X坐标
let sY = 0; // 起点Y坐标
/* 按下鼠标左键 */
cav.onmousedown = function (ed) {
sX = ed.offsetX;
sY = ed.offsetY;
// 更改选中状态, 重绘矩形
reDraw(cav, ctx, list, iem);
/* 当仅点击选中矩形便抬起鼠标后, 重新初始化画布 */
cav.onmouseup = function () {
// 重绘矩形
reDraw(cav, ctx, list, iem);
// 初始化
draw(cav, list, iem);
};
/* 按住拖动鼠标, 移动选中矩形*/
moveDraw(cav, ctx, list, iem, sX, sY);
/* 监听键盘, 点击后可以控制删除, 由于移动矩形事件已经监听了onmousemove, 所以在移动矩形方法中仍有一次调用 */
delDraw(cav, ctx, list, iem);
}
};
/* 移动矩形 */
function moveDraw(cav, ctx, list, i, sX, sY) {
let mark = list[i]
cav.onmousemove = function (em) {
let iframe = {
x: mark.x + (em.offsetX - sX),
y: mark.y + (em.offsetY - sY),
w: mark.w,
h: mark.h
}
list.splice(i, 1, iframe);
/* 监听键盘, 使矩形在移动后仍可删除, 在点击未移动过的矩形时仍有一次监听 */
delDraw(cav, ctx, list, i);
// 重新绘制
reDraw(cav, ctx, list, i);
}
cav.onmouseup = function () {
// 重绘矩形
reDraw(cav, ctx, list, i);
// 初始化
draw(cav, list, i);
};
};
/* 删除矩形 */
function delDraw(cav, ctx, list, i) {
/* 按键事件 */
if (i === null) {
// i为null时阻止按键监听事件冒泡
cav.onkeydown = function (k) {
return false;
}
} else {
// 监听按键事件
cav.onkeydown = function (k) {
let key = k.keyCode || k.which;
if (key == 8 && i !== null) {
if (list.length >= 1) {
// 删除数组元素
list.splice(i, 1);
// 重绘矩形
reDraw(cav, ctx, list);
} else {
/* 矩形数组长度为0, 已将矩形框全部删除 */
ctx.clearRect(0, 0, cav.width, cav.height);
}
// 重置监听状态, 防止删除完毕后, 按键监听不消失
delDraw(cav, ctx, list, null)
// 重绘矩形
reDraw(cav, ctx, list);
// 初始化
draw(cav, list);
}
}
}
};
/* 重绘所有矩形 */
function reDraw(cav, ctx, list, i) {
ctx.setLineDash([8, 0]); // 设置边框为实线
ctx.clearRect(0, 0, cav.width, cav.height);
// 绘制未选中部分
list.forEach(function (value, index, array) {
if (i === undefined || index != i) {
ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.rect(value.x, value.y, value.w, value.h);
ctx.stroke();
}
});
// 绘制已选中部分
list.forEach(function (value, index, array) {
if (index === i) {
/* 绘制方框 */
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.rect(value.x, value.y, value.w, value.h);
ctx.fillStyle = 'RGBA(102,102,102,0.2)'
ctx.fillRect(value.x, value.y, value.w, value.h);
ctx.stroke();
// 绘制四个角的圆圈
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.arc(value.x, value.y, 4, 0, Math.PI * 2)
ctx.fillStyle = "red";
ctx.fill();// 画起点实心圆
ctx.stroke();
ctx.beginPath();
ctx.arc(value.x, value.y + value.h, 4, 0, Math.PI * 2);
ctx.fillStyle = "red";
ctx.fill();// 画起点纵向实心圆
ctx.stroke();
ctx.beginPath();
ctx.arc(value.x + value.w, value.y + value.h, 4, 0, Math.PI * 2);
ctx.fillStyle = "red";
ctx.fill();// 画起点横向实心圆
ctx.stroke();
ctx.beginPath();
ctx.arc(value.x + value.w, value.y, 4, 0, Math.PI * 2);
ctx.fillStyle = "red";
ctx.fill();// 画终点实心圆
ctx.stroke();
}
})
};
vue文件中引入draw方法
import {draw} from "../../utils/draw";
编写dom
<el-dialog
title="图片画图"
:visible.sync="imageDialog"
width="1240px">
<div class="imgcontent" v-loading="loadingImg">
<el-image style="width:1200px;height: 675px" :src="taskTypeImage"></el-image>
<canvas ref="markCanvas"></canvas>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="imageDialog = false">取 消</el-button>
<el-button type="primary" @click="confirmImg">确 定</el-button>
</span>
</el-dialog>
初始化加载方法
initCanvas() {
let that = this
this.$nextTick(() => {
// 初始化canvas宽高
let cav = this.$refs.markCanvas;
cav.width = 1200;
cav.height = 675;
let ctx = cav.getContext('2d');
ctx.strokeStyle = 'red'
cav.style.cursor = 'crosshair'
// 计算使用变量
let list = this.markList; // 画框数据集合, 用于服务端返回的数据显示和绘制的矩形保存
// 若服务端保存的为百分比则此处需计算实际座标, 直接使用实际座标可省略
list.forEach(function (value, index, array) {
let newValue = {
x: value.x * cav.width,
y: value.y * cav.height,
w: value.w * cav.width,
h: value.h * cav.height,
}
list.splice(index, 1, newValue)
})
// 若list长度不为0, 则显示已标记框
if (list.length !== 0) {
list.forEach(function (value, index, array) {
// 遍历绘制所有标记框
ctx.rect(value.x, value.y, value.w, value.h);
ctx.stroke();
});
}
// 调用封装的绘制方法
draw(cav,list);
// 备注: js中对象操作指向的是对象的物理地址, 获取绘制完矩形的结果数组直接取用或处理this.markList即可
})
},