這篇文章帶給大家的內容是關於canvas實現圖片塗鴉功能(附程式碼),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
需求
需要對圖片進行標註,匯出圖片。
需要標註N多圖片最後同時儲存。
需要根據多邊形區域資料(區域、顏色、名稱)標註。
對應方案
用canvas實作塗鴉、圓形、矩形的繪製,最後產生圖片base64編碼用來上傳
大量圖片批次上傳很耗時,為了提高使用者體驗,改為只實現圓形、矩形繪製,最終保存成座標,下次顯示時根據座標再繪製。
多邊形區域的顯示是根據座標點繪製,名稱顯示的位置為多邊形質心。
程式碼
<template> <div> <canvas> </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> .canvas{ cursor: crosshair; } </style>
必須傳入的參數
- ##圖片路徑
url: string
- 繪圖區域寬度
width: string
- 繪圖區域高度
height: string
選擇傳入的參數
- 是否可以繪製,預設true
canDraw: boolean
- 座標點資訊,不傳入則不繪製
info: string
- #是否可繪製,預設true
canDraw: boolean
- 繪圖顏色,預設red
lineColor: string
- 繪圖筆寬度,預設2##
lineWidth: number
- #繪圖筆類型,rec、circle,預設rec
-
lineType: string
##清空畫布
-
clean()
-
#
getInfo()
特殊說明
canvas物件不能獲得座標,是透過父元素座標取得的,所以該元件的父元素以上的層級不能有太多的定位、嵌套,否則繪製座標會偏移。
- 網域不同的圖片可能有跨域問題,看過很多資料沒有太好的辦法,最後專案中是用node服務做了一個圖片轉為base64的接口,再給canvas繪製解決的。不一定適用於其他項目,如果有更好的辦法解決歡迎分享。
- 匯出座標點資料只能匯出規則圖案的座標點,因為隨意塗鴉的座標點太多時會崩潰的(雖然沒試過具體到什麼程度會崩潰),如果有高性能的實現方式歡迎分享。
- 如果塗鴉後儲存再請求圖片url出現請求不到的情況,是因為CDN快取的問題,在圖片路徑後面拼個隨機碼就可以解決。
- #
以上是canvas實作圖片塗鴉功能(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

HTML5代碼由標籤、元素和屬性組成:1.標籤定義內容類型,用尖括號包圍,如。 2.元素由開始標籤、內容和結束標籤組成,如內容。 3.屬性在開始標籤中定義鍵值對,增強功能,如。這些是構建網頁結構的基本單位。

HTML5是構建現代網頁的關鍵技術,提供了許多新元素和功能。 1.HTML5引入了語義化元素如、、等,增強了網頁結構和SEO。 2.支持多媒體元素和,無需插件即可嵌入媒體。 3.表單增強了新輸入類型和驗證屬性,簡化了驗證過程。 4.提供了離線和本地存儲功能,提升了網頁性能和用戶體驗。

H5代碼的最佳實踐包括:1.使用正確的DOCTYPE聲明和字符編碼;2.採用語義化標籤;3.減少HTTP請求;4.使用異步加載;5.優化圖像。這些實踐能提升網頁的效率、可維護性和用戶體驗。

Web标准和技术从HTML4、CSS2和简单的JavaScript演变至今,经历了显著的发展。1)HTML5引入了Canvas、WebStorage等API,增强了Web应用的复杂性和互动性。2)CSS3增加了动画和过渡功能,使页面效果更加丰富。3)JavaScript通过Node.js和ES6的现代化语法,如箭头函数和类,提升了开发效率和代码可读性,这些变化推动了Web应用的性能优化和最佳实践的发展。

H5不僅僅是HTML5的簡稱,它代表了一個更廣泛的現代網頁開發技術生態:1.H5包括HTML5、CSS3、JavaScript及相關API和技術;2.它提供更豐富、互動、流暢的用戶體驗,能在多設備上無縫運行;3.使用H5技術棧可以創建響應式網頁和復雜交互功能。

H5與HTML5指的是同一個東西,即HTML5。 HTML5是HTML的第五個版本,帶來了語義化標籤、多媒體支持、畫布與圖形、離線存儲與本地存儲等新功能,提升了網頁的表現力和交互性。

H5referstoHTML5,apivotaltechnologyinwebdevelopment.1)HTML5introducesnewelementsandAPIsforrich,dynamicwebapplications.2)Itsupportsmultimediawithoutplugins,enhancinguserexperienceacrossdevices.3)SemanticelementsimprovecontentstructureandSEO.4)H5'srespo

H5開發需要掌握的工具和框架包括Vue.js、React和Webpack。 1.Vue.js適用於構建用戶界面,支持組件化開發。 2.React通過虛擬DOM優化頁面渲染,適合複雜應用。 3.Webpack用於模塊打包,優化資源加載。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

Dreamweaver Mac版
視覺化網頁開發工具

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。