需求css
對應方案node
<template> <div> <canvas :id="radom" :class="{canDraw: 'canvas'}" :width="width" :height="height" :style="{'width':`${width}px`,'height':`${height}px`}" @mousedown="canvasDown($event)" @mouseup="canvasUp($event)" @mousemove="canvasMove($event)" @touchstart="canvasDown($event)" @touchend="canvasUp($event)" @touchmove="canvasMove($event)"> </canvas> </div> </template> <script> // import proxy from './proxy.js' const uuid = require('node-uuid') export default { props: { canDraw: { // 圖片路徑 type: Boolean, default: true }, url: { // 圖片路徑 type: String }, info: { // 位置點信息 type: Array }, width: { // 繪圖區域寬度 type: String }, height: { // 繪圖區域高度 type: String }, lineColor: { // 畫筆顏色 type: String, default: 'red' }, lineWidth: { // 畫筆寬度 type: Number, default: 2 }, lineType: { // 畫筆類型 type: String, default: 'circle' } }, watch: { info (val) { if (val) { this.initDraw() } } }, data () { return { // 同一頁面屢次渲染時,用於區分元素的id radom: uuid.v4(), // canvas對象 context: {}, // 是否處於繪製狀態 canvasMoveUse: false, // 繪製矩形和橢圓時用來保存起始點信息 beginRec: { x: '', y: '', imageData: '' }, // 儲存座標信息 drawInfo: [], // 背景圖片緩存 img: new Image() } }, mounted () { this.initDraw() }, methods: { // 初始化繪製信息 initDraw () { // 初始化畫布 const canvas = document.getElementById(this.radom) this.context = canvas.getContext('2d') // 初始化背景圖片 this.img.setAttribute('crossOrigin', 'Anonymous') this.img.src = this.url this.img.onerror = () => { var timeStamp = +new Date() this.img.src = this.url + '?' + timeStamp } this.img.onload = () => { this.clean() } // proxy.getBase64({imgUrl: this.url}).then((res) => { // if (res.code * 1 === 0) { // this.img.src = 'data:image/jpeg;base64,'+res.data // this.img.onload = () => { // this.clean() // } // } // }) // 初始化畫筆 this.context.lineWidth = this.lineWidth this.context.strokeStyle = this.lineColor }, // 鼠標按下 canvasDown (e) { if (this.canDraw) { this.canvasMoveUse = true // client是基於整個頁面的座標,offset是cavas距離pictureDetail頂部以及左邊的距離 const canvasX = e.clientX - e.target.parentNode.offsetLeft const canvasY = e.clientY - e.target.parentNode.offsetTop // 記錄起始點和起始狀態 this.beginRec.x = canvasX this.beginRec.y = canvasY this.beginRec.imageData = this.context.getImageData(0, 0, this.width, this.height) // 存儲本次繪製座標信息 this.drawInfo.push({ x: canvasX / this.width, y: canvasY / this.height, type: this.lineType }) } }, Area (p0,p1,p2) { let area = 0.0 ; area = p0.x * p1.y + p1.x * p2.y + p2.x * p0.y - p1.x * p0.y - p2.x * p1.y - p0.x * p2.y; return area / 2 ; }, // 計算多邊形質心 getPolygonAreaCenter (points) { let sum_x = 0; let sum_y = 0; let sum_area = 0; let p1 = points[1]; for (var i = 2; i < points.length; i++) { let p2 = points[i]; let area = this.Area(points[0],p1,p2) ; sum_area += area ; sum_x += (points[0].x + p1.x + p2.x) * area; sum_y += (points[0].y + p1.y + p2.y) * area; p1 = p2 ; } return { x: sum_x / sum_area / 3, y: sum_y / sum_area / 3 } }, // 根據座標信息繪製圖形 drawWithInfo () { this.info.forEach(item => { this.context.beginPath() if (!item.type) { // 設置顏色 this.context.strokeStyle = item.regionColor this.context.fillStyle = item.regionColor // 繪製多邊形的邊 if (typeof item.region === 'string') { item.region = JSON.parse(item.region) } item.region.forEach(point => { this.context.lineTo(point.x * this.width, point.y * this.height) }) this.context.closePath() // 在多邊形質心標註文字 let point = this.getPolygonAreaCenter(item.region) this.context.fillText(item.areaName, point.x * this.width, point.y * this.height) } else if (item.type === 'rec') { this.context.rect(item.x * this.width, item.y * this.height, item.w * this.width, item.h * this.height) } else if (item.type === 'circle') { this.drawEllipse(this.context, (item.x + item.a) * this.width, (item.y + item.b) * this.height, item.a > 0 ? item.a * this.width : -item.a * this.width, item.b > 0 ? item.b * this.height : -item.b * this.height) } this.context.stroke() }) }, // 鼠標移動時繪製 canvasMove (e) { if (this.canvasMoveUse && this.canDraw) { // client是基於整個頁面的座標,offset是cavas距離pictureDetail頂部以及左邊的距離 let canvasX = e.clientX - e.target.parentNode.offsetLeft let canvasY = e.clientY - e.target.parentNode.offsetTop if (this.lineType === 'rec') { // 繪製矩形時恢復起始點狀態再從新繪製 this.context.putImageData(this.beginRec.imageData, 0, 0) this.context.beginPath() this.context.rect(this.beginRec.x, this.beginRec.y, canvasX - this.beginRec.x, canvasY - this.beginRec.y) let info = this.drawInfo[this.drawInfo.length - 1] info.w = canvasX / this.width - info.x info.h = canvasY / this.height - info.y } else if (this.lineType === 'circle') { // 繪製橢圓時恢復起始點狀態再從新繪製 this.context.putImageData(this.beginRec.imageData, 0, 0) this.context.beginPath() let a = (canvasX - this.beginRec.x) / 2 let b = (canvasY - this.beginRec.y) / 2 this.drawEllipse(this.context, this.beginRec.x + a, this.beginRec.y + b, a > 0 ? a : -a, b > 0 ? b : -b) let info = this.drawInfo[this.drawInfo.length - 1] info.a = a / this.width info.b = b / this.height } this.context.stroke() } }, // 繪製橢圓 drawEllipse (context, x, y, a, b) { context.save() var r = (a > b) ? a : b var ratioX = a / r var ratioY = b / r context.scale(ratioX, ratioY) context.beginPath() context.arc(x / ratioX, y / ratioY, r, 0, 2 * Math.PI, false) context.closePath() context.restore() }, // 鼠標擡起 canvasUp (e) { if (this.canDraw) { this.canvasMoveUse = false } }, // 獲取座標信息 getInfo () { return this.drawInfo }, // 清空畫布 clean () { this.context.drawImage(this.img, 0, 0, this.width, this.height) this.drawInfo = [] if (this.info && this.info.length !== 0) this.drawWithInfo() } } } </script> <style lang="scss" scoped> .canvas{ cursor: crosshair; } </style>
url: string
width: string
height: string
canDraw: boolean
info: string
canDraw: boolean
lineColor: string
lineWidth: number
lineType: string
clean()
getInfo()