canvas标注 js
发表于:2020-10-24 12:51:14 分类:开发杂记 阅读:466次

function MarkImage(div, image, markData, styleConfig) { var POSITION_ABSOLUTE = 'absolute'; var config = { markColor: "#fff2aa", markFillColor: "rgba(0, 0, 0, 0.3)", textSize: 16, textColor: "#3482ff", selectedColor: "#FF0000", intoBorderColor: "rgba(200, 100, 100, 0.8)" }; var temCanvas = null; var tempContent = null; var tempPoints = []; var drawState = false; //当前选中的标签 this.selected = null; //当前移入的标签 this.moveInto = null; //画布宽度 this.width = 0; //画面高度 this.height = 0; //画面所在dom元素 this.element = div; //需要标注的底图 this.image = image; //实时标签数据 this.markArray = []; var scaling = 1; var idSequence = { startTime: new Date().getTime() }; var pushDownPoint = null; var pushDown = false; var pushUpPoint = null; function initCanvas(markImage) { if (!div || !image) { return false; } if (styleConfig) { if (styleConfig.markColor) { config.markColor = styleConfig.markColor; } if (styleConfig.textSize) { config.textSize = styleConfig.textSize; } if (styleConfig.textColor) { config.textColor = styleConfig.textColor; } } removeAllChild(div); const wh = Number(image.width) / Number(image.height); if (wh > 1) { scaling = div.offsetWidth / image.width; } else { scaling = div.offsetHeight / image.height; } markImage.width = image.width * scaling; markImage.height = image.height * scaling; var imageCanvas = document.createElement('canvas'); div.appendChild(imageCanvas); imageCanvas.id = 'MARK_IMAGE'; imageCanvas.width = markImage.width; imageCanvas.height = markImage.height; imageCanvas.style.position = POSITION_ABSOLUTE; var imageCanvasCtx = imageCanvas.getContext("2d"); imageCanvasCtx.drawImage(image, 0, 0, imageCanvas.width, imageCanvas.height); drawMarks(markImage); temCanvas = document.createElement('canvas'); div.appendChild(temCanvas); temCanvas.id = 'MARK_IMAGE_TEMP'; temCanvas.width = markImage.width; temCanvas.height = markImage.height; temCanvas.style.position = POSITION_ABSOLUTE; tempContent = temCanvas.getContext("2d"); bindEvent(markImage); return true; } function drawMarks(markImage) { if (markData) { for (var i = 0; i < markData.length; i++) { var data = markData[i]; var mark = new Object(); mark.markType = data.markType; mark.realName = data.realName; mark.name = data.name; mark.points = JSON.parse(data.markJson); markImage.drawMark(mark); } } } function bindEvent(markImage) { markImage.element.onclick = function (event) { if (event.layerX <= markImage.width && event.layerY <= markImage.height) { if (drawState) { if (pushDownPoint && pushUpPoint && (Math.abs(pushUpPoint.x - pushDownPoint.x) < 10 || Math.abs(pushUpPoint.y - pushDownPoint.y) < 10)) { tempPoints.push({ x: event.layerX, y: event.layerY }); drawTempMark(markImage); } } else { if (markImage.moveInto) { if (markImage.selected) { if (markImage.selected.id !== markImage.moveInto.id) { flashSelected(markImage, markImage.selected, false); markImage.selected = markImage.moveInto; flashSelected(markImage, markImage.selected, true); } } else { markImage.selected = markImage.moveInto; flashSelected(markImage, markImage.selected, true); } } else { if (markImage.selected) { flashSelected(markImage, markImage.selected, false); markImage.selected = null; } } } } } markImage.element.onmousemove = function (event) { markImage.element.style.cursor = 'default'; if (event.layerX <= markImage.width && event.layerY <= markImage.height) { if (!drawState) { var point = { x: event.layerX / scaling, y: event.layerY / scaling }; var mark = intoMark(markImage, point); if (mark) { markImage.element.style.cursor = 'pointer'; } else { if (markImage.moveInto) { markImage.moveInto = null; } } } else { if (pushDown) { drawTempRectangle({ x: event.layerX, y: event.layerY }); } } } } markImage.element.oncontextmenu = function (event) { if (event.button === 2) { event.preventDefault(); draw(markImage); } } markImage.element.onmousedown = function (event) { if (event.layerX <= markImage.width && event.layerY <= markImage.height) { if (drawState) { pushDownPoint = { x: event.layerX, y: event.layerY }; pushDown = true; } } } markImage.element.onmouseup = function (event) { if (event.layerX <= markImage.width && event.layerY <= markImage.height) { if (drawState) { pushUpPoint = { x: event.layerX, y: event.layerY }; pushDown = false; if (pushDownPoint && pushUpPoint && (Math.abs(pushUpPoint.x - pushDownPoint.x) >= 10 || Math.abs(pushUpPoint.y - pushDownPoint.y) >= 10)) { drawRectangle(markImage); } } } } } function draw(markImage) { if (tempPoints && tempPoints.length > 3) { var newMark = {}; newMark.realName = '请选择标注标签'; newMark.markType = '0'; newMark.points = []; for (var i = 0; i < tempPoints.length; i++) { newMark.points.push({ x: tempPoints[i].x / scaling, y: tempPoints[i].y / scaling }); } tempPoints = []; temCanvas.width = markImage.width; markImage.selectedMark(markImage.drawMark(newMark)); } } function drawRectangle(markImage) { tempPoints = []; tempPoints.push(pushDownPoint); tempPoints.push({ x: pushDownPoint.x, y: pushUpPoint.y }); tempPoints.push(pushUpPoint); tempPoints.push({ x: pushUpPoint.x, y: pushDownPoint.y }); temCanvas.width = markImage.width; draw(markImage); } function drawTempRectangle(point) { if (pushDownPoint) { if (point.x - pushDownPoint.x !== 0 && point.y - pushDownPoint.y !== 0) { temCanvas.width = temCanvas.width; tempContent.setLineDash([20, 5]); // [实线长度, 间隙长度] tempContent.lineDashOffset = -0; tempContent.strokeStyle = config.intoBorderColor; tempContent.strokeRect(pushDownPoint.x, pushDownPoint.y, (point.x - pushDownPoint.x), (point.y - pushDownPoint.y)); } } } function drawTempMark(markImage) { if (tempPoints && tempPoints.length > 0) { pushUpPoint = null; pushDownPoint = null; var p = tempPoints[tempPoints.length - 1]; if (tempPoints.length === 1) { tempContent.arc(p.x, p.y, 2, 0, 2 * Math.PI, false); tempContent.stroke(); } else { tempContent.moveTo(tempPoints[tempPoints.length - 2].x, tempPoints[tempPoints.length - 2].y); //设置起点状态 tempContent.lineTo(p.x, p.y); //设置末端状态 tempContent.stroke(); tempContent.arc(p.x, p.y, 2, 0, 2 * Math.PI, false); tempContent.stroke(); } } } function flashSelected(markImage, mark, selected) { mark.dataCanvas.width = markImage.width; mark.nameCanvas.width = markImage.width; var dataCanvasCtx = mark.dataCanvas.getContext("2d"); var nameCanvasCtx = mark.nameCanvas.getContext("2d"); nameCanvasCtx.font = config.textSize + "px 黑体"; if (selected) { dataCanvasCtx.strokeStyle = config.selectedColor; nameCanvasCtx.fillStyle = config.selectedColor; } else { dataCanvasCtx.strokeStyle = config.markColor; nameCanvasCtx.fillStyle = config.textColor; } if (mark.textPoint.y === mark.border[0].y) { nameCanvasCtx.fillText("名称:" + mark.realName, mark.textPoint.x * scaling, mark.textPoint.y * scaling + config.textSize); } else { nameCanvasCtx.fillText("名称:" + mark.realName, mark.textPoint.x * scaling, mark.textPoint.y * scaling); } if (mark.points) { for (var i = 0; i < mark.points.length; i++) { const point = mark.points[i]; if (i === 0) { dataCanvasCtx.beginPath(); dataCanvasCtx.moveTo(point.x * scaling, point.y * scaling); } else { dataCanvasCtx.lineTo(point.x * scaling, point.y * scaling); if (i === mark.points.length - 1) { dataCanvasCtx.closePath(); dataCanvasCtx.fillStyle = config.markFillColor; dataCanvasCtx.fill(); dataCanvasCtx.stroke(); } } } } } function intoMark(markImage, point) { for (var i = 0; i < markImage.markArray.length; i++) { var mark = markImage.markArray[i]; if (mark.border[0].x < point.x && point.x < mark.border[1].x && mark.border[0].y < point.y && point.y < mark.border[1].y) { if (!markImage.moveInto || markImage.moveInto.id !== mark.id) { markImage.moveInto = mark; drawBorder(mark); } return mark; } } clearBorder(markImage); return null; } function drawBorder(mark) { temCanvas.width = temCanvas.width; tempContent.setLineDash([20, 5]); // [实线长度, 间隙长度] tempContent.lineDashOffset = -0; tempContent.strokeStyle = config.intoBorderColor; tempContent.strokeRect(mark.border[0].x * scaling, mark.border[0].y * scaling, (mark.border[1].x - mark.border[0].x) * scaling, (mark.border[1].y - mark.border[0].y) * scaling); } function clearBorder(markImage) { if (markImage.moveInto) { temCanvas.width = temCanvas.width; } } //添加一个标注 this.drawMark = function (mark) { mark.id = genId(); this.markArray.push(mark); mark.dataCanvas = drawPoints(mark, this); if (mark.textPoint.y === mark.border[0].y) { mark.nameCanvas = drawText('MARK_NAME_' + mark.id, mark.realName, mark.textPoint.x * scaling, mark.textPoint.y * scaling + config.textSize, this); } else { mark.nameCanvas = drawText('MARK_NAME_' + mark.id, mark.realName, mark.textPoint.x * scaling, mark.textPoint.y * scaling, this); } return mark.id; } function drawPoints(mark, markImage) { var minX = -1, minY = -1, maxX = -1, maxY = -1; var dataCanvas = document.createElement('canvas'); div.appendChild(dataCanvas); dataCanvas.id = 'MARK_DATA_' + mark.id; dataCanvas.style.position = POSITION_ABSOLUTE; dataCanvas.width = markImage.width; dataCanvas.height = markImage.height; var dataCanvasCtx = dataCanvas.getContext("2d"); dataCanvasCtx.strokeStyle = config.markColor; if (mark.points) { var xArr = [],yArr = []; for (var i = 0; i < mark.points.length; i++) { const point = mark.points[i]; if(!xArr.includes(point.x)){ xArr.push(point.x); } if(!yArr.includes(point.y)){ yArr.push(point.y); } if (i === 0) { minX = point.x; maxX = point.x; minY = point.y; maxY = point.y; mark.textPoint = point; dataCanvasCtx.beginPath(); dataCanvasCtx.moveTo(point.x * scaling, point.y * scaling); } else { if (point.x < minX) { minX = point.x; mark.textPoint = point; } minY = point.y < minY ? point.y : minY; maxX = point.x > maxX ? point.x : maxX; maxY = point.y > maxY ? point.y : maxY; dataCanvasCtx.lineTo(point.x * scaling, point.y * scaling); if (i === mark.points.length - 1) { dataCanvasCtx.closePath(); dataCanvasCtx.fillStyle = config.markFillColor; dataCanvasCtx.fill(); dataCanvasCtx.stroke(); } } } if(mark.points.length===4&&xArr.length<=2&&yArr.length<=2){ mark.markType = "0"; }else{ mark.markType = "1"; } mark.border = [{ x: minX, y: minY }, { x: maxX, y: maxY }]; } return dataCanvas; } //设置绘制状态 true 画图状态 false 选择状态 this.setDrawState = function (state) { drawState = state; if (state) { tempPoints = []; temCanvas.width = this.width; if (this.selected) { flashSelected(this, this.selected, false); this.selected = null; } } } this.getDrawState = function () { return drawState; } //获取所有数据,返回格式同传入格式 this.getMarkerData = function () { var data = []; for (var i = 0; i < this.markArray.length; i++) { var mark = {}; mark.id = this.markArray[i].id; if(this.markArray[i].name){ mark.name = this.markArray[i].name; } mark.realName = this.markArray[i].realName; mark.markType = this.markArray[i].markType; mark.markJson = JSON.stringify(this.markArray[i].points); data.push(mark); } return data; } function drawText(id, name, x, y, markImage) { var nameCanvas = document.createElement('canvas'); div.appendChild(nameCanvas); nameCanvas.id = id; nameCanvas.style.position = POSITION_ABSOLUTE; nameCanvas.width = markImage.width; nameCanvas.height = markImage.height; var nameCanvasCtx = nameCanvas.getContext("2d"); nameCanvasCtx.font = config.textSize + "px 黑体"; nameCanvasCtx.fillStyle = config.textColor; nameCanvasCtx.fillText("名称:" + name, x, y); return nameCanvas; } //编辑指定标签 this.editMark = function (mark) { var findMark = this.findMark(mark.id); if (!findMark) { return false; } this.element.removeChild(findMark.nameCanvas); this.element.removeChild(findMark.dataCanvas); findMark.dataCanvas = drawPoints(mark, this); if (mark.textPoint.y === mark.border[0].y) { mark.nameCanvas = drawText('MARK_NAME_' + mark.id, mark.realName, mark.textPoint.x * scaling, mark.textPoint.y * scaling + config.textSize, this); } else { mark.nameCanvas = drawText('MARK_NAME_' + mark.id, mark.realName, mark.textPoint.x * scaling, mark.textPoint.y * scaling, this); } return true; } //移除所有 this.removeAll = function () { for (var i = 0; i < this.markArray.length; i++) { this.element.removeChild(this.markArray[i].nameCanvas); this.element.removeChild(this.markArray[i].dataCanvas); } this.markArray = []; } //根据ID移除标签 this.removeMark = function (target) { var id = null; if(typeof (target) === "string"){ id = target; }else{ if(target.id){ id = target.id; } } if(!id){ return null; } var findMark = null; for (var i = 0; i < this.markArray.length; i++) { if (this.markArray[i].id === id) { findMark = this.markArray[i]; this.markArray.splice(i, 1); break; } } if (findMark) { this.element.removeChild(findMark.nameCanvas); this.element.removeChild(findMark.dataCanvas); } return findMark; } //根据ID查找标签 this.findMark = function (id) { for (var i = 0; i < this.markArray.length; i++) { if (this.markArray[i].id === id) { return this.markArray[i]; } } return null; } //根据ID选中标签 this.selectedMark = function (id) { var findMark = this.findMark(id); if (findMark) { if (this.selected) { flashSelected(this, this.selected, false); } this.selected = findMark; flashSelected(this, this.selected, true); } } function genId() { var now = new Date().getTime(); var timeDifference = now - idSequence.startTime; if (idSequence.time && idSequence.time === timeDifference) { idSequence.sequence = idSequence.sequence + 1; } else { idSequence.time = timeDifference; idSequence.sequence = 0; } return genIdString(idSequence.time + "", idSequence.sequence + "") } function genIdString(a, b) { var post = b.padStart(3, "0"); return a + post; } function removeAllChild(div) { while (div.hasChildNodes()) { div.removeChild(div.firstChild); } } initCanvas(this); }
关键词:js,canvas,图片标注