天天看點

canvas根據坐标點位畫圖形-canvas畫矩形

  1. 首先,根據坐标畫矩形需要定義一些屬性:滑鼠單擊的位置,用來實時畫線,已經點選坐标的所有位置,目前所有儲存的資料
    canvas根據坐标點位畫圖形-canvas畫矩形
data() {
	return {
		type: 'rectangle', //目前可編輯圖形的狀态
		mouseStartPos: [], //滑鼠點選的位置
		mouseClickArr: [], //目前已點選的坐标記錄
		drawAllData: [], //目前所有儲存的資料
	}
},
           
  1. 繪制矩形需要四個坐标,但是點選坐标隻需要三個坐标就可以通過對稱計算出其他點位的坐标資料,這裡将算法封裝為js導出使用,在utils檔案夾下建立mathUtils.js檔案
    canvas根據坐标點位畫圖形-canvas畫矩形
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. 在頁面中引入使用
  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);
           

圖例如下,會出現點選點位與實時點位之間的連線

canvas根據坐标點位畫圖形-canvas畫矩形
  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);
	}
}
           

圖例如下,成品效果:

canvas根據坐标點位畫圖形-canvas畫矩形

完整代碼如下,複制可用

首頁:

<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;