UNPKG

@zxr3680166/simple-mind-map

Version:

一个简单的web在线思维导图

236 lines (220 loc) 7.24 kB
import { bfsWalk } from '../utils' import { CONSTANTS } from '../constants/constant' // 键盘导航插件 class KeyboardNavigation { // 构造函数 constructor(opt) { this.opt = opt this.mindMap = opt.mindMap this.onKeyup = this.onKeyup.bind(this) this.mindMap.keyCommand.addShortcut(CONSTANTS.KEY_DIR.LEFT, () => { this.onKeyup(CONSTANTS.KEY_DIR.LEFT) }) this.mindMap.keyCommand.addShortcut(CONSTANTS.KEY_DIR.UP, () => { this.onKeyup(CONSTANTS.KEY_DIR.UP) }) this.mindMap.keyCommand.addShortcut(CONSTANTS.KEY_DIR.RIGHT, () => { this.onKeyup(CONSTANTS.KEY_DIR.RIGHT) }) this.mindMap.keyCommand.addShortcut(CONSTANTS.KEY_DIR.DOWN, () => { this.onKeyup(CONSTANTS.KEY_DIR.DOWN) }) } // 处理按键事件 onKeyup(dir) { if (this.mindMap.renderer.activeNodeList.length > 0) { this.focus(dir) } else { let root = this.mindMap.renderer.root this.mindMap.execCommand('GO_TARGET_NODE', root) } } // 聚焦到下一个节点 focus(dir) { // 当前聚焦的节点 let currentActiveNode = this.mindMap.renderer.activeNodeList[0] // 当前聚焦节点的位置信息 let currentActiveNodeRect = this.getNodeRect(currentActiveNode) // 寻找的下一个聚焦节点 let targetNode = null let targetDis = Infinity // 保存并维护距离最近的节点 let checkNodeDis = (rect, node) => { let dis = this.getDistance(currentActiveNodeRect, rect) if (dis < targetDis) { targetNode = node targetDis = dis } } // 第一优先级:阴影算法 this.getFocusNodeByShadowAlgorithm({ currentActiveNode, currentActiveNodeRect, dir, checkNodeDis }) // 第二优先级:区域算法 if (!targetNode) { this.getFocusNodeByAreaAlgorithm({ currentActiveNode, currentActiveNodeRect, dir, checkNodeDis }) } // 第三优先级:简单算法 if (!targetNode) { this.getFocusNodeBySimpleAlgorithm({ currentActiveNode, currentActiveNodeRect, dir, checkNodeDis }) } // 找到了则让目标节点聚焦 if (targetNode) { this.mindMap.execCommand('GO_TARGET_NODE', targetNode) } } // 1.简单算法 getFocusNodeBySimpleAlgorithm({ currentActiveNode, currentActiveNodeRect, dir, checkNodeDis }) { // 遍历节点树 bfsWalk(this.mindMap.renderer.root, node => { // 跳过当前聚焦的节点 if (node.uid === currentActiveNode.uid) return // 当前遍历到的节点的位置信息 let rect = this.getNodeRect(node) let { left, top, right, bottom } = rect let match = false // 按下了左方向键 if (dir === CONSTANTS.KEY_DIR.LEFT) { // 判断节点是否在当前节点的左侧 match = right <= currentActiveNodeRect.left // 按下了右方向键 } else if (dir === CONSTANTS.KEY_DIR.RIGHT) { // 判断节点是否在当前节点的右侧 match = left >= currentActiveNodeRect.right // 按下了上方向键 } else if (dir === CONSTANTS.KEY_DIR.UP) { // 判断节点是否在当前节点的上面 match = bottom <= currentActiveNodeRect.top // 按下了下方向键 } else if (dir === CONSTANTS.KEY_DIR.DOWN) { // 判断节点是否在当前节点的下面 match = top >= currentActiveNodeRect.bottom } // 符合要求,判断是否是最近的节点 if (match) { checkNodeDis(rect, node) } }) } // 2.阴影算法 getFocusNodeByShadowAlgorithm({ currentActiveNode, currentActiveNodeRect, dir, checkNodeDis }) { bfsWalk(this.mindMap.renderer.root, node => { if (node.uid === currentActiveNode.uid) return let rect = this.getNodeRect(node) let { left, top, right, bottom } = rect let match = false if (dir === CONSTANTS.KEY_DIR.LEFT) { match = left < currentActiveNodeRect.left && top < currentActiveNodeRect.bottom && bottom > currentActiveNodeRect.top } else if (dir === CONSTANTS.KEY_DIR.RIGHT) { match = right > currentActiveNodeRect.right && top < currentActiveNodeRect.bottom && bottom > currentActiveNodeRect.top } else if (dir === CONSTANTS.KEY_DIR.UP) { match = top < currentActiveNodeRect.top && left < currentActiveNodeRect.right && right > currentActiveNodeRect.left } else if (dir === CONSTANTS.KEY_DIR.DOWN) { match = bottom > currentActiveNodeRect.bottom && left < currentActiveNodeRect.right && right > currentActiveNodeRect.left } if (match) { checkNodeDis(rect, node) } }) } // 3.区域算法 getFocusNodeByAreaAlgorithm({ currentActiveNode, currentActiveNodeRect, dir, checkNodeDis }) { // 当前聚焦节点的中心点 let cX = (currentActiveNodeRect.right + currentActiveNodeRect.left) / 2 let cY = (currentActiveNodeRect.bottom + currentActiveNodeRect.top) / 2 bfsWalk(this.mindMap.renderer.root, node => { if (node.uid === currentActiveNode.uid) return let rect = this.getNodeRect(node) let { left, top, right, bottom } = rect // 遍历到的节点的中心点 let ccX = (right + left) / 2 let ccY = (bottom + top) / 2 // 节点的中心点坐标和当前聚焦节点的中心点坐标的差值 let offsetX = ccX - cX let offsetY = ccY - cY if (offsetX === 0 && offsetY === 0) return let match = false if (dir === CONSTANTS.KEY_DIR.LEFT) { match = offsetX <= 0 && offsetX <= offsetY && offsetX <= -offsetY } else if (dir === CONSTANTS.KEY_DIR.RIGHT) { match = offsetX > 0 && offsetX >= -offsetY && offsetX >= offsetY } else if (dir === CONSTANTS.KEY_DIR.UP) { match = offsetY <= 0 && offsetY < offsetX && offsetY < -offsetX } else if (dir === CONSTANTS.KEY_DIR.DOWN) { match = offsetY > 0 && -offsetY < offsetX && offsetY > offsetX } if (match) { checkNodeDis(rect, node) } }) } // 获取节点的位置信息 getNodeRect(node) { let { scaleX, scaleY, translateX, translateY } = this.mindMap.draw.transform() let { left, top, width, height } = node return { right: (left + width) * scaleX + translateX, bottom: (top + height) * scaleY + translateY, left: left * scaleX + translateX, top: top * scaleY + translateY } } // 获取两个节点的距离 getDistance(node1Rect, node2Rect) { let center1 = this.getCenter(node1Rect) let center2 = this.getCenter(node2Rect) return Math.sqrt( Math.pow(center1.x - center2.x, 2) + Math.pow(center1.y - center2.y, 2) ) } // 获取节点的中心点 getCenter({ left, right, top, bottom }) { return { x: (left + right) / 2, y: (top + bottom) / 2 } } } KeyboardNavigation.instanceName = 'keyboardNavigation' export default KeyboardNavigation