console
var demo = new Vue({
'el': '#demo',
components: {},
data: {
rectType: 1,
rectList: [],
myCanvas: null,
cxt: null,
mouseInit: null,
selectedRect: null,
changeRect: null,
nowBorderRect: null,
selectedId: null,
moved: false,
mouse: false,
nowId: null,
opacity: 0.2,
color1: "#0f0",
color2: "#f00",
circlsRadius: 5,
lineWidth: 2,
min: 50,
minAngle: 10
},
watch: {},
mounted() {
this.init();
},
methods: {
init() {
this.$nextTick(() => {
this.myCanvas = this.$refs.myCanvas;
this.cxt = this.myCanvas.getContext("2d");
this.myCanvas.onmousedown = e => {
this.onMouseDown(e);
};
this.myCanvas.onmouseup = e => {
};
this.myCanvas.onmousemove = e => {
this.onMouseMove(e);
};
window.addEventListener("mouseup", this.onMouseUp, false);
});
},
throttle(fn, time = 10) {
let timer = null;
return (...args) => {
if (!timer) {
timer = setTimeout(() => {
fn.apply(null, args);
timer = null;
}, time);
}
};
},
onMouseDown(e) {
this.mouseInit = {
x: e.offsetX,
y: e.offsetY
};
let nowRect = null;
let changeRect = null;
if (this.isrCircleIn(this.mouseInit).rect) {
this.changeRect = this.isrCircleIn(this.mouseInit).rect;
} else {
this.rectList.forEach(item => {
if (this.pointInPoly(this.mouseInit, item.points)) {
nowRect = item;
}
});
if (nowRect) {
this.selectedId = nowRect.id;
this.selectedRect = nowRect;
this.nowId = null;
} else {
this.nowId = this.rectList.length;
this.selectedId = null;
this.nowBorderRect=null
this.mouse = true;
}
this.drawRect();
if (this.selectedId || this.selectedId === 0) {
this.moved = true;
} else {
this.moved = false;
}
}
},
async onMouseMove(e) {
let pt = {
x: e.offsetX,
y: e.offsetY
};
await this.throttle(this.isRectIn)(pt);
if (this.mouse) {
this.rectList[this.nowId] = await this.setRectPoints(e);
this.selectedRect = this.rectList[this.nowId];
this.drawRect();
}
if (this.moved) {
this.moveRect(e);
}
if (this.changeRect) {
this.moveCircle(
e,
this.changeRect.id,
this.isrCircleIn(this.mouseInit).index
);
}
},
onMouseUp(e) {
this.moved = false;
this.mouse = false;
this.nowId = null;
this.selectedId = null;
this.selectedRect = null;
this.changeRect = null;
this.mouseInit = null;
if (this.nowBorderRect) {
this.isMin(this.nowBorderRect);
}
},
borderRect(rect) {
this.cxt.beginPath();
this.cxt.moveTo(rect.points[0].x, rect.points[0].y);
rect.points.map(i => {
this.cxt.lineTo(i.x, i.y);
});
this.cxt.strokeStyle = rect.type === 2 ? this.color2 : this.color1;
this.cxt.lineWidth = this.lineWidth;
this.cxt.globalAlpha = 1;
this.cxt.closePath();
this.cxt.stroke();
this.borderCircle(rect);
},
borderCircle(rect) {
rect.points.map(i => {
this.cxt.beginPath();
this.cxt.arc(i.x, i.y, this.circlsRadius, 2 * Math.PI, 0);
this.cxt.fillStyle = "#fff";
this.cxt.fill();
this.cxt.strokeStyle = rect.type === 2 ? this.color2 : this.color1;
this.cxt.lineWidth = this.lineWidth;
this.cxt.globalAlpha = 1;
this.cxt.closePath();
this.cxt.stroke();
});
this.nowBorderRect = rect;
},
async drawRect() {
this.cxt.clearRect(0, 0, this.myCanvas.width, this.myCanvas.height);
this.rectList.map(item => {
this.cxt.beginPath();
this.cxt.fillStyle = item.type === 2 ? this.color2 : this.color1;
this.cxt.globalAlpha = this.opacity;
this.cxt.moveTo(item.points[0].x, item.points[0].y);
item.points.forEach(i => {
this.cxt.lineTo(i.x, i.y);
});
this.cxt.closePath();
this.cxt.fill();
});
if (this.selectedRect || this.changeRect || this.nowBorderRect) {
let rect = this.selectedRect || this.changeRect || this.nowBorderRect;
this.borderRect(rect);
}
},
moveRect(e) {
this.rectList.map(item => {
if (item.id === this.selectedId) {
let movex = e.offsetX - this.mouseInit.x;
let movey = e.offsetY - this.mouseInit.y;
this.mouseInit.x = e.offsetX;
this.mouseInit.y = e.offsetY;
item.points.map(i => {
i.x += movex;
i.y += movey;
});
this.selectedRect = item;
}
});
this.drawRect();
},
moveCircle(e, id, index) {
let movex = e.offsetX - this.mouseInit.x;
let movey = e.offsetY - this.mouseInit.y;
this.mouseInit.x = e.offsetX;
this.mouseInit.y = e.offsetY;
this.rectList[id].points[index].x += movex;
this.rectList[id].points[index].y += movey;
this.drawRect();
},
pointInPoly(pt, poly) {
for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) {
((poly[i].y <= pt.y && pt.y < poly[j].y) ||
(poly[j].y <= pt.y && pt.y < poly[i].y)) &&
pt.x <
((poly[j].x - poly[i].x) * (pt.y - poly[i].y)) /
(poly[j].y - poly[i].y) +
poly[i].x &&
(c = !c);
}
return c;
},
isrCircleIn(pt) {
let t = false;
let rect = null;
let index = null;
this.rectList.forEach(item => {
item.points.forEach((i, idx) => {
let line = Math.abs(
Math.sqrt(Math.pow(i.x - pt.x, 2) + Math.pow(i.y - pt.y, 2))
);
if (line - this.circlsRadius < 0) {
t = true;
rect = item;
index = idx;
}
});
});
return {
t: t,
rect: rect,
index: index
};
},
isRectIn(pt) {
if (this.rectList.length > 0) {
if (this.isrCircleIn(pt).t) {
this.myCanvas.style.cursor = "pointer";
} else if (
this.rectList.find(item => this.pointInPoly(pt, item.points))
) {
this.myCanvas.style.cursor = "move";
} else {
this.myCanvas.style.cursor = "default";
}
}
},
isMin(rect) {
if (rect) {
let line1 = Math.abs(
Math.sqrt(
Math.pow(rect.points[0].x - rect.points[2].x, 2) +
Math.pow(rect.points[0].y - rect.points[2].y, 2)
)
);
let line2 = Math.abs(
Math.sqrt(
Math.pow(rect.points[1].x - rect.points[3].x, 2) +
Math.pow(rect.points[1].y - rect.points[3].y, 2)
)
);
if (line1 < this.min || line2 < this.min) {
alert("框选区域太小");
this.deleteRect(rect.id);
} else if (this.isRuleRect(rect)) {
alert("框选区域不合法请重新绘制");
this.deleteRect(rect.id);
} else {
this.isCanvasIn();
}
}
},
isRuleRect(rect) {
let A = rect.points[0];
let B = rect.points[1];
let C = rect.points[2];
let D = rect.points[3];
let AB = Math.sqrt(
Math.pow(A.x - B.x, 2) + Math.pow(A.y - B.y, 2)
).toFixed(2);
let BC = Math.sqrt(
Math.pow(B.x - C.x, 2) + Math.pow(B.y - C.y, 2)
).toFixed(2);
let AD = Math.sqrt(
Math.pow(A.x - D.x, 2) + Math.pow(A.y - D.y, 2)
).toFixed(2);
let CD = Math.sqrt(
Math.pow(C.x - D.x, 2) + Math.pow(C.y - D.y, 2)
).toFixed(2);
let BD = Math.sqrt(
Math.pow(B.x - D.x, 2) + Math.pow(B.y - D.y, 2)
).toFixed(2);
let AC = Math.sqrt(
Math.pow(A.x - C.x, 2) + Math.pow(A.y - C.y, 2)
).toFixed(2);
let cosA =
(Math.pow(AB, 2) + Math.pow(AD, 2) - Math.pow(BD, 2)) / (2 * AB * AD);
let cosB =
(Math.pow(AB, 2) + Math.pow(BC, 2) - Math.pow(AC, 2)) / (2 * AB * BC);
let cosC =
(Math.pow(BC, 2) + Math.pow(CD, 2) - Math.pow(BD, 2)) / (2 * BC * CD);
let cosD =
(Math.pow(AD, 2) + Math.pow(CD, 2) - Math.pow(AC, 2)) / (2 * AD * CD);
let angleA = Math.round((Math.acos(cosA) * 180) / Math.PI);
let angleB = Math.round((Math.acos(cosB) * 180) / Math.PI);
let angleC = Math.round((Math.acos(cosC) * 180) / Math.PI);
let angleD = Math.round((Math.acos(cosD) * 180) / Math.PI);
return (
[angleA, angleB, angleC, angleD].find(item => item < this.minAngle) ||
this.convex(rect.points, 4) === -1
);
},
deleteRect(id) {
this.selectedId = null;
this.selectedRect = null;
this.changeRect = null;
this.nowBorderRect = null;
this.mouseInit = null;
let arr = this.rectList.filter(item => item.id !== id);
if (arr && arr.length > 0) {
this.rectList = arr.map((item, index) => {
return {
id: index,
type: item.type,
points: item.points
};
});
} else {
this.rectList = [];
}
this.drawRect();
},
isCanvasIn() {
this.rectList.map(item => {
item.points.map(i => {
if (i.x < 0) {
i.x = 0;
}
if (i.x > this.myCanvas.width) {
i.x = this.myCanvas.width;
}
if (i.y < 0) {
i.y = 0;
}
if (i.y > this.myCanvas.height) {
i.y = this.myCanvas.height;
}
});
});
this.drawRect();
},
convex(p, n) {
var j, k, z;
var flag = 0;
if (n < 3) {
return 0;
}
for (var i = 0; i < n; i++) {
j = (i + 1) % n;
k = (i + 2) % n;
z = (p[j].x - p[i].x) * (p[k].y - p[j].y);
z -= (p[j].y - p[i].y) * (p[k].x - p[j].x);
if (z < 0) {
flag |= 1;
} else if (z > 0) {
flag |= 2;
}
if (flag == 3) {
return -1;
}
}
if (flag != 0) {
return 1;
} else {
return 0;
}
},
setRectPoints(e) {
if (this.mouseInit.x <= e.offsetX && this.mouseInit.y <= e.offsetY) {
return {
id: this.nowId,
type: this.rectType,
points: [
{
x: this.mouseInit.x,
y: this.mouseInit.y,
isSelected: false
},
{
x: e.offsetX,
y: this.mouseInit.y,
isSelected: false
},
{
x: e.offsetX,
y: e.offsetY,
isSelected: false
},
{
x: this.mouseInit.x,
y: e.offsetY,
isSelected: false
}
]
};
} else if (
this.mouseInit.x >= e.offsetX &&
this.mouseInit.y >= e.offsetY
) {
return {
id: this.nowId,
type: this.rectType,
points: [
{
x: e.offsetX,
y: e.offsetY,
isSelected: false
},
{
x: this.mouseInit.x,
y: e.offsetY,
isSelected: false
},
{
x: this.mouseInit.x,
y: this.mouseInit.y,
isSelected: false
},
{
x: e.offsetX,
y: this.mouseInit.y,
isSelected: false
}
]
};
} else if (
this.mouseInit.x >= e.offsetX &&
this.mouseInit.y <= e.offsetY
) {
return {
id: this.nowId,
type: this.rectType,
points: [
{
x: e.offsetX,
y: this.mouseInit.y,
isSelected: false
},
{
x: this.mouseInit.x,
y: this.mouseInit.y,
isSelected: false
},
{
x: this.mouseInit.x,
y: e.offsetY,
isSelected: false
},
{
x: e.offsetX,
y: e.offsetY,
isSelected: false
}
]
};
} else {
return {
id: this.nowId,
type: this.rectType,
points: [
{
x: this.mouseInit.x,
y: e.offsetY,
isSelected: false
},
{
x: e.offsetX,
y: e.offsetY,
isSelected: false
},
{
x: e.offsetX,
y: this.mouseInit.y,
isSelected: false
},
{
x: this.mouseInit.x,
y: this.mouseInit.y,
isSelected: false
}
]
};
}
}
},
beforeDestroy() {
window.removeEventListener("mouseup", this.onMouseUp);
delete this.onMouseUp;
}
});
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui@2.0.11/lib/index.js"></script>
<div id="demo" class="canvas-demo">
<div class="top-box">
<el-radio-group v-model="rectType">
<el-radio :label="1">绿色框</el-radio>
<el-radio :label="2">红色框</el-radio>
</el-radio-group>
</div>
<div class="draw-box">
<canvas ref="myCanvas" width="640" height="480"></canvas>
</div>
<div class="btn-box">
<el-button type="primary" size="mini" @click="deleteRect(nowBorderRect.id)">删除选中</el-button>
</div>
</div>
@import url("//unpkg.com/element-ui@2.0.11/lib/theme-chalk/index.css");
.canvas-demo{
width:100%;
}
.canvas-demo .draw-box {
display: flex;
justify-content: center;
}
.canvas-demo canvas {
background-color: #e1e1e1;
}
.canvas-demo .top-box {
display: flex;
justify-content: center;
margin: 10px auto;
}
.canvas-demo .btn-box {
margin: 20px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}