看過前面幾篇文章的讀者相信也應該有所瞭解了,咱們藉助於團隊內部開發的編輯器實現了不少成功的項目案例,已經屢次看到咱們點擊一個文本、一個按鈕等等...去彈出一個表格之類的交互,有些同窗不免有些好奇咱們是如何實如今 canvas 圖形對象上實現事件派發和監聽的,接下來聽我娓娓道來。javascript
其實在實現事件代理對象以前,我實現過一個文本對象,就是在 canvas 上繪製出一個固定默認寬高的矩形,用戶雙擊時可輸入文字,它的實現可謂是很是簡單。可是其中也有不容忽視的小細節須要注意。java
// 建立input createTextInput() { this.setVisible(false) const textInput = document.createElement("input") this.c("textInput", textInput) // 設置基本樣式 ... //input剛建立出來並不會自動聚焦 //這裏須要調用一次自身focus 讓input聚焦 textInput.focus() const remove = () => { // 刪除Input this.removeTextInput() textInput.removeEventListener("blur", remove, false) } textInput.addEventListener("blur", remove, false) } // 更新input的位置 updateTextInput() { this.setVisible(false) const textInput = this.getClient("textInput") const style = textInput.style const zoom = this._network.getZoom() const rect = this._network.getViewRect() const { x, y } = this.getLocation() style.display = "block" style.left = x * zoom + -rect.x + "px" style.top = y * zoom + -rect.y + "px" }
這一步因爲咱們採用的是 input 因此咱們只須要在 input 失去焦點的時候,獲取到 input.value 的內容就行,同時也必定要注意要刪除掉 input 節點,不然頁面上的文本一旦多了起來,會很大程度影響性能.node
由此想到了咱們是否是也能夠實現一個相似的事件代理對象,這樣一來咱們就能輕鬆操做咱們框架上 canvas 畫布裏的各類對象了。接下來就讓咱們一塊兒去瞧瞧團隊裏是如何實現的事件代理對象。canvas
使用虛擬的一個 HTMLNODE 節點代理目標對象事件緩存
// BaseHTMLNode是本身封裝的一個原生DOM節點對象 class EventProxyNode extends BaseHTMLNode { constructor(network) { // 關聯的目標對象 this._attacher = null; } ... }
設計的時候要考慮到使用時傳入一個目標對象關聯綁定微信
在這裏咱們經過綁定事件和派發事件去使得目標對象和代理對象關聯
在編輯器中已經預留了派發事件和監聽事件的操做面板。
在工具中步驟截圖以下:app
因爲咱們在畫布交互對象中註冊了交互事件監聽,每當咱們的事件代理對象觸發事件時就派發
一個畫布的事件出去
框架
(不知道是哪位小夥伴寫的註釋,給他點贊 👍)
關於事件代理是如何執行代理的代碼以下所示,每次代理對象觸發了事件後就經過咱們的canvas畫布對象把事件派發出去,傳遞的參數當中是代理對象關聯的目標對象,這樣就至關因而咱們的目標對象觸發了這個事件,而後只須要在監聽對象上綁定監聽寫上處理邏輯便可。dom
/** * 事件代理網元觸發事件的時候把事件派發到宿主網元上 * @param {*} network */ function addEventProxyInteraction(network) { network.addInteractionListener((e) => { if (e.element instanceof EventProxyNode) { network.fireInteractionEvent({ // 事件名稱 kind: e.kind, // 事件源 element: e.element._attacher, // 事件對象 event: e.event, }); } }); }
在這裏咱們的管理事件代理對象的就是目標對象,當咱們初始化渲染頁面生成圖元時,
就會生成對應的事件代理對象,而且在屬性面板預留了一個開關用來控制是否啓用事件代理對象編輯器
// 建立事件代理對象 createEventProxyNode() { if (this._eventProxyNode) { return } this._eventProxyNode = new EventProxyNode(this._network) const parent = this.getParent() this._eventProxyNode.setParent(parent) const zoom = this._network.getZoom() this._eventProxyNode.scaleWithZoom(zoom) const { x, y, width, height } = this.getRect() this._eventProxyNode.setLocation(x, y) this._eventProxyNode.setSize(width, height) this._eventProxyNode.append() this._eventProxyNode._attacher = this } // 初始化事件代理對象 initProxyNodes() { const box = this._box box.forEach(node => { if (node.getEventProxy && node.getEventProxy()) { node.createEventProxyNode() } }) } ...
這樣一來咱們就能點擊一個明明不是 DOM 節點,也不是咱們框架當中的一個內置對象,卻依然可以觸發一系列的事件,而且還能在配置當中靈活配置相應的事件,方便了咱們去實現各類交互。
有時當咱們實現一個很小的功能點的時候,能夠根據這個功能點放大,宏觀的去思考一下有沒有可能實現一個通用的、可複用的功能點,功能雖小,實現雖簡單,可是卻能方便咱們作不少事,高樓大廈也是從一磚一瓦來的。
若是對可視化感興趣,能夠和我交流,微信541002349. 另外關注公衆號「ITMan彪叔」 能夠及時收到更多有價值的文章。