VUE使用canvas繪制管線管廊實現(xiàn)思路

正文
最近接到公司項目中的一個需求,需要繪制一個展示管線平面圖的功能,除了展示以外,還需要進行內(nèi)容的編輯,UI人員給的最終效果圖如下:

經(jīng)過分析后,覺得使用canvas,能夠?qū)⒋诵Ч麑崿F(xiàn)。最終將功能拆分為以下三點:
- canvas 繪制圖形并填充
- canvas 繪制圖片
- canvas 繪制文字
其中,繪制圖形并填充對應(yīng)的為管線,繪制圖片對應(yīng)的為設(shè)備圖標,繪制文字則對應(yīng)設(shè)備上方的文字描述及其他部分的文字信息,例如:一供、二回等。
因上述功能為項目中的功能的一部分,項目整體由VUE搭建,所以,此功能需要在VUE的項目結(jié)構(gòu)上進行建立。
查閱大量資料及論壇后,沒有找到合適的案例,那只能由本人親自來手搓了,此案例涉及到以下知識點:
- JS的鼠標事件:
onmousedownonmousemoveonmouseup - canvas 坐標軸與瀏覽器坐標軸的計算
- canvas 實時繪制
- canvas 旋轉(zhuǎn)
- 構(gòu)造函數(shù)的運用
先分享一下最后的成果吧~

因時間及此案例代碼量的關(guān)系

vue使用canvas繪制管線實現(xiàn)思路
首先,因canvas的繪制離不開JS,但是在vue頁面中來書寫,又會導致vue頁面代碼量過多,所以,我單獨寫了一個js文件,通過 export 進行導出,在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
},
// 回傳鼠標抬起事件
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 導出,在vue頁面,就可以通過 myCanvas 來進行調(diào)用了。
接下來,我們需要一個構(gòu)造函數(shù),這個函數(shù)的作用是,通過構(gòu)造函數(shù),可以 new 多個對象,每個對象里面有鼠標按下的起點坐標 startX、startY,鼠標抬起的重點坐標 endX、endY,以及繪制的類型、繪制不同類型的信息對象、繪制形狀的方法、繪制文字的方法、繪制圖片的方法:
/**
* 創(chuàng)建繪制元素工廠函數(shù)
*
* */
class ElementFactory {
constructor(startX, startY, endX, endY) {
this.startX = startX; // 鼠標 按下 X點
this.startY = startY; // 鼠標 按下 Y點
this.endX = endX; // 鼠標 抬起 X點
this.endY = endY; // 鼠標 抬起 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)寫完了,那接下來,就剩下一些鼠標的管理事件了。
在函數(shù) canvasMousedown 中,主要處理三件事情:
1、鼠標按下事件;
2、鼠標移動事件;
3、鼠標抬起事件。
鼠標按下的那一刻,有以下幾個方面需要注意:
- 鼠標是左鍵點擊還是右鍵點擊;
- 當前鼠標點擊的位置,即
startX、startY; - 當前鼠標點擊的位置是否是存在了已經(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 方法為:
// 鼠標點擊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
};
鼠標按下后,獲取到 clickX、clickY,判斷當前點擊的位置是否已經(jīng)繪制了元素shape,如果shape存在,執(zhí)行移動事件,如果不存在,則執(zhí)行繪制事件。
大致的思路就是上述分享內(nèi)容,接下來的文章中,我會將具體的方法及注意事項進行細化,
以上就是VUE使用canvas繪制管線管廊實現(xiàn)思路的詳細內(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é),有需要的朋友們可以參考學習下。2020-01-01
在 vue-cli v3.0 中使用 SCSS/SASS的方法
關(guān)于如何在 vue-cli v3.0 中使用 SCSS/SASS,這里提供三種方案。感興趣的朋友通過本文一起學習吧2018-06-06
vue jsx 使用指南及vue.js 使用jsx語法的方法
這篇文章主要介紹了vue jsx 使用指南及vue.js 使用jsx語法的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-11-11
淺談webpack SplitChunksPlugin實用指南
這篇文章主要介紹了淺談webpack SplitChunksPlugin實用指南,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09
VSCode Vue開發(fā)推薦插件和VSCode快捷鍵(小結(jié))
這篇文章主要介紹了VSCode Vue開發(fā)推薦插件和VSCode快捷鍵(小結(jié)),文中通過圖文表格介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08

