多边形绘图,纯js实现(cv可用),可以自选绘图区域颜色,展示文字。绘图完成后可以拖拽多边形点位实现区域的修改(引用的话,创建html页面及可用,中间就用到了layui的弹出层,需要引用layui的js、css,官网直接可下,也可以自行修改,另一个是在线引用。

  • 上代码


Canvas绘图中只有一个元素-canvas,所以实现多边形的拖拽是非常麻烦的,方法1、只能判断你点击的地方为圆心,给个差不多的半径的圆的范围是否包含某个点,然后达成修改多边形。 方法2、就是判断离鼠标点击的地方距离最近的一个点

这是绘画好的多边形区域(canvas是底板,图片是给canvas的背景图片):

ios 画个多边形 用多边形画画_canvas


这是拖拽、拉伸点位实现的更改多边形区域:

ios 画个多边形 用多边形画画_javascript_02

上代码

绘画完区域后 Enter 是结束当前绘画事件(可更改成其他事件结束当前绘画),主要功能代码是在代码下部分showGraphical() ,都有注释。可能循环较多,因为后来部分功能不需要,有一些多余代码(可删)。
不懂的属性官网都有: canvas属性.

<html>

<head>
	<meta http-equiv="content-type" content="text/html;charset=utf-8">
	<title></title>
	<link rel="stylesheet" href="./layui/css/layui.css">
	<style>
		/*谷歌去除滚动条*/
		::-webkit-scrollbar {
			display: none;
		}

		html,
		body {
			/*隐藏滚动条,当IE下溢出,仍然可以滚动*/
			-ms-overflow-style: none;
			/*火狐下隐藏滚动条*/
			overflow: -moz-scrollbars-none;
		}

		.layui-btn {
			background-color: cornflowerblue;
			color: white;
			text-overflow: inherit;
		}

		#mop2 {
			margin-top: 15px;
		}
		/*更改颜色弹出层右上角关闭隐藏*/
		.layui-layer-setwin {
			display: none;
		}
		/*下方操作按钮*/
		#bottom {
			text-align: center;
			margin-top: 30px;
		}
	</style>
</head>
<body>
	<canvas id="canvas" onclick="" style='background-color: #F0F8FF' width="900" height="515"></canvas>
	<div id="bottom">
		<input type="button" class="layui-btn oder" value="清空" onclick="ClearCanvas()" />
		<input type="button" class="layui-btn oder" value="保存" onclick="Success()" />
		<input type="button" class="layui-btn oder" value="显示" onclick="showGraphical()" />
		<input type="button" class="layui-btn oder" value="隐藏" onclick="HideCanvas()" />
	</div>
	<div style="display: none;" id="mop2">
		<div class="layui-form-item">
			<label class="layui-form-label">标签:</label>
			<div class="layui-input-inline">
				<select class="layui-input" name="biaoqian" id="biaoqian">
					<option value="">请选择标签</option>
					<option value="测试标签">我是一个标签</option>
				</select>
			</div>
		</div>
		<div class="layui-form-item">
			<label class="layui-form-label">选择颜色</label>
			<div class="layui-input-inline">
				<select class="layui-input" name="yanse" id="yanse" onchange="changeColor(this.value)">
					<option value="">请选择区域颜色</option>
					<option value="黑">黑</option>
					<option value="灰">灰</option>
					<option value="白">白</option>
					<option value="红">红</option>
					<option value="橙">橙</option>
					<option value="黄">黄</option>
					<option value="绿">绿</option>
					<option value="青">青</option>
					<option value="蓝">蓝</option>
					<option value="紫">紫</option>
				</select>
			</div>
		</div>
		<div class="layui-form-item">
			<div class="layui-input-block">
				<input type="button" onclick="UnitOut()" style="width: 9.9vw;" value="保存" class="layui-btn">
			</div>
		</div>
	</div>
</body>

</html>
<script src="./layui/layui.js"></script>
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
<!--方案1-->
<script>
	var Unocolor = null;//选择的颜色
	$(function () {
		bindImg();
		//拾色器渲染
		layui.use('form', function () {
			var form = layui.form;
			//常规使用
			form.render();
		});
	})
	function changeColor(e) {
		Unocolor = e;
	}
	//绑定区域图片
	function bindImg() {
        $("#canvas").css("background", `url(/images/11.png) center center no-repeat`)
		$("#canvas").css("background-color", `#F0F8FF`)
		//$.ajax({
		//    url: 接口,
		//    data: 值,
		//    type: "post",
		//    success: function (res) {
		//        console.log(res);
		//        $("#canvas").css("background", `url(${res}) center center no-repeat`)
		//        $("#canvas").css("background-color", `#F0F8FF`)
		//        //$("#canvas").css("background-size", `cover`)
		//    }
		//})
	}

	const canvas = document.getElementById("canvas")
	const context = canvas.getContext("2d")
	const COLOR = "#1890ff"
	//线的粗细
	const LINEWIDTH = 2
	//圆点半径
	const DOTRADIUS = 3

	//点颜色
	context.fillStyle = COLOR
	//线颜色
	context.strokeStyle = COLOR

	context.lineWidth = LINEWIDTH
	//单个实时获取图形的值
	let points = []
	//获取到所有图形值的组
	let ListPoints = [];
	//是否继续操作
	let isDrawing = false
	//是否删除数组末尾最后一个
	let isPointAdd = false
	//(全局)创建的图形的索引
	var GraphicIndex = 0;

	//计算位置
	function windowToCanvas(x, y) {
		var bbox = canvas.getBoundingClientRect();
		return {
			x: x - bbox.left * (canvas.width / bbox.width),
			y: y - bbox.top * (canvas.height / bbox.height)
		};
	}

	//绘制圆点
	function drawDot(loc) {
		context.beginPath()
		context.arc(loc.x, loc.y, DOTRADIUS * 2, 0, 2 * Math.PI);
		context.fill()
	}

	//绘制线
	function drawLine(locStart, locEnd) {
		context.beginPath()
		context.moveTo(locStart.x, locStart.y);
		context.lineTo(locEnd.x, locEnd.y);
		context.stroke();
	}

	//绘制图案
	function drawPolygon(points) {
		context.clearRect(0, 0, canvas.width, canvas.height);
		const length = points.length
		for (let i = 0; i < parseInt(length / 2); i++) {
			drawDot(points[i * 2])
			drawLine(points[i * 2], points[i * 2 + 1])
		}
		drawDot(points[length - 1])
		//以圆点为中心划分区域
		drawLine(points[length - 1], points[0])
	}

	//触发左键
	canvas.addEventListener("mousedown", e => {
		isDrawing = true;
		for (var i = 0; i < ListPoints.length; i++) {
			var singlePoints = ListPoints[i];
			for (var j = 0; j < singlePoints.length; j++) {
				if (singlePoints[j].show) {
					var dis = Math.sqrt((e.clientX - singlePoints[j].x) * (e.clientX - singlePoints[j].x) + (e.clientY - singlePoints[j].y) * (e.clientY - singlePoints[j].y));//Math.sqrt()求平方跟 为了判断鼠标点击后以鼠标点击的位置为中心画个圆,圆内是否包含的有显示的点,有的话就移动
					if (dis <= 20) {
						singlePoints[j]['update'] = true;//要修改的点位加上update=true
						//IsClear = true;
					}
					isDrawing = false
				}
			}
		}
		if (isDrawing) {
			const loc = windowToCanvas(e.clientX, e.clientY)
			points.push(loc)
			if (points.length > 1) {
				if (points[points.length - 1].x == points[points.length - 3].x && points[points.length - 1].y == points[points.length - 3].y) {
					points.pop();//防止用户在同一坐标点多次点击导致的bug,如果相同,就移除数组的最后一项坐标
				}
				else {
					drawPolygon(points)
				}
			}
			else {
				drawPolygon(points)
			}
			isDrawing = true
			isPointAdd = true
		}
	})

	//鼠标松开事件
	canvas.addEventListener("mouseup", e => {
		var istrue = false;
		for (var i = 0; i < ListPoints.length; i++) {
			var singlePoints = ListPoints[i];
			for (var j = 0; j < singlePoints.length; j++) {
				if (singlePoints[j].update) {
					singlePoints[j].x = e.clientX;
					singlePoints[j].y = e.clientY;
					istrue = true;
				}
			}
			if (istrue) {
				showGraphical();
				for (var j = 0; j < singlePoints.length; j++) {
					if (singlePoints[j].update) {
						delete singlePoints[j].update;
					}
				}
			}
		}

	})

	//移动触发
	canvas.addEventListener("mousemove", e => {
		if (isDrawing) {
			const loc = windowToCanvas(e.clientX, e.clientY)
			if (!isPointAdd) {
				points.pop()
			}
			points.push(loc)
			isPointAdd = false
			if (points.length > 2) {
				if (points[points.length - 1].x == points[points.length - 3].x && points[points.length - 1].y == points[points.length - 3].y) {
					points.pop();//防止用户在同一坐标点多次点击导致的bug,如果相同,就移除数组的最后一项坐标
				}
				else {
					drawPolygon(points)
				}
			}
			else {
				drawPolygon(points)
			}
		}
		else {
			var istrue = false;
			for (var i = 0; i < ListPoints.length; i++) {
				var singlePoints = ListPoints[i];
				for (var j = 0; j < singlePoints.length; j++) {
					if (singlePoints[j].update) {
						singlePoints[j].x = e.clientX;
						singlePoints[j].y = e.clientY;
						istrue = true;
					}
				}
				if (istrue) {
					showGraphical();
				}
			}
		}
	})

	//Enter事件监听
	$(document).keydown(function (event) {
		console.log("当前绘画区域的点位信息:");
		console.log(points)
		if (event.keyCode == 13) {
			if (isDrawing) {
				updateColor()

			}
		}
		else if (event.keyCode == 3) {
			console.log(2222222)
		}
	});

	//鼠标右键事件监听
	$("#canvas").oncontextmenu = function (e) {
		e.preventDefault();
		// 执行代码块
		console.log(158585858)
		console.log(this)
	}

	//标签更改保存事件
	function UnitOut() {
		layui.use('layer', function () {
			var layer = layui.layer;
			var bq = $("#biaoqian option:selected").val()
			var ys = $("#yanse option:selected").val()
			if (bq == "" || ys == "") {
				layer.msg("请全部完成后再试!", { icon: 5 })
				return false;
			}
			if (bq.length > 0 && Unocolor.length > 0) {
				drawPolygon(points)
				bindColorRGB(Unocolor)
				for (var i = 0; i < points.length; i++) {
					spotAdd = points[i];
					spotAdd['Index'] = GraphicIndex;
					spotAdd['show'] = false;
					spotAdd['color'] = Unocolor;
					spotAdd['lable'] = $("#biaoqian option:selected").val();
					spotAdd['min'] = min;
					spotAdd['max'] = max;
				}
				GraphicIndex++;
				ListPoints.push(points)
				var arrResult = [];
				for (var m = 0; m < ListPoints.length; m++) {
					去除重复点
					var arrTemp = [];
					var sum = 0;
					var singlePoints = ListPoints[m];
					for (var i = 0; i < singlePoints.length; i++) {
						if (i < singlePoints.length - 1 && singlePoints.length != 1) {
							if (singlePoints[i].x != singlePoints[i + 1].x || singlePoints[i].y != singlePoints[i + 1].y) {
								sum = 0;
							}
						}
						else {
							sum = 0;
						}
						for (var j = 0; j < singlePoints.length; j++) {
							if (singlePoints[i].x == singlePoints[j].x && singlePoints[i].y == singlePoints[j].y) {
								sum++;
							}
						}
						if (sum <= 2) {
							arrTemp.push(singlePoints[i])
						}
					}
					arrResult.push(arrTemp)

				}
				ListPoints = arrResult;

				points = [];
				isDrawing = false;
				isPointAdd = false;




				layer.close(indexLayui);
			}
			else {
				layer.msg("请全部完成后再试!", { icon: 5 })
			}
		})
	}

	var min = "", max = "", colorRGB = "";
	//颜色最终赋值
	function bindColorRGB(colorval) {
		switch (colorval) {
			case '黑': min = "0,0,0"; max = "180,255,46"; colorRGB = "RGB(0, 0, 0, 0.3)";
				break;
			case '灰': min = "0,0,46"; max = "180,43,220"; colorRGB = "RGB(163, 155, 153, 0.3)";
				break;
			case '白': min = "0,0,221"; max = "180,30,255"; colorRGB = "RGB(255, 255, 255, 0.3)";
				break;
			case '红': min = "0,43,46"; max = "10,255,255"; colorRGB = "RGB(232, 28, 28, 0.3)";
				break;
			case '橙': min = "11,43,46"; max = "25,255,255"; colorRGB = "RGB(237, 160, 59, 0.3)";
				break;
			case '黄': min = "26,43,46"; max = "34,255,255"; colorRGB = "RGB(255, 255, 0, 0.3)";
				break;
			case '绿': min = "35,43,46"; max = "77,255,255"; colorRGB = "RGB(28, 230, 31, 0.3)";
				break;
			case '青': min = "78,43,46"; max = "99,255,255"; colorRGB = "RGB(18, 206, 235, 0.3)";
				break;
			case '蓝': min = "100,43,46"; max = "124,255,255"; colorRGB = "RGB(17, 58, 242, 0.3)";
				break;
			case '紫': min = "125,43,46"; max = "155,255,255"; colorRGB = "RGB(87, 5, 252, 0.3)";
				break;
		}
	}

	//修改颜色
	var indexLayui = null;
	function updateColor() {
		layui.use('layer', function () {
			var layer = layui.layer;
			indexLayui = layer.open({
				type: 1,
				title: '添加标签',
				area: ['380px', '250px'],
				content: $("#mop2"),
				end: function () {
					$("#mop2").hide();
				},
			});
		});
	}

	//显示区域
	var xcenter = 0;//中心x轴
	var ycenter = 0;//中心y轴
	function showGraphical() {
		context.clearRect(0, 0, 900, 515);//清空整个画布 900  515 画布宽高
		console.log("展示中图形的点位信息:")
		console.log(ListPoints)
		for (var i = 0; i < ListPoints.length; i++) {
			//SingleGraphicShow(ListPoints[i]);
			var SingleArrPoint = ListPoints[i];
			//var temparr = [{ x: 1, y: 6 }, { x: 9, y: 6 }, { x: 2, y: 1 }, { x: 5, y: 9 }, {x:1,y:6}]
			SingleArea(SingleArrPoint);
			xcenter = 0;//中心x轴
			ycenter = 0;//中心y轴
			var cc = document.getElementById("canvas");
			var canvasNew = cc.getContext("2d");
			canvasNew.beginPath();
			//点色
			//canvasNew.fillStyle = ""
			canvasNew.lineWidth = 2  //线的粗细
			canvasNew.beginPath();  //开始

			var lableTemp = "";
			for (var j = 0; j < SingleArrPoint.length; j++) {
				if (j == 0) {
					canvasNew.moveTo(SingleArrPoint[0].x, SingleArrPoint[0].y);//起始结束点
				}
				else {
					canvasNew.lineTo(SingleArrPoint[j].x, SingleArrPoint[j].y);
				}
				xcenter += SingleArrPoint[j].x;
				ycenter += SingleArrPoint[j].y;
				SingleArrPoint[j].show = true;
				lableTemp = SingleArrPoint[j].lable;
				bindColorRGB(SingleArrPoint[j].color)
			}
			canvasNew.closePath();
			canvasNew.strokeStyle = colorRGB; //绘制好后线的颜色
			canvasNew.stroke();  // 进行绘制绘图
			canvasNew.fillStyle = colorRGB;//填充的颜色和透明度
			canvasNew.fill();//填充颜色

			canvasNew.font = "20px Georgia";//字体样式
			canvasNew.fillStyle = "white";//填充的颜色和透明度
			canvasNew.fillText(lableTemp, xcenter / SingleArrPoint.length, ycenter / SingleArrPoint.length);//文字内容和位置
		}
	}

	//清空画布包括之前的点位(初始化画布)
	function ClearCanvas() {
		context.clearRect(0, 0, 900, 515);//清空整个画布 900  515 画布宽高
		ListPoints = [];
		points = [];
		const COLOR = "#1890ff"
		//线的粗细
		const LINEWIDTH = 2
		//圆点半径
		const DOTRADIUS = 3

		//点颜色
		context.fillStyle = COLOR
		//线颜色
		context.strokeStyle = COLOR

		context.lineWidth = LINEWIDTH
	}

	//隐藏画布图形(只是隐藏图形未清空数组点位)
	function HideCanvas() {
		context.clearRect(0, 0, 900, 515);//清空整个画布 900  515 画布宽高
		for (var i = 0; i < ListPoints.length; i++) {
			var SingleArrPoint = ListPoints[i];
			for (var j = 0; j < SingleArrPoint.length; j++) {
				SingleArrPoint[j].show = false;
			}
		}
		const COLOR = "#1890ff"
		//线的粗细
		const LINEWIDTH = 2
		//圆点半径
		const DOTRADIUS = 3

		//点颜色
		context.fillStyle = COLOR
		//线颜色
		context.strokeStyle = COLOR

		context.lineWidth = LINEWIDTH
	}

	//封装显示单个图形(可循环调用)
	function SingleGraphicShow(SingleArrPoint) {
		context.clearRect(0, 0, 900, 515);//清空整个画布 900  515 画布宽高
		for (var i = 0; i < length; i++) {
			var SingleTemp = ListPoints[i];
			for (var j = 0; j < SingleTemp.length; j++) {
				SingleArrPoint[j].show = false;
			}
		}//清空所有数组需将数组内的show(是否在显示)属性设置为false
		xcenter = 0;//中心x轴
		ycenter = 0;//中心y轴
		var cc = document.getElementById("canvas");
		var canvasNew = cc.getContext("2d");
		canvasNew.beginPath();
		//点色
		//canvasNew.fillStyle = ""
		//线色

		canvasNew.lineWidth = 2  //线的粗细
		canvasNew.beginPath();  //开始


		for (var j = 0; j < SingleArrPoint.length; j++) {
			if (j == 0) {
				canvasNew.moveTo(SingleArrPoint[0].x, SingleArrPoint[0].y);//起始结束点
			} else {
				canvasNew.lineTo(SingleArrPoint[j].x, SingleArrPoint[j].y);
			}
			xcenter += SingleArrPoint[j].x;
			ycenter += SingleArrPoint[j].y;
			SingleArrPoint[j].show = true;
		}
		canvasNew.closePath();
		canvasNew.strokeStyle = "#DC143C"; //绘制好后线的颜色
		canvasNew.stroke();  // 进行绘制绘图

		canvasNew.fillStyle = "rgb(220,20,60,0.3)";//填充的颜色和透明度
		canvasNew.fill();//填充颜色

		canvasNew.font = "20px Georgia";//字体样式
		canvasNew.fillStyle = "black";//填充的颜色和透明度
		canvasNew.fillText("China!", xcenter / SingleArrPoint.length, ycenter / SingleArrPoint.length);//文字内容和位置
	}

	//将数据转换成最终后台需要的上传格式
	var resultPoints = [];
	function ChangeValue() {
		for (var i = 0; i < ListPoints.length; i++) {
			var SingleArrPoint = ListPoints[i];
			var arrx = [],
				arry = [],
				name = "",
				color = "",
				max = "",
				min = "";
			for (var j = 0; j < SingleArrPoint.length; j++) {
				arrx.push(SingleArrPoint[j].x);
				arry.push(SingleArrPoint[j].y);
				name = SingleArrPoint[j].lable;
				color = SingleArrPoint[j].color;
				max = SingleArrPoint[j].max;
				min = SingleArrPoint[j].min;
			}
			resultPoints.push({ x: arrx, y: arry, name: name, color: color, max: max, min: min });
		}
		console.log(resultPoints);
	}

	//求单个不规则图形的面积  公式: sum+=0.5×数组[i].x(数组[i+1].y-数组[i+1].y)             i:数组的索引下标 x,y为横纵坐标
	function SingleArea(SingleArrPoint) {
		var sum = 0;
		for (var i = 0; i < SingleArrPoint.length; i++) {
			if (i == 0) {
				sum += SingleArrPoint[i].x * (SingleArrPoint[i + 1].y - SingleArrPoint[SingleArrPoint.length - 1].y);
			}
			else if (i == SingleArrPoint.length - 1) {
				sum += SingleArrPoint[i].x * (SingleArrPoint[0].y - SingleArrPoint[i - 1].y);
			}
			else {
				sum += SingleArrPoint[i].x * (SingleArrPoint[i + 1].y - SingleArrPoint[i - 1].y)
			}
		}
		console.log("图形面积为:" + Math.abs(sum / 2));
	}

	//layui关闭弹出层
	function closeUser() {
		var index1 = parent.layer.getFrameIndex(window.name); //先得到当前iframe层的索引
		parent.layer.close(index1); //再执行关闭
	}


	//16进制转rgb
	//使用方法:
	//"#fff".colorRgb();  // rgb(255,255,255)
	//"#ffffff".colorRgb();  // rgb(255,255,255)
	String.prototype.colorRgb = function () {
		// 16进制颜色值的正则
		var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
		// 把颜色值变成小写
		var color = this.toLowerCase();
		if (reg.test(color)) {
			// 如果只有三位的值,需变成六位,如:#fff => #ffffff
			if (color.length === 4) {
				var colorNew = "#";
				for (var i = 1; i < 4; i += 1) {
					colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1));
				}
				color = colorNew;
			}
			// 处理六位的颜色值,转为RGB
			var colorChange = [];
			for (var i = 1; i < 7; i += 2) {
				colorChange.push(parseInt("0x" + color.slice(i, i + 2)));
			}
			return "RGB(" + colorChange.join(",") + ",0.3)";
		} else {
			return color;
		}
	};
</script>

ios 画个多边形 用多边形画画_canvas_03


注:此功能最好放到一个跳转页面,因为其他样式可能会导致canvas的 x,y的坐标偏移,导致绘画好区域后拖拽寻找点位偏移较大。

里面还有个计算绘制好的多边形面积的,因为功能后来不需要了,只做了简单图形的面积验证没问题,好像是只要没有绘制的线相交,面积就没问题。