From 7fc42879c7c4bf3dd183863e4a75150abf0c3f93 Mon Sep 17 00:00:00 2001 From: wanglin25 Date: Fri, 9 Dec 2022 16:53:42 +0800 Subject: [PATCH] =?UTF-8?q?'=E6=96=B0=E5=A2=9E=E9=94=AE=E7=9B=98=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=EF=BC=8C=E5=8D=B3=E9=80=9A=E8=BF=87=E6=96=B9=E5=90=91?= =?UTF-8?q?=E9=94=AE=E6=9D=A5=E5=88=87=E6=8D=A2=E6=BF=80=E6=B4=BB=E7=9A=84?= =?UTF-8?q?=E8=8A=82=E7=82=B9'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++ simple-mind-map/index.js | 6 + simple-mind-map/package.json | 2 +- simple-mind-map/src/Event.js | 13 ++ simple-mind-map/src/KeyboardNavigation.js | 133 ++++++++++++++++++ simple-mind-map/src/Render.js | 22 +++ simple-mind-map/src/TextEdit.js | 3 + simple-mind-map/src/View.js | 12 ++ .../pages/Edit/components/NodeHyperlink.vue | 3 +- web/src/pages/Edit/components/NodeNote.vue | 2 +- web/src/pages/Edit/components/NodeTag.vue | 1 + web/src/pages/Edit/components/Outline.vue | 10 +- 12 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 simple-mind-map/src/KeyboardNavigation.js diff --git a/README.md b/README.md index f09cb4d2..c1dc4aab 100644 --- a/README.md +++ b/README.md @@ -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+ diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index 0b664aa0..8355c313 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -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() diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json index ac77b230..2ef2d070 100644 --- a/simple-mind-map/package.json +++ b/simple-mind-map/package.json @@ -1,6 +1,6 @@ { "name": "simple-mind-map", - "version": "0.2.16", + "version": "0.2.17", "description": "一个简单的web在线思维导图", "authors": [ { diff --git a/simple-mind-map/src/Event.js b/simple-mind-map/src/Event.js index d2dc97ea..eb1fce37 100644 --- a/simple-mind-map/src/Event.js +++ b/simple-mind-map/src/Event.js @@ -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 diff --git a/simple-mind-map/src/KeyboardNavigation.js b/simple-mind-map/src/KeyboardNavigation.js new file mode 100644 index 00000000..ab944bf4 --- /dev/null +++ b/simple-mind-map/src/KeyboardNavigation.js @@ -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 + } + } +} diff --git a/simple-mind-map/src/Render.js b/simple-mind-map/src/Render.js index 3706809f..c5980c9c 100644 --- a/simple-mind-map/src/Render.js +++ b/simple-mind-map/src/Render.js @@ -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 diff --git a/simple-mind-map/src/TextEdit.js b/simple-mind-map/src/TextEdit.js index 480acc61..d1b5f4d1 100644 --- a/simple-mind-map/src/TextEdit.js +++ b/simple-mind-map/src/TextEdit.js @@ -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) diff --git a/simple-mind-map/src/View.js b/simple-mind-map/src/View.js index ff76bed5..bca83896 100644 --- a/simple-mind-map/src/View.js +++ b/simple-mind-map/src/View.js @@ -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 diff --git a/web/src/pages/Edit/components/NodeHyperlink.vue b/web/src/pages/Edit/components/NodeHyperlink.vue index a6a96b56..32620758 100644 --- a/web/src/pages/Edit/components/NodeHyperlink.vue +++ b/web/src/pages/Edit/components/NodeHyperlink.vue @@ -11,11 +11,12 @@ v-model="link" size="mini" placeholder="http://xxxx.com/" + @keyup.native.stop >
{{ $t('nodeHyperlink.name') }} - +
{{ $t('dialog.cancel') }} diff --git a/web/src/pages/Edit/components/NodeNote.vue b/web/src/pages/Edit/components/NodeNote.vue index 63f66c9e..205d30e6 100644 --- a/web/src/pages/Edit/components/NodeNote.vue +++ b/web/src/pages/Edit/components/NodeNote.vue @@ -12,7 +12,7 @@ v-model="note" > --> -
+
{{ $t('dialog.cancel') }} diff --git a/web/src/pages/Edit/components/NodeTag.vue b/web/src/pages/Edit/components/NodeTag.vue index 5eebdd07..7a288f52 100644 --- a/web/src/pages/Edit/components/NodeTag.vue +++ b/web/src/pages/Edit/components/NodeTag.vue @@ -8,6 +8,7 @@ diff --git a/web/src/pages/Edit/components/Outline.vue b/web/src/pages/Edit/components/Outline.vue index c7d99372..893505a9 100644 --- a/web/src/pages/Edit/components/Outline.vue +++ b/web/src/pages/Edit/components/Outline.vue @@ -8,7 +8,15 @@ default-expand-all > - +