mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-17 22:08:25 +08:00
236 lines
7.1 KiB
JavaScript
236 lines
7.1 KiB
JavaScript
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
|