'新增键盘导航,即通过方向键来切换激活的节点'

This commit is contained in:
wanglin2
2022-12-09 16:53:42 +08:00
parent 39b55b0cd7
commit 41b0b21354
12 changed files with 217 additions and 4 deletions

View File

@@ -423,6 +423,14 @@ v0.1.5+
将节点移动到另一个节点的后面
#### moveNodeToCenter(node)
v0.2.17+
移动节点到画布中心。
目前如果是存在缩放的情况下回到中心会重置缩放。
## keyCommand实例
`keyCommand`实例负责快捷键的添加及触发,内置了一些快捷键,也可以自行添加。可通过`mindMap.keyCommand`获取到该实例。
@@ -550,6 +558,12 @@ v0.1.1+
动态设置变换数据可以通过getTransformData方法获取变换数据
#### setScale(scale)
v0.2.17+
设置缩放
## MiniMap实例
v0.2.11+

View File

@@ -15,6 +15,7 @@ import { layoutValueList } from './src/utils/constant'
import { SVG } from '@svgdotjs/svg.js'
import xmind from './src/parse/xmind'
import { simpleDeepClone } from './src/utils'
import KeyboardNavigation from './src/KeyboardNavigation'
// 默认选项配置
const defaultOpt = {
@@ -133,6 +134,11 @@ class MindMap {
mindMap: this
})
// 键盘导航类
this.keyboardNavigation = new KeyboardNavigation({
mindMap: this
})
// 批量执行类
this.batchExecution = new BatchExecution()

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.2.16",
"version": "0.2.17",
"description": "一个简单的web在线思维导图",
"authors": [
{

View File

@@ -48,6 +48,7 @@ class Event extends EventEmitter {
this.onMousewheel = this.onMousewheel.bind(this)
this.onContextmenu = this.onContextmenu.bind(this)
this.onSvgMousedown = this.onSvgMousedown.bind(this)
this.onKeyup = this.onKeyup.bind(this)
}
/**
@@ -69,6 +70,7 @@ class Event extends EventEmitter {
this.mindMap.el.addEventListener('mousewheel', this.onMousewheel)
}
this.mindMap.svg.on('contextmenu', this.onContextmenu)
window.addEventListener('keyup', this.onKeyup)
}
/**
@@ -84,6 +86,7 @@ class Event extends EventEmitter {
window.removeEventListener('mouseup', this.onMouseup)
this.mindMap.el.removeEventListener('mousewheel', this.onMousewheel)
this.mindMap.svg.off('contextmenu', this.onContextmenu)
window.removeEventListener('keyup', this.onKeyup)
}
/**
@@ -177,6 +180,16 @@ class Event extends EventEmitter {
e.preventDefault()
this.emit('contextmenu', e)
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 11:12:11
* @Desc: 按键松开事件
*/
onKeyup(e) {
this.emit('keyup', e)
}
}
export default Event

View File

@@ -0,0 +1,133 @@
import { isKey } from './utils/keyMap'
import { bfsWalk } from './utils'
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 11:06:50
* @Desc: 键盘导航类
*/
export default class KeyboardNavigation {
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 11:07:24
* @Desc: 构造函数
*/
constructor(opt) {
this.opt = opt
this.mindMap = opt.mindMap
this.onKeyup = this.onKeyup.bind(this)
this.mindMap.on('keyup', this.onKeyup)
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 14:12:27
* @Desc: 处理按键事件
*/
onKeyup(e) {
if (this.mindMap.renderer.activeNodeList.length > 0) {
;['Left', 'Up', 'Right', 'Down'].forEach(dir => {
if (isKey(e, dir)) {
this.focus(dir)
}
})
} else {
let root = this.mindMap.renderer.root
this.mindMap.renderer.moveNodeToCenter(root)
root.active()
}
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 14:12:39
* @Desc: 聚焦到下一个节点
*/
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
}
}
bfsWalk(this.mindMap.renderer.root, node => {
let rect = this.getNodeRect(node)
let { left, top, right, bottom } = rect
if (dir === 'Right') {
if (left >= currentActiveNodeRect.right) {
checkNodeDis(rect, node)
}
} else if (dir === 'Left') {
if (right <= currentActiveNodeRect.left) {
checkNodeDis(rect, node)
}
} else if (dir === 'Up') {
if (bottom <= currentActiveNodeRect.top) {
checkNodeDis(rect, node)
}
} else if (dir === 'Down') {
if (top >= currentActiveNodeRect.bottom) {
checkNodeDis(rect, node)
}
}
})
if (targetNode) {
this.mindMap.renderer.moveNodeToCenter(targetNode)
targetNode.active()
}
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 14:12:50
* @Desc: 获取节点的位置信息
*/
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
}
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 14:13:04
* @Desc: 获取两个节点的距离
*/
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)
)
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 14:13:11
* @Desc: 获取节点的中心点
*/
getCenter({ left, right, top, bottom }) {
return {
x: (left + right) / 2,
y: (top + bottom) / 2
}
}
}

View File

@@ -1097,6 +1097,28 @@ class Render {
this.mindMap.render()
}
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 11:46:57
* @Desc: 移动节点到画布中心
*/
moveNodeToCenter(node) {
let halfWidth = this.mindMap.width / 2
let halfHeight = this.mindMap.height / 2
let { left, top, width, height } = node
let nodeCenterX = left + width / 2
let nodeCenterY = top + height / 2
let { state } = this.mindMap.view.getTransformData()
let targetX = halfWidth - state.x
let targetY = halfHeight - state.y
let offsetX = targetX - nodeCenterX
let offsetY = targetY - nodeCenterY
this.mindMap.view.translateX(offsetX)
this.mindMap.view.translateY(offsetY)
this.mindMap.view.setScale(1)
}
}
export default Render

View File

@@ -88,6 +88,9 @@ export default class TextEdit {
this.textEditNode = document.createElement('div')
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none;`
this.textEditNode.setAttribute('contenteditable', true)
this.textEditNode.addEventListener('keyup', e => {
e.stopPropagation()
})
document.body.appendChild(this.textEditNode)
}
node.style.domText(this.textEditNode, this.mindMap.view.scale)

View File

@@ -213,6 +213,18 @@ class View {
this.transform()
this.mindMap.emit('scale', this.scale)
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-12-09 16:31:59
* @Desc: 设置缩放
*/
setScale(scale) {
this.scale = scale
this.transform()
this.mindMap.emit('scale', this.scale)
}
}
export default View

View File

@@ -11,11 +11,12 @@
v-model="link"
size="mini"
placeholder="http://xxxx.com/"
@keyup.native.stop
></el-input>
</div>
<div class="item">
<span class="name">{{ $t('nodeHyperlink.name') }}</span>
<el-input v-model="linkTitle" size="mini"></el-input>
<el-input v-model="linkTitle" size="mini" @keyup.native.stop></el-input>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel">{{ $t('dialog.cancel') }}</el-button>

View File

@@ -12,7 +12,7 @@
v-model="note"
>
</el-input> -->
<div class="noteEditor" ref="noteEditor"></div>
<div class="noteEditor" ref="noteEditor" @keyup.stop></div>
<!-- <div class="tip">换行请使用Enter+Shift</div> -->
<span slot="footer" class="dialog-footer">
<el-button @click="cancel">{{ $t('dialog.cancel') }}</el-button>

View File

@@ -8,6 +8,7 @@
<el-input
v-model="tag"
@keyup.native.enter="add"
@keyup.native.stop
:disabled="tagArr.length >= max"
:placeholder="$t('nodeTag.addTip')"
>

View File

@@ -8,7 +8,15 @@
default-expand-all
>
<span class="customNode" slot-scope="{ node, data }">
<span class="nodeEdit" :key="getKey()" contenteditable="true" @keydown.stop @keyup.stop @blur="onBlur($event, node)" v-html="node.label"></span>
<span
class="nodeEdit"
:key="getKey()"
contenteditable="true"
@keydown.stop
@keyup.stop
@blur="onBlur($event, node)"
v-html="node.label"
></span>
</span>
</el-tree>
</Sidebar>