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