看过前面几篇文章的读者相信也应该有所了解了,咱们借助于团队内部开发的编辑器实现了不少成功的项目案例,已经屡次看到咱们点击一个文本、一个按钮等等...去弹出一个表格之类的交互,有些同窗不免有些好奇咱们是如何实如今 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彪叔” 能够及时收到更多有价值的文章。