canvas根据坐标点位画图形-canvas画矩形

  1. 首先,根据坐标画矩形需要定义一些属性:鼠标单击的位置,用来实时画线,已经点击坐标的所有位置,当前所有保存的数据
    在这里插入图片描述
data() {
	return {
		type: 'rectangle', //当前可编辑图形的状态
		mouseStartPos: [], //鼠标点击的位置
		mouseClickArr: [], //当前已点击的坐标记录
		drawAllData: [], //当前所有保存的数据
	}
},
  1. 绘制矩形需要四个坐标,但是点选坐标只需要三个坐标就可以通过对称计算出其他点位的坐标数据,这里将算法封装为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;
  1. 在页面中引入使用
import mathUtils from '@/utils/mathUtils.js';
  1. 接下来正式开始绘制矩形,首先在鼠标按下的时候记录一次鼠标的位置
//鼠标按下
mapMousedown(e) {
	console.log('鼠标按下',e);
	let x = e.offsetX
	let y = e.offsetY
	
	this.mouseStartPos = [e.offsetX, e.offsetY]
},
  1. 鼠标移动过程中调用重绘方法并传入实时坐标
//鼠标移动
mapMousemove(e) {
	let x = e.offsetX
	let y = e.offsetY
	
	this.redrawMap({x,y})
},
  1. 通过记录鼠标点击的记录与鼠标的实时坐标,实现一个实时画线的功能
//实时画图形
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;
	}
},
  1. 在实时画的的时候需要调用计算矩形坐标的方法
//画矩形
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);
},
  1. 最后在redrawMap方法重绘过程中调用实时画图形的方法就可以了
//实时的画各类图形
point && point.x && this.drawNowDrawing(point.x, point.y);

图例如下,会出现点选点位与实时点位之间的连线
在这里插入图片描述

  1. 接下来就要在点击地图事件中判断当前点击坐标中是否已经存在了三个点位,存在就将它保存进所有图形记录中
//单击地图
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;
	}
},
  1. 将所有矩形数据保存以后,在重绘过程中绘制已经保存的所有数据
//绘制已经保存的房间数据
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;

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值