- 在上一个板块中实现了画矩形,这个板块实现多个矩形选中其中一个,首先需要定义一个变量存放选中图形的坐标,在da ta中定义:
activeData: [], //当前选中的图形坐标数据
- 当点击了鼠标以后我们需要判断当坐标是否在矩形内部或线上,需要一个算法,这里需要引入turf.js库,放在文章最后自行复制使用,判断方法在mathUtils.js文件中封装:
// 判断点是否在多边形内部或者线上
pointInPolygonORLine(point, polygon) {
var pt = turf.point(point);
var poly = turf.polygon([polygon]);
return turf.booleanPointInPolygon(pt, poly)
},
- 在鼠标按下的时候进行判断
if (this.type === 'move') {
let activePoint = []
if (this.drawAllData.length > 0) {
for (const [i, item] of this.drawAllData.entries()) {
mathUtils.pointInPolygonORLine(this.mouseStartPos, item) === true ? activePoint = item : []
}
}
if (this.activeData.length === 0) {
this.activeData = activePoint;
} else if (this.activeData.length > 0) {
this.drawAllData = this.drawAllData.concat([this.activeData])
this.activeData = activePoint;
}
this.drawAllData = this.drawAllData.filter(item => {
return item !== this.activeData
})
this.redrawMap();
}
- 最后在redrawMap方法重绘的时候绘制选中的数据就可以了
//绘制正在编辑的数据
if (this.activeData.length > 0) {
drawMap.drawRectangle(this.activeData, true);
}
为了区分选中没选中,对绘制的矩形颜色进行了修改,传入了isCheck参数
drawRectangle(arr, isCheck) {
mapCtx.strokeStyle = isCheck ? '#1289ff' : '#1289ff80';
mapCtx.fillStyle = isCheck ? '#ffffff80' : '#fff0';
mapCtx.lineWidth = 2;
mapCtx.setLineDash([]);
mapCtx.lineJoin = 'bevel';
mapCtx.beginPath();
mapCtx.moveTo(arr[0][0], arr[0][1]);
for (let i = 1; i < arr.length; i++) {
mapCtx.lineTo(arr[i][0], arr[i][1]);
}
mapCtx.stroke();
mapCtx.fill();
},
附上整体代码,复制可用:
首页:
<template>
<div id="app">
<div class="nav-top">
<div :class="{'nav-sel':type==='move'}" @click="setType('move')">选择</div>
<div :class="{'nav-sel':type==='rectangle'}" @click="setType('rectangle')">矩形</div>
<div :class="{'nav-sel':type==='circle'}" @click="setType('circle')">圆形</div>
</div>
<div class="draw-box" ref="drawBox">
<canvas class="canvas-style" ref="canvasMap" @click="mapClick" @mousedown="mapMousedown"
@mousemove="mapMousemove" @mouseup="mapMouseUp" @dblclick="mapDbclick"
@mousewheel.prevent="mapMouseWheel" @contextmenu.prevent="rightMenu"></canvas>
</div>
</div>
</template>
<script>
import drawMap from '@/utils/drawMap.js';
import mathUtils from '@/utils/mathUtils.js';
export default {
name: 'app',
data() {
return {
type: 'rectangle', //当前可编辑图形的状态
mouseStartPos: [], //鼠标点击的位置
mouseClickArr: [], //当前已点击的坐标记录
drawAllData: [], //当前所有保存的数据
activeData: [], //当前选中的图形坐标数据
}
},
mounted() {
//初始化画板
const initData = {
id: this.$refs.canvasMap,
w: this.$refs.drawBox.clientWidth,
h: this.$refs.drawBox.clientHeight
}
drawMap.initMap(initData);
this.redrawMap();
},
methods: {
//单击地图
mapClick(e) {
let x = e.offsetX
let y = e.offsetY
//点击地图加入点位
switch (this.type) {
case 'rectangle':
this.mouseClickArr.push([x, y])
if (this.mouseClickArr.length === 3) {
this.drawRectangle(this.mouseClickArr)
this.redrawMap()
this.mouseClickArr = []
}
break;
}
},
//鼠标按下
mapMousedown(e) {
let x = e.offsetX
let y = e.offsetY
this.mouseStartPos = [e.offsetX, e.offsetY]
if (this.type === 'move') {
let activePoint = []
if (this.drawAllData.length > 0) {
for (const [i, item] of this.drawAllData.entries()) {
mathUtils.pointInPolygonORLine(this.mouseStartPos, item) === true ? activePoint = item : []
}
}
if (this.activeData.length === 0) {
this.activeData = activePoint;
} else if (this.activeData.length > 0) {
this.drawAllData = this.drawAllData.concat([this.activeData])
this.activeData = activePoint;
}
this.drawAllData = this.drawAllData.filter(item => {
return item !== this.activeData
})
this.redrawMap();
}
},
//鼠标移动
mapMousemove(e) {
let x = e.offsetX
let y = e.offsetY
this.redrawMap({
x,
y
})
},
//鼠标抬起
mapMouseUp(e) {
},
//鼠标双击
mapDbclick(e) {
console.log('鼠标双击', e);
},
//鼠标滚轮
mapMouseWheel(e) {
console.log('鼠标滚轮', e);
},
//鼠标右击
rightMenu(e) {
console.log('鼠标右击', e);
},
async redrawMap(point) {
//canvas重绘
drawMap.redrawMap();
//实时画鼠标点位
point && point.x && drawMap.drawCircle({
x: point.x,
y: point.y,
r: 4,
fillStyle: '#fff'
})
//绘制已经保存的房间数据
if (this.drawAllData.length > 0) {
for (const [i, item] of this.drawAllData.entries()) {
drawMap.drawRectangle(item);
}
}
//绘制正在编辑的数据
if (this.activeData.length > 0) {
drawMap.drawRectangle(this.activeData, true);
}
//实时的画各类图形
point && point.x && this.drawNowDrawing(point.x, point.y);
},
//实时画图形
drawNowDrawing(x, y) {
switch (this.type) {
case 'rectangle':
if (this.mouseClickArr.length >= 1) {
const mouseClick = this.mouseClickArr.length === 1 ? [
[x, y],
[x, y]
] : [
[x, y]
]
const newArr = this.mouseClickArr.concat(mouseClick)
this.drawRectangle(newArr)
}
break;
}
},
//画矩形
drawRectangle(arr) {
// 画矩形,点选三个点完成一个矩形
const vPoint = mathUtils.calculateVerticalPoint(arr);
// 根据第一点算的为第四点 根据第二点算的为第三点
const point4 = mathUtils.calculatePoint(vPoint, arr[0], arr[2]);
const point3 = mathUtils.calculatePoint(vPoint, arr[1], arr[2]);
const rectangleData = [arr[0], arr[1], point3, point4, arr[0]];
if (this.mouseClickArr.length === 3) {
this.drawAllData = this.drawAllData.concat([rectangleData])
}
drawMap.drawRectangle(rectangleData);
},
//设置可编辑类型
setType(e) {
this.type = e
},
}
}
</script>
<style>
html,
body {
margin: 0;
padding: 0;
}
.nav-top {
display: flex;
align-items: center;
}
.nav-top>div {
padding: 10px;
border: 1px solid;
border-radius: 8px;
margin-right: 20px;
cursor: pointer;
}
.nav-top .nav-sel {
border: 2px solid #18c1f6;
}
.draw-box {
width: 100vw;
height: calc(100vh - 64px);
background: #F1F2F6;
position: fixed;
bottom: 0;
}
.hidden-icon {
position: absolute;
top: 0;
z-index: -100;
left: 0;
visibility: hidden;
}
.del-icon {
width: 16px;
transform: translate(-8px, -8px);
user-select: none;
}
</style>
mathUtils.js
import * as turf from "@/utils/turf.es";
let adsorptionDistance = 6
const mathUtils = {
// 计算点到线垂点的方法
calculateVerticalPoint(arr) {
const point = arr[2]
var x1 = arr[0][0];
var y1 = arr[0][1];
var x2 = arr[1][0];
var y2 = arr[1][1]
if (x1 == x2 && y1 == y2) {
return [point[0], point[1]];
}
var m = point[0];
var n = point[1];
var a = y2 - y1;
var b = x1 - x2;
var c = x2 * y1 - x1 * y2;
var x3 = (b * b * m - a * b * n - a * c) / (a * a + b * b);
var y3 = (a * a * n - a * b * m - b * c) / (a * a + b * b);
return [Math.round(x3 * 100) / 100, Math.round(y3 * 100) / 100];
},
// 根据垂点计算平行点
calculatePoint(vPoint, point, point2) {
const x = point[0] - vPoint[0] + point2[0]
const y = point[1] - vPoint[1] + point2[1]
return [x, y]
},
// 判断点是否在多边形内部或者线上
pointInPolygonORLine(point, polygon) {
var pt = turf.point(point);
var poly = turf.polygon([polygon]);
return turf.booleanPointInPolygon(pt, poly)
},
}
export default mathUtils;
drawMap.js
let ctxDom, mapCtx; //初始化必要参数
const drawMap = {
//初始化地图
initMap({
id,
w,
h
} = obj) {
ctxDom = id
id.width = w
id.height = h
mapCtx = id.getContext("2d");
},
//地图重绘
redrawMap() {
mapCtx.clearRect(0, 0, ctxDom.width, ctxDom.height);
},
//画圆
drawCircle({
x,
y,
r,
strokeStyle = '#1289ff80', //边框色
fillStyle = '#fff0', //填充色
} = obj) {
mapCtx.beginPath();
mapCtx.fillStyle = fillStyle;
mapCtx.setLineDash([]);
mapCtx.strokeStyle = strokeStyle
mapCtx.arc(x, y, r, 0, 2 * Math.PI);
mapCtx.closePath();
mapCtx.stroke();
mapCtx.fill();
},
drawRectangle(arr, isCheck) {
mapCtx.strokeStyle = isCheck ? '#1289ff' : '#1289ff80';
mapCtx.fillStyle = isCheck ? '#ffffff80' : '#fff0';
mapCtx.lineWidth = 2;
mapCtx.setLineDash([]);
mapCtx.lineJoin = 'bevel';
mapCtx.beginPath();
mapCtx.moveTo(arr[0][0], arr[0][1]);
for (let i = 1; i < arr.length; i++) {
mapCtx.lineTo(arr[i][0], arr[i][1]);
}
mapCtx.stroke();
mapCtx.fill();
},
}
export default drawMap
turf.es.js
请移步网盘或扫描下方二维码下载