- 首先,根据坐标画矩形需要定义一些属性:鼠标单击的位置,用来实时画线,已经点击坐标的所有位置,当前所有保存的数据
data() {
return {
type: 'rectangle', //当前可编辑图形的状态
mouseStartPos: [], //鼠标点击的位置
mouseClickArr: [], //当前已点击的坐标记录
drawAllData: [], //当前所有保存的数据
}
},
- 绘制矩形需要四个坐标,但是点选坐标只需要三个坐标就可以通过对称计算出其他点位的坐标数据,这里将算法封装为js导出使用,在utils文件夹下新建mathUtils.js文件
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]
},
}
export default mathUtils;
- 在页面中引入使用
import mathUtils from '@/utils/mathUtils.js';
- 接下来正式开始绘制矩形,首先在鼠标按下的时候记录一次鼠标的位置
//鼠标按下
mapMousedown(e) {
console.log('鼠标按下',e);
let x = e.offsetX
let y = e.offsetY
this.mouseStartPos = [e.offsetX, e.offsetY]
},
- 鼠标移动过程中调用重绘方法并传入实时坐标
//鼠标移动
mapMousemove(e) {
let x = e.offsetX
let y = e.offsetY
this.redrawMap({x,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]];
drawMap.drawRectangle(rectangleData);
},
- 最后在redrawMap方法重绘过程中调用实时画图形的方法就可以了
//实时的画各类图形
point && point.x && this.drawNowDrawing(point.x, point.y);
图例如下,会出现点选点位与实时点位之间的连线
- 接下来就要在点击地图事件中判断当前点击坐标中是否已经存在了三个点位,存在就将它保存进所有图形记录中
//单击地图
mapClick(e) {
console.log('单击地图',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.drawAllData = this.drawAllData.concat([this.mouseClickArr])
this.redrawMap()
this.mouseClickArr = []
}
break;
}
},
- 将所有矩形数据保存以后,在重绘过程中绘制已经保存的所有数据
//绘制已经保存的房间数据
if (this.drawAllData.length > 0) {
for (const [i, item] of this.drawAllData.entries()) {
this.drawRectangle(item);
}
}
图例如下,成品效果:
完整代码如下,复制可用
首页:
<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: [], //当前所有保存的数据
}
},
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) {
console.log('单击地图',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.drawAllData = this.drawAllData.concat([this.mouseClickArr])
this.redrawMap()
this.mouseClickArr = []
}
break;
}
},
//鼠标按下
mapMousedown(e) {
console.log('鼠标按下',e);
let x = e.offsetX
let y = e.offsetY
this.mouseStartPos = [e.offsetX, e.offsetY]
},
//鼠标移动
mapMousemove(e) {
let x = e.offsetX
let y = e.offsetY
this.redrawMap({x,y})
},
//鼠标抬起
mapMouseUp(e) {
console.log('鼠标抬起',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()) {
this.drawRectangle(item);
}
}
//实时的画各类图形
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]];
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>
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) {
mapCtx.strokeStyle = '#1289ff80';
mapCtx.fillStyle = '#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
mathUtils.js
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]
},
}
export default mathUtils;