import {Component, Input, OnInit} from '@angular/core'; import {Point} from '../../domain/point.domain'; import {PresetPoint} from '../../domain/presetPoint.domain'; @Component({ selector: 'work-canvas', templateUrl: './canvas.component.html', styleUrls: ['./canvas.component.less'] }) export class CanvasComponent implements OnInit { presetPointinfo: PresetPoint = new PresetPoint(); // 画图对象 width = 0; // canvas宽度 height = 0; // canvas高度 bgCxt = null; // bgCanvas 2D图形 graphCxt = null; // graphCanvas 2D图形 dlCxt = null; // dlCanvas 2D图形 labelCxt = null; // labelsCanvas 2D图形 bgImg = null; // 背景图片 imgScale = 1; // 缩放比例 imgPosition: Point = new Point(); // 点位 currentMousePosition: Point = new Point(); // 鼠标在图片的相对位置 carBoardLabels = []; // 标注区域 isActive: boolean; // 是否可活动 labelEndPoint: Point = new Point(); labelStartPoint: Point = new Point(); labelCarBoardPoints = []; // 点位 scaleDegree = 0.1; // 每次缩放的比例 lineWidth = 2; // 线宽度 isCanvas = false; isDrawing = false; constructor() { this.isActive = false; } @Input() set canvasDrawing(canvasDrawing) { this.isDrawing = canvasDrawing; } @Input() set presetPoint(presetPoint) { this.presetPointinfo = presetPoint; this.isActive = false; const img = new Image(); img.onload = (evt: any) => { this.isActive = true; this.bgImg = img; this.fitImg(); this.bgCxt.clearRect(0, 0, this.width, this.height); this.bgCxt.drawImage(img, this.imgPosition.X, this.imgPosition.Y, this.bgImg.width * this.imgScale, this.bgImg.height * this.imgScale); this.labelCxt.clearRect(0, 0, this.width, this.height); // 画图 this.drawLabelRects(); }; this.carBoardLabels = []; if (this.presetPointinfo?.DetectArgs._Items.length > 0) { for (const item of this.presetPointinfo.DetectArgs._Items) { for (const info of item.Region._Items) { info.X = this.toActualX(info.X); info.Y = this.toActualY(info.Y); } this.carBoardLabels.push(item.Region._Items); } } if (!!this.presetPointinfo?.ImgUrl) { img.src = this.presetPointinfo.ImgUrl; this.isCanvas = true; } } ngOnInit(): void { this.initCanvas(); } /** * 清空数据 */ clearn() { this.carBoardLabels = []; // this.bgCxt.clearRect(0, 0, this.width, this.height); this.graphCxt.clearRect(0, 0, this.width, this.height); this.dlCxt.clearRect(0, 0, this.width, this.height); this.labelCxt.clearRect(0, 0, this.width, this.height); } /** * 初始化canvas */ initCanvas(): void { this.width = document.getElementById('canvasBox').offsetWidth; this.height = document.getElementById('canvasBox').offsetHeight; const bgCanvas = document.getElementById('bgCanvas') as HTMLCanvasElement; const labelsCanvas = document.getElementById('labelsCanvas') as HTMLCanvasElement; const graphCanvas = document.getElementById('graphCanvas') as HTMLCanvasElement; const dlCanvas = document.getElementById('dlCanvas') as HTMLCanvasElement; bgCanvas.width = labelsCanvas.width = graphCanvas.width = dlCanvas.width = this.width; bgCanvas.height = labelsCanvas.height = graphCanvas.height = dlCanvas.height = this.height; this.bgCxt = bgCanvas.getContext('2d'); this.graphCxt = graphCanvas.getContext('2d'); this.dlCxt = dlCanvas.getContext('2d'); this.labelCxt = labelsCanvas.getContext('2d'); this.graphCxt.lineWidth = 2; this.graphCxt.strokeStyle = 'black'; } /** * 图片初始化 */ fitImg(): void { // 左右留白 const blankWidth = 20; const blankHeight = 20; // 图片范围 const width = this.width - blankWidth * 2; const height = this.height - blankHeight * 2; // 缩放比 const scale1 = (this.bgImg.width > width) ? (width / this.bgImg.width) : 1; const scale2 = (this.bgImg.height > height) ? (height / this.bgImg.height) : 1; // 取宽高缩放比的最小值 this.imgScale = Math.min(scale1, scale2); // 定位图片位置 this.imgPosition.X = blankWidth + (width - this.bgImg.width * this.imgScale) / 2; this.imgPosition.Y = blankHeight + (height - this.bgImg.height * this.imgScale) / 2; } /** * 画图 */ drawLabelRects(): void { this.labelCxt.clearRect(0, 0, this.width, this.height); for (const item of this.carBoardLabels) { this.drawLabelCarBoardRect(item); } } /** * 绘制 */ drawLabelCarBoardRect(labelCarBoardPointsInfo): void { this.labelCxt.beginPath(); this.labelCxt.lineWidth = 3; this.labelCxt.strokeStyle = 'green'; this.labelCxt.moveTo(labelCarBoardPointsInfo[0].X, labelCarBoardPointsInfo[0].Y); for (let i = 1; i < labelCarBoardPointsInfo.length; i++) { this.labelCxt.lineTo(labelCarBoardPointsInfo[i].X, labelCarBoardPointsInfo[i].Y); } this.labelCxt.lineTo(labelCarBoardPointsInfo[0].X, labelCarBoardPointsInfo[0].Y); this.labelCxt.stroke(); this.labelCxt.closePath(); // 标注框上方编号 this.labelCxt.fillStyle = 'red'; const total = labelCarBoardPointsInfo.length; let lat = 0; let lon = 0; for (const item of labelCarBoardPointsInfo) { lat += item.X * Math.PI / 180; lon += item.Y * Math.PI / 180; } lat /= total; lon /= total; const X = lat * 180 / Math.PI; const Y = lon * 180 / Math.PI; this.labelCxt.fillRect(X, Y, 50, 30); this.labelCxt.save(); this.labelCxt.textAlign = 'center'; this.labelCxt.fillStyle = 'black'; this.labelCxt.font = '20px Arial'; this.labelCxt.fillText(this.carBoardLabels.length, X + 50 / 2, Y + 20); this.labelCxt.restore(); this.labelCxt.closePath(); } /** * 屏蔽右键菜单 */ contextmenuBind(e): boolean { return false; } /** * 鼠标移动 */ onMouseMove(e): void { if (!this.isCanvas) { return; } if (!this.isDrawing) { return; } this.drawDotLine(e); this.drawIrregularRect(e); } /** * 连接图像 * @ param e */ drawIrregularRect(e): void { if (!this.isCanvas) { return; } if (!this.isDrawing) { return; } this.graphCxt.clearRect(0, 0, this.width, this.height); this.graphCxt.beginPath(); if (this.labelCarBoardPoints.length > 0) { this.graphCxt.moveTo(this.labelCarBoardPoints[0].X, this.labelCarBoardPoints[0].Y); for (let i = 1; i < this.labelCarBoardPoints.length; i++) { const p = this.labelCarBoardPoints[i]; this.graphCxt.lineTo(p.X, p.Y); } this.graphCxt.lineTo(this.currentMousePosition.X, this.currentMousePosition.Y); } this.graphCxt.stroke(); this.graphCxt.closePath(); } /** * 鼠标点击 */ onMouseClick(): void { if (!this.isCanvas) { return; } if (!this.isDrawing) { return; } const clickPoint = new Point(); clickPoint.X = this.currentMousePosition.X; clickPoint.Y = this.currentMousePosition.Y; this.labelCarBoardPoints.push(clickPoint); } /** * 鼠标在图片位置 * @ param e */ drawDotLine(e): void { if (!!e) { this.currentMousePosition.X = e.offsetX; this.currentMousePosition.Y = e.offsetY; } if (!!!this.bgImg) { return; } if (this.currentMousePosition.X < this.imgPosition.X) { this.currentMousePosition.X = this.imgPosition.X; } if (this.currentMousePosition.Y < this.imgPosition.Y) { this.currentMousePosition.Y = this.imgPosition.Y; } if (this.currentMousePosition.X > this.toActualX(this.bgImg.width)) { this.currentMousePosition.X = this.toActualX(this.bgImg.width); } if (this.currentMousePosition.Y > this.toActualY(this.bgImg.height)) { this.currentMousePosition.Y = this.toActualY(this.bgImg.height); } this.dlCxt.clearRect(0, 0, this.width, this.height); this.dlCxt.setLineDash([2, 1]); this.dlCxt.beginPath(); this.dlCxt.moveTo(0, this.currentMousePosition.Y); this.dlCxt.lineTo(this.width, this.currentMousePosition.Y); this.dlCxt.moveTo(this.currentMousePosition.X, 0); this.dlCxt.lineTo(this.currentMousePosition.X, this.height); this.dlCxt.stroke(); this.dlCxt.fillStyle = 'green'; this.dlCxt.font = '20px Arial'; this.dlCxt.closePath(); } /** * 滑动滚轮 */ onMousewheel(e): void { if (!this.isCanvas) { return; } if (!this.isDrawing) { return; } } /** * 缩放 * @ param mousePoint * @ param delta * @ constructor */ LabelImageManager(mousePoint, delta): void { this.imgScale *= 1 - delta * this.scaleDegree; this.imgPosition.X += (mousePoint.X - this.imgPosition.X) * delta * this.scaleDegree; this.imgPosition.Y += (mousePoint.Y - this.imgPosition.Y) * delta * this.scaleDegree; this.reDrawBgAndLabel(); this.reDrawDotLineAndTag(mousePoint); } /** * 重绘光标——虚线以及标签提示 * @param actualPoint 绘制位置 */ reDrawDotLineAndTag(actualPoint): void { if (!!!actualPoint) { return; } this.dlCxt.clearRect(0, 0, this.width, this.height); this.dlCxt.font = '20px 微软雅黑'; this.dlCxt.textAlign = 'left'; this.dlCxt.textBaseline = 'top'; // 阴影 this.dlCxt.fillStyle = 'black'; this.dlCxt.fillText('', actualPoint.X + 16, actualPoint.Y + 6); // 绘制标签提示 this.dlCxt.setLineDash([2, 1]); this.dlCxt.lineWidth = 1; this.dlCxt.strokeStyle = 'black'; this.dlCxt.beginPath(); } /** * 清空,画图 */ reDrawBgAndLabel(): void { this.bgCxt.clearRect(0, 0, this.width, this.height); this.drawBackground(); this.graphCxt.clearRect(0, 0, this.width, this.height); this.drawLabelRects(); } /** * 绘制背景 */ drawBackground() { if (!this.bgImg) { return; } this.bgCxt.clearRect(0, 0, this.width, this.height); this.bgCxt.drawImage(this.bgImg, 0, 0, this.bgImg.width, this.bgImg.height, this.imgPosition.X, this.imgPosition.Y, this.bgImg.width * this.imgScale, this.bgImg.height * this.imgScale); } /** * 鼠标按下 */ onMousedown(evt): void { if (!this.isCanvas) { return; } if (!this.isDrawing) { return; } // 若是右键 if (evt.which === 3) { this.onRightMouseDown(); } this.labelStartPoint = new Point(); this.labelStartPoint.X = this.currentMousePosition.X; this.labelStartPoint.Y = this.currentMousePosition.Y; this.labelEndPoint = new Point(); this.labelEndPoint.X = this.currentMousePosition.X; this.labelEndPoint.Y = this.currentMousePosition.Y; } /** * 右键按下 */ onRightMouseDown() { if (this.labelCarBoardPoints.length >= 3) { this.ConnectPolygon(); } } /** * 连接点 * @ constructor */ ConnectPolygon() { if (this.labelCarBoardPoints.length === 0) { return; } const labelCarBoardPointsInfo = this.labelCarBoardPoints.concat(); this.carBoardLabels.push(labelCarBoardPointsInfo); this.labelCarBoardPoints = []; this.graphCxt.clearRect(0, 0, this.width, this.height); this.drawLabelCarBoardRect(labelCarBoardPointsInfo); } /** * 鼠标放开 */ onMouseUp(evt) { // if (!!!this.isActive) { // return; // } // const item = { // xMin: this.toImgX(Math.min(this.labelStartPoint.X, this.labelEndPoint.X)), // xMax: this.toImgX(Math.max(this.labelStartPoint.X, this.labelEndPoint.X)), // yMin: this.toImgY(Math.min(this.labelStartPoint.Y, this.labelEndPoint.Y)), // yMax: this.toImgY(Math.max(this.labelStartPoint.Y, this.labelEndPoint.Y)), // }; // this.isLabeling = false; // // if (util.isRightMouseEvent(evt)) { // this.removeLabel(this.checkLabels(evt)); // return; // } // // if (item.xMax - item.xMin < 30 && item.yMax - item.yMin < 30) { // const editLabel = this.checkLabels(evt); // return; // } // // this.labels.push(item); // this.setLabelsUpdated(); // // this.graphCxt.clearRect(0, 0, this.width, this.height); // this.drawLabelRect(item, this.labels.length); } /** * 将图片相对X转换成真实X * @ param imgX */ toActualX(imgX) { return Math.round(imgX * this.imgScale + this.imgPosition.X); } /** * 将图片相对Y转换成真实Y * @ param imgY */ toActualY(imgY) { return Math.round(imgY * this.imgScale + this.imgPosition.Y); } /** * 将真实X转换成图片相对X * @ param actualX */ toImgX(actualX) { return Math.round((actualX - this.imgPosition.X) / this.imgScale); } /** * 将真实Y转换成图片相对Y * @ param actualY */ toImgY(actualY) { return Math.round((actualY - this.imgPosition.Y) / this.imgScale); } /** * 删除点位 */ deletePoint(index): void { this.carBoardLabels.splice(index, 1); this.drawLabelRects(); } returnCarBoardLabels() { const arr = JSON.parse(JSON.stringify(this.carBoardLabels)); for (const item of arr) { item.push(item[0]); for (const info of item) { info.X = this.toImgX(info.X); info.Y = this.toImgY(info.Y); } } return arr; } }