VUE使用canvas繪制管線管廊實(shí)現(xiàn)思路
正文
最近接到公司項(xiàng)目中的一個(gè)需求,需要繪制一個(gè)展示管線平面圖的功能,除了展示以外,還需要進(jìn)行內(nèi)容的編輯,UI人員給的最終效果圖如下:
經(jīng)過(guò)分析后,覺(jué)得使用canvas,能夠?qū)⒋诵Ч麑?shí)現(xiàn)。最終將功能拆分為以下三點(diǎn):
- canvas 繪制圖形并填充
- canvas 繪制圖片
- canvas 繪制文字
其中,繪制圖形并填充對(duì)應(yīng)的為管線,繪制圖片對(duì)應(yīng)的為設(shè)備圖標(biāo),繪制文字則對(duì)應(yīng)設(shè)備上方的文字描述及其他部分的文字信息,例如:一供、二回等。
因上述功能為項(xiàng)目中的功能的一部分,項(xiàng)目整體由VUE搭建,所以,此功能需要在VUE的項(xiàng)目結(jié)構(gòu)上進(jìn)行建立。
查閱大量資料及論壇后,沒(méi)有找到合適的案例,那只能由本人親自來(lái)手搓了,此案例涉及到以下知識(shí)點(diǎn):
- JS的鼠標(biāo)事件:
onmousedown
onmousemove
onmouseup
- canvas 坐標(biāo)軸與瀏覽器坐標(biāo)軸的計(jì)算
- canvas 實(shí)時(shí)繪制
- canvas 旋轉(zhuǎn)
- 構(gòu)造函數(shù)的運(yùn)用
先分享一下最后的成果吧~
因時(shí)間及此案例代碼量的關(guān)系
vue使用canvas繪制管線實(shí)現(xiàn)思路
首先,因canvas的繪制離不開(kāi)JS,但是在vue頁(yè)面中來(lái)書(shū)寫(xiě),又會(huì)導(dǎo)致vue頁(yè)面代碼量過(guò)多,所以,我單獨(dú)寫(xiě)了一個(gè)js文件,通過(guò) export
進(jìn)行導(dǎo)出,在vue頁(yè)面中進(jìn)行調(diào)用,下面來(lái)看代碼:
index.vue
<template> <div class="canvas-container"> <div class="canvas-icon-content">左側(cè)選項(xiàng)列表</div> <div class="canvas-content"> <div class="canvas" id="canvas"> <canvas id="myCanvas" ref="myCanvas"></canvas> </div> <div class="canvas-options">下方操作按鈕</div> </div> </div> </template> <script> import canvas from '../utils/canvas' let myCanvas = {} export default { name: 'index', data() { return {} }, mounted() { myCanvas = canvas.init('myCanvas') }, } </script>
在 vue 頁(yè)面中,主要是針對(duì)整體界面的搭建,css樣式進(jìn)行編寫(xiě),其中除了界面外,還有 管線設(shè)備 信息修改的彈窗界面編寫(xiě),如下圖
針對(duì)信息編輯后的 “確定” 及 “取消” 事件,全部通過(guò)調(diào)用 myCanvas
中的方法來(lái)進(jìn)行。
JS文件
在JS文件中,
首先定義一個(gè) allElementCollection
數(shù)組,這個(gè)數(shù)組最終需要提交給后端,同時(shí),頁(yè)面中元素的繪制主要來(lái)自于這個(gè)主要數(shù)組。
剩下的就是來(lái)添加繪制的工作,以及JS中數(shù)據(jù)傳入vue頁(yè)面,vue頁(yè)面的數(shù)據(jù),傳入JS中。
數(shù)據(jù)傳輸這里,我是這樣做的,定義了一個(gè)對(duì)象 canvasDraw
,里面部分方法,如下代碼:
const canvasDraw = { init(element) { canvas = document.getElementById(element) ctx = canvas.getContext('2d') const w = 1200, h = 800; canvas.width = w * devicePixelRatio; canvas.height = h * devicePixelRatio; canvas.style.width = w + 'px'; canvas.style.height = h + 'px'; return canvas }, // 回傳鼠標(biāo)抬起事件 canvasMouseUp: (e) => {}, // 繪制類(lèi)型切換 drawTypeChange: (ele) => {}, // 修改管線類(lèi)型(冷熱水) changePipelineType: (type) => {}, // 設(shè)備參數(shù)修改 canvasModifyInfo: (info) => {}, // 顯示設(shè)備可拖動(dòng)的區(qū)域范圍 showEquipmentIconArea: () => {}, commit: () => { // todo // 提交事件 }, // 清除整個(gè)畫(huà)布 clearAll: (info) => {}, // 數(shù)據(jù)回顯 echoData: (data) => {} } export default canvasDraw
上述代碼中,所有的 canvas
相關(guān)的方法,都通過(guò)對(duì)象 canvasDraw
導(dǎo)出,在vue頁(yè)面,就可以通過(guò) myCanvas
來(lái)進(jìn)行調(diào)用了。
接下來(lái),我們需要一個(gè)構(gòu)造函數(shù),這個(gè)函數(shù)的作用是,通過(guò)構(gòu)造函數(shù),可以 new
多個(gè)對(duì)象,每個(gè)對(duì)象里面有鼠標(biāo)按下的起點(diǎn)坐標(biāo) startX
、startY
,鼠標(biāo)抬起的重點(diǎn)坐標(biāo) endX
、endY
,以及繪制的類(lèi)型、繪制不同類(lèi)型的信息對(duì)象、繪制形狀的方法、繪制文字的方法、繪制圖片的方法:
/** * 創(chuàng)建繪制元素工廠函數(shù) * * */ class ElementFactory { constructor(startX, startY, endX, endY) { this.startX = startX; // 鼠標(biāo) 按下 X點(diǎn) this.startY = startY; // 鼠標(biāo) 按下 Y點(diǎn) this.endX = endX; // 鼠標(biāo) 抬起 X點(diǎn) this.endY = endY; // 鼠標(biāo) 抬起 Y點(diǎn) this.type = 0; // 繪制類(lèi)型:圖形、文字、圖片 this.pipelineInfo = {}; // 圖形(管線)私有信息 this.equipmentInfo = {}; // 圖片(設(shè)備)私有信息 this.textInfo = {}; // 文字(文字)私有信息 } get minX() { return Math.min(this.startX, this.endX); } get maxX() { return Math.max(this.startX, this.endX); } get minY() { return Math.min(this.startY, this.endY); } get maxY() { return Math.max(this.startY, this.endY); } get middleX() { return this.endX - (this.endX - this.startX) / 2 } get middleY() { return this.endY - (this.endY - this.startY) / 2 } // 判斷點(diǎn)擊的是否存在元素繪制的范圍之內(nèi) isInside(x, y) { return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY } // 繪制管線 drawPipeline() {} // 繪制設(shè)備 drawEquipment() {} // 繪制設(shè)備上方文字 drawEquipmentText() {} // 繪制純文本 drawText() {} // 根據(jù)條件來(lái)調(diào)用不同的繪制方法 drawAllElement() { parseInt(this.type) === 0 ? this.drawPipeline() : (parseInt(this.type) === 1 ? this.drawEquipment() : this.drawText()) } }
基本的方法已經(jīng)寫(xiě)完了,那接下來(lái),就剩下一些鼠標(biāo)的管理事件了。
在函數(shù) canvasMousedown
中,主要處理三件事情:
1、鼠標(biāo)按下事件;
2、鼠標(biāo)移動(dòng)事件;
3、鼠標(biāo)抬起事件。
鼠標(biāo)按下的那一刻,有以下幾個(gè)方面需要注意:
- 鼠標(biāo)是左鍵點(diǎn)擊還是右鍵點(diǎn)擊;
- 當(dāng)前鼠標(biāo)點(diǎn)擊的位置,即
startX
、startY
; - 當(dāng)前鼠標(biāo)點(diǎn)擊的位置是否是存在了已經(jīng)繪制的內(nèi)容;
具體代碼如下:
const canvasMousedown = (e) => { const rect = canvas.getBoundingClientRect(); const clickX = e.clientX - rect.left; const clickY = e.clientY - rect.top; // 查詢(xún)所點(diǎn)擊元素是否存在 const shape = getElement(clickX, clickY); if (shape) { moveAllElement(e, clickX, clickY, rect, shape); canvas.style.cursor= "move"; } else { if (e.buttons === 1) { draw_element_type === 0 ? drawRealTimePipeline(e, clickX, clickY, rect) : (draw_element_type === 1 ? drawRealTimeEquipment(e, clickX, clickY, rect) : drawRealTimeText(e, clickX, clickY, rect)) } } };
其中 getElement
方法為:
// 鼠標(biāo)點(diǎn)擊canvas查看是否點(diǎn)擊到了已經(jīng)繪制的路線,若是,則返回相關(guān)線的對(duì)象,若否,返回null const getElement = (x, y) => { for (let i = allElementCollection.length - 1; i >= 0; i--) { if (element.isInside(x, y)) return element; } return null };
鼠標(biāo)按下后,獲取到 clickX
、clickY
,判斷當(dāng)前點(diǎn)擊的位置是否已經(jīng)繪制了元素shape
,如果shape
存在,執(zhí)行移動(dòng)事件,如果不存在,則執(zhí)行繪制事件。
大致的思路就是上述分享內(nèi)容,接下來(lái)的文章中,我會(huì)將具體的方法及注意事項(xiàng)進(jìn)行細(xì)化,
以上就是VUE使用canvas繪制管線管廊實(shí)現(xiàn)思路的詳細(xì)內(nèi)容,更多關(guān)于VUE canvas繪制管線管廊的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue用戶長(zhǎng)時(shí)間不操作退出到登錄頁(yè)的兩種實(shí)現(xiàn)方式
出于安全考慮,用戶長(zhǎng)時(shí)間不操作,就回到登錄頁(yè)面,讓用戶重新登錄,本文就記錄一下實(shí)現(xiàn)這種效果的兩種方式,具有一定的參考價(jià)值,感興趣的可以了解一下2021-09-09關(guān)于引入vue.js 文件的知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家分享的是關(guān)于引入vue.js 文件的知識(shí)點(diǎn)總結(jié),有需要的朋友們可以參考學(xué)習(xí)下。2020-01-01在 vue-cli v3.0 中使用 SCSS/SASS的方法
關(guān)于如何在 vue-cli v3.0 中使用 SCSS/SASS,這里提供三種方案。感興趣的朋友通過(guò)本文一起學(xué)習(xí)吧2018-06-06Vue項(xiàng)目中使用jquery的簡(jiǎn)單方法
這篇文章主要給大家介紹了關(guān)于Vue項(xiàng)目中使用jquery的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Vue具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05vue jsx 使用指南及vue.js 使用jsx語(yǔ)法的方法
這篇文章主要介紹了vue jsx 使用指南及vue.js 使用jsx語(yǔ)法的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-11-11淺談webpack SplitChunksPlugin實(shí)用指南
這篇文章主要介紹了淺談webpack SplitChunksPlugin實(shí)用指南,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09VSCode Vue開(kāi)發(fā)推薦插件和VSCode快捷鍵(小結(jié))
這篇文章主要介紹了VSCode Vue開(kāi)發(fā)推薦插件和VSCode快捷鍵(小結(jié)),文中通過(guò)圖文表格介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08