mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-17 22:08:25 +08:00
新增支持节点拖拽
This commit is contained in:
145
README.md
145
README.md
@@ -1,7 +1,23 @@
|
||||
[TOC]
|
||||
|
||||
# web思维导图的简单实现
|
||||
|
||||
## 特性
|
||||
|
||||
- [x] 支持逻辑结构图、思维导图、组织结构图、目录组织图四种结构
|
||||
|
||||
- [x] 内置多种主题,允许高度自定义样式
|
||||
|
||||
- [x] 支持快捷键
|
||||
|
||||
- [x] 节点内容支持图片、图标、超链接、备注、标签
|
||||
|
||||
- [x] 支持前进后退
|
||||
|
||||
- [x] 支持拖动、缩放
|
||||
|
||||
- [x] 支持右键多选
|
||||
|
||||
- [x] 支持节点拖拽
|
||||
|
||||
## 目录介绍
|
||||
|
||||
1.simple-mind-map
|
||||
@@ -44,6 +60,10 @@ npm run build
|
||||
```
|
||||
会自动把`index.html`移动到根目录。
|
||||
|
||||
## 相关文章
|
||||
|
||||
[Web思维导图实现的技术点分析](https://juejin.cn/post/6987711560521089061)
|
||||
|
||||
# 安装
|
||||
|
||||
```bash
|
||||
@@ -193,31 +213,34 @@ const mindMap = new MindMap({
|
||||
|
||||
执行命令,每执行一个命令就会在历史堆栈里添加一条记录用于回退或前进。所有命令如下:
|
||||
|
||||
| 命令名称 | 描述 | 参数 |
|
||||
| ------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| SELECT_ALL | 全选 | |
|
||||
| BACK | 回退指定的步数 | step(要回退的步数,默认为1) |
|
||||
| FORWARD | 前进指定的步数 | step(要前进的步数,默认为1) |
|
||||
| INSERT_NODE | 插入同级节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效 | |
|
||||
| INSERT_CHILD_NODE | 插入子节点,操作节点为当前激活的节点 | |
|
||||
| UP_NODE | 上移节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的第一个节点使用无效 | |
|
||||
| DOWN_NODE | 操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的最后一个节点使用无效 | |
|
||||
| REMOVE_NODE | 删除节点,操作节点为当前激活的节点 | |
|
||||
| PASTE_NODE | 粘贴节点到节点,操作节点为当前激活的节点 | data(要粘贴的节点数据,一般通过`renderer.copyNode()`方法和`renderer.cutNode()`方法获取) |
|
||||
| CUT_NODE | 剪切节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点使用无效 | callback(回调函数,剪切的节点数据会通过调用该函数并通过参数返回) |
|
||||
| SET_NODE_STYLE | 修改节点样式 | node(要设置样式的节点)、prop(样式属性)、value(样式属性值)、isActive(布尔值,是否设置的是激活状态的样式) |
|
||||
| SET_NODE_ACTIVE | 设置节点是否激活 | node(要设置的节点)、active(布尔值,是否激活) |
|
||||
| CLEAR_ACTIVE_NODE | 清除当前已激活节点的激活状态,操作节点为当前激活的节点 | |
|
||||
| SET_NODE_EXPAND | 设置节点是否展开 | node(要设置的节点)、expand(布尔值,是否展开) |
|
||||
| EXPAND_ALL | 展开所有节点 | |
|
||||
| UNEXPAND_ALL | 收起所有节点 | |
|
||||
| SET_NODE_DATA | 更新节点数据,即更新节点数据对象里`data`对象的数据 | node(要设置的节点)、data(对象,要更新的数据,如`{expand: true}`) |
|
||||
| SET_NODE_TEXT | 设置节点文本 | node(要设置的节点)、text(要设置的文本字符串,换行可以使用`\n`) |
|
||||
| SET_NODE_IMAGE | 设置节点图片 | node(要设置的节点)、imgData(对象,图片信息,结构为:`{url, title, width, height}`,图片的宽高必须要传) |
|
||||
| SET_NODE_ICON | 设置节点图标 | node(要设置的节点)、icons(数组,预定义的图片名称组成的数组,可用图标可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js)文件里的`nodeIconList`列表里获取到,图标名称为`type_name`,如`['priority_1']`) |
|
||||
| SET_NODE_HYPERLINK | 设置节点超链接 | node(要设置的节点)、link(超链接地址)、title(超链接名称,可选) |
|
||||
| SET_NODE_NOTE | 设置节点备注 | node(要设置的节点)、note(备注文字) |
|
||||
| SET_NODE_TAG | 设置节点标签 | node(要设置的节点)、tag(字符串数组,内置颜色信息可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js)里获取到) |
|
||||
| 命令名称 | 描述 | 参数 |
|
||||
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| SELECT_ALL | 全选 | |
|
||||
| BACK | 回退指定的步数 | step(要回退的步数,默认为1) |
|
||||
| FORWARD | 前进指定的步数 | step(要前进的步数,默认为1) |
|
||||
| INSERT_NODE | 插入同级节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效 | |
|
||||
| INSERT_CHILD_NODE | 插入子节点,操作节点为当前激活的节点 | |
|
||||
| UP_NODE | 上移节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的第一个节点使用无效 | |
|
||||
| DOWN_NODE | 操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的最后一个节点使用无效 | |
|
||||
| REMOVE_NODE | 删除节点,操作节点为当前激活的节点 | |
|
||||
| PASTE_NODE | 粘贴节点到节点,操作节点为当前激活的节点 | data(要粘贴的节点数据,一般通过`renderer.copyNode()`方法和`renderer.cutNode()`方法获取) |
|
||||
| CUT_NODE | 剪切节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点使用无效 | callback(回调函数,剪切的节点数据会通过调用该函数并通过参数返回) |
|
||||
| SET_NODE_STYLE | 修改节点样式 | node(要设置样式的节点)、prop(样式属性)、value(样式属性值)、isActive(布尔值,是否设置的是激活状态的样式) |
|
||||
| SET_NODE_ACTIVE | 设置节点是否激活 | node(要设置的节点)、active(布尔值,是否激活) |
|
||||
| CLEAR_ACTIVE_NODE | 清除当前已激活节点的激活状态,操作节点为当前激活的节点 | |
|
||||
| SET_NODE_EXPAND | 设置节点是否展开 | node(要设置的节点)、expand(布尔值,是否展开) |
|
||||
| EXPAND_ALL | 展开所有节点 | |
|
||||
| UNEXPAND_ALL | 收起所有节点 | |
|
||||
| SET_NODE_DATA | 更新节点数据,即更新节点数据对象里`data`对象的数据 | node(要设置的节点)、data(对象,要更新的数据,如`{expand: true}`) |
|
||||
| SET_NODE_TEXT | 设置节点文本 | node(要设置的节点)、text(要设置的文本字符串,换行可以使用`\n`) |
|
||||
| SET_NODE_IMAGE | 设置节点图片 | node(要设置的节点)、imgData(对象,图片信息,结构为:`{url, title, width, height}`,图片的宽高必须要传) |
|
||||
| SET_NODE_ICON | 设置节点图标 | node(要设置的节点)、icons(数组,预定义的图片名称组成的数组,可用图标可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js)文件里的`nodeIconList`列表里获取到,图标名称为`type_name`,如`['priority_1']`) |
|
||||
| SET_NODE_HYPERLINK | 设置节点超链接 | node(要设置的节点)、link(超链接地址)、title(超链接名称,可选) |
|
||||
| SET_NODE_NOTE | 设置节点备注 | node(要设置的节点)、note(备注文字) |
|
||||
| SET_NODE_TAG | 设置节点标签 | node(要设置的节点)、tag(字符串数组,内置颜色信息可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js)里获取到) |
|
||||
| INSERT_AFTER(v0.1.5+) | 将节点移动到另一个节点的后面 | node(要移动的节点)、 exist(目标节点) |
|
||||
| INSERT_BEFORE(v0.1.5+) | 将节点移动到另一个节点的前面 | node(要移动的节点)、 exist(目标节点) |
|
||||
| MOVE_NODE_TO(v0.1.5+) | 移动一个节点作为另一个节点的子节点 | node(要移动的节点)、 toNode(目标节点) |
|
||||
|
||||
|
||||
#### setData(data)
|
||||
@@ -235,7 +258,11 @@ const mindMap = new MindMap({
|
||||
|
||||
`isDownload`:是否需要直接触发下载,布尔值,默认为`false`
|
||||
|
||||
#### toPos(x, y)
|
||||
|
||||
v0.1.5+
|
||||
|
||||
将浏览器可视窗口的坐标转换成相对于画布的坐标
|
||||
|
||||
## render实例
|
||||
|
||||
@@ -301,6 +328,30 @@ const mindMap = new MindMap({
|
||||
|
||||
|
||||
|
||||
#### moveNodeTo(node, toNode)
|
||||
|
||||
v0.1.5+
|
||||
|
||||
移动一个节点作为另一个节点的子节点
|
||||
|
||||
|
||||
|
||||
#### insertBefore(node, exist)
|
||||
|
||||
v0.1.5+
|
||||
|
||||
将节点移动到另一个节点的前面
|
||||
|
||||
|
||||
|
||||
#### insertAfter(node, exist)
|
||||
|
||||
v0.1.5+
|
||||
|
||||
将节点移动到另一个节点的后面
|
||||
|
||||
|
||||
|
||||
## keyCommand实例
|
||||
|
||||
`keyCommand`实例负责快捷键的添加及触发,内置了一些快捷键,也可以自行添加。可通过`mindMap.keyCommand`获取到该实例。
|
||||
@@ -558,6 +609,14 @@ v0.1.1+
|
||||
|
||||
|
||||
|
||||
#### isDrag
|
||||
|
||||
v0.1.5+
|
||||
|
||||
节点是否正在拖拽中
|
||||
|
||||
|
||||
|
||||
### 方法
|
||||
|
||||
#### addChildren(node)
|
||||
@@ -680,6 +739,38 @@ v0.1.1+
|
||||
|
||||
|
||||
|
||||
#### hide()
|
||||
|
||||
v0.1.5+
|
||||
|
||||
隐藏节点及其下级节点
|
||||
|
||||
|
||||
|
||||
#### show()
|
||||
|
||||
v0.1.5+
|
||||
|
||||
显示节点及其下级节点
|
||||
|
||||
|
||||
|
||||
#### isParent(node)
|
||||
|
||||
v0.1.5+
|
||||
|
||||
检测当前节点是否是某个节点的祖先节点
|
||||
|
||||
|
||||
|
||||
#### isBrother(node)
|
||||
|
||||
v0.1.5+
|
||||
|
||||
检测当前节点是否是某个节点的兄弟节点
|
||||
|
||||
|
||||
|
||||
## 内置工具方法
|
||||
|
||||
引用:
|
||||
|
||||
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>一个简单的web思维导图实现</title><link href="dist/css/app.d4d92b48.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.37b3d8f8.css" rel="preload" as="style"><link href="dist/js/app.a76c2c51.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.52f014f8.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.37b3d8f8.css" rel="stylesheet"><link href="dist/css/app.d4d92b48.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="dist/js/chunk-vendors.52f014f8.js"></script><script src="dist/js/app.a76c2c51.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>一个简单的web思维导图实现</title><link href="dist/css/app.ae564d8a.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.caacd1e0.css" rel="preload" as="style"><link href="dist/js/app.f545c734.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.523e8595.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.caacd1e0.css" rel="stylesheet"><link href="dist/css/app.ae564d8a.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="dist/js/chunk-vendors.523e8595.js"></script><script src="dist/js/app.f545c734.js"></script></body></html>
|
||||
3
simple-mind-map/READMD.md
Normal file
3
simple-mind-map/READMD.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# 一个web思维导图的简单实现
|
||||
|
||||
详细文档见:[https://github.com/wanglin2/mind-map](https://github.com/wanglin2/mind-map)
|
||||
@@ -876,9 +876,9 @@ const rootData = {
|
||||
export default {
|
||||
// ...data1,
|
||||
// ...data2,
|
||||
// ...data3,
|
||||
...data3,
|
||||
// ...data4,
|
||||
...rootData,
|
||||
// ...rootData,
|
||||
"theme": {
|
||||
"template": "minions",
|
||||
"config": {
|
||||
|
||||
@@ -9,6 +9,7 @@ import Command from './src/Command'
|
||||
import BatchExecution from './src/BatchExecution'
|
||||
import Export from './src/Export'
|
||||
import Select from './src/Select'
|
||||
import Drag from './src/Drag'
|
||||
import {
|
||||
layoutValueList
|
||||
} from './src/utils/constant'
|
||||
@@ -113,6 +114,11 @@ class MindMap {
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 拖动类
|
||||
this.drag = new Drag({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 批量执行类
|
||||
this.batchExecution = new BatchExecution()
|
||||
|
||||
@@ -321,6 +327,18 @@ class MindMap {
|
||||
let result = await this.doExport.export(...args)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 09:20:03
|
||||
* @Desc: 转换位置
|
||||
*/
|
||||
toPos(x, y) {
|
||||
return {
|
||||
x: x - this.elRect.left,
|
||||
y: y - this.elRect.top
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MindMap
|
||||
288
simple-mind-map/src/Drag.js
Normal file
288
simple-mind-map/src/Drag.js
Normal file
@@ -0,0 +1,288 @@
|
||||
import {
|
||||
bfsWalk,
|
||||
throttle
|
||||
} from './utils'
|
||||
import Base from './layouts/Base'
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 17:38:55
|
||||
* @Desc: 节点拖动类
|
||||
*/
|
||||
class Drag extends Base {
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 22:35:16
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor({
|
||||
mindMap
|
||||
}) {
|
||||
super(mindMap.renderer)
|
||||
this.mindMap = mindMap
|
||||
this.reset()
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 19:33:56
|
||||
* @Desc: 复位
|
||||
*/
|
||||
reset() {
|
||||
// 当前拖拽节点
|
||||
this.node = null
|
||||
// 当前重叠节点
|
||||
this.overlapNode = null
|
||||
// 当前上一个同级节点
|
||||
this.prevNode = null
|
||||
// 当前下一个同级节点
|
||||
this.nextNode = null
|
||||
// 画布的变换数据
|
||||
this.drawTransform = null
|
||||
// 克隆节点
|
||||
this.clone = null
|
||||
// 连接线
|
||||
this.line = null
|
||||
// 同级位置占位符
|
||||
this.placeholder = null
|
||||
// 鼠标按下位置和节点左上角的偏移量
|
||||
this.offsetX = 0
|
||||
this.offsetY = 0
|
||||
// 克隆节点左上角的坐标
|
||||
this.cloneNodeLeft = 0
|
||||
this.cloneNodeTop = 0
|
||||
// 当前鼠标是否按下
|
||||
this.isMousedown = false
|
||||
// 拖拽的鼠标位置变量
|
||||
this.mouseDownX = 0
|
||||
this.mouseDownY = 0
|
||||
this.mouseMoveX = 0
|
||||
this.mouseMoveY = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 22:36:36
|
||||
* @Desc: 绑定事件
|
||||
*/
|
||||
bindEvent() {
|
||||
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
|
||||
this.mindMap.on('node_mousedown', (node, e) => {
|
||||
if (e.which !== 1 || node.isRoot) {
|
||||
return
|
||||
}
|
||||
// 计算鼠标按下的位置距离节点左上角的距离
|
||||
this.drawTransform = this.mindMap.draw.transform()
|
||||
let {
|
||||
scaleX,
|
||||
scaleY,
|
||||
translateX,
|
||||
translateY
|
||||
} = this.drawTransform
|
||||
this.offsetX = e.clientX - (node.left * scaleX + translateX)
|
||||
this.offsetY = e.clientY - (node.top * scaleY + translateY)
|
||||
//
|
||||
this.node = node
|
||||
this.isMousedown = true
|
||||
let {
|
||||
x,
|
||||
y
|
||||
} = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseDownX = x
|
||||
this.mouseDownY = y
|
||||
})
|
||||
this.mindMap.on('mousemove', (e) => {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
let {
|
||||
x,
|
||||
y
|
||||
} = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseMoveX = x
|
||||
this.mouseMoveY = y
|
||||
if ((Math.abs(x - this.mouseDownX) <= 10 && Math.abs(y - this.mouseDownY) <= 10) && !this.node.isDrag) {
|
||||
return
|
||||
}
|
||||
this.mindMap.renderer.clearAllActive()
|
||||
this.onMove(x, y)
|
||||
})
|
||||
this.onMouseup = this.onMouseup.bind(this)
|
||||
this.mindMap.on('node_mouseup', this.onMouseup)
|
||||
this.mindMap.on('mouseup', this.onMouseup)
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 19:38:02
|
||||
* @Desc: 鼠标松开事件
|
||||
*/
|
||||
onMouseup() {
|
||||
if (!this.isMousedown) {
|
||||
return;
|
||||
}
|
||||
this.isMousedown = false
|
||||
this.node.isDrag = false
|
||||
this.node.show()
|
||||
this.removeCloneNode()
|
||||
// 存在重叠子节点,则移动作为其子节点
|
||||
if (this.overlapNode) {
|
||||
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
|
||||
this.mindMap.execCommand('MOVE_NODE_TO', this.node, this.overlapNode)
|
||||
} else if (this.prevNode) { // 存在前一个相邻节点,作为其下一个兄弟节点
|
||||
this.mindMap.renderer.setNodeActive(this.prevNode, false)
|
||||
this.mindMap.execCommand('INSERT_AFTER', this.node, this.prevNode)
|
||||
} else if (this.nextNode) { // 存在下一个相邻节点,作为其前一个兄弟节点
|
||||
this.mindMap.renderer.setNodeActive(this.nextNode, false)
|
||||
this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode)
|
||||
}
|
||||
this.reset()
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 19:34:53
|
||||
* @Desc: 创建克隆节点
|
||||
*/
|
||||
createCloneNode() {
|
||||
if (!this.clone) {
|
||||
// 节点
|
||||
this.clone = this.node.group.clone()
|
||||
this.clone.opacity(0.5)
|
||||
this.clone.css('z-index', 99999)
|
||||
this.node.isDrag = true
|
||||
this.node.hide()
|
||||
// 连接线
|
||||
this.line = this.draw.path()
|
||||
this.line.opacity(0.5)
|
||||
this.node.style.line(this.line)
|
||||
// 同级位置占位符
|
||||
this.placeholder = this.draw.rect().fill({
|
||||
color: this.node.style.merge('lineColor', true)
|
||||
})
|
||||
this.mindMap.draw.add(this.clone)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 19:35:16
|
||||
* @Desc: 移除克隆节点
|
||||
*/
|
||||
removeCloneNode() {
|
||||
if (!this.clone) {
|
||||
return
|
||||
}
|
||||
this.clone.remove()
|
||||
this.line.remove()
|
||||
this.placeholder.remove()
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 18:53:47
|
||||
* @Desc: 拖动中
|
||||
*/
|
||||
onMove(x, y) {
|
||||
if (!this.isMousedown) {
|
||||
return;
|
||||
}
|
||||
this.createCloneNode()
|
||||
let {
|
||||
scaleX,
|
||||
scaleY,
|
||||
translateX,
|
||||
translateY
|
||||
} = this.drawTransform
|
||||
this.cloneNodeLeft = x - this.offsetX
|
||||
this.cloneNodeTop = y - this.offsetY
|
||||
x = (this.cloneNodeLeft - translateX) / scaleX
|
||||
y = (this.cloneNodeTop - translateY) / scaleY
|
||||
let t = this.clone.transform()
|
||||
this.clone.translate(x - t.translateX, y - t.translateY)
|
||||
// 连接线
|
||||
let parent = this.node.parent
|
||||
this.line.plot(this.quadraticCurvePath(parent.left + parent.width / 2, parent.top + parent.height / 2, x + this.node.width / 2, y + this.node.height / 2))
|
||||
this.checkOverlapNode()
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 10:20:43
|
||||
* @Desc: 检测重叠节点
|
||||
*/
|
||||
checkOverlapNode() {
|
||||
if (!this.drawTransform) {
|
||||
return
|
||||
}
|
||||
let {
|
||||
scaleX,
|
||||
scaleY,
|
||||
translateX,
|
||||
translateY
|
||||
} = this.drawTransform
|
||||
let checkRight = this.cloneNodeLeft + this.node.width * scaleX
|
||||
let checkBottom = this.cloneNodeTop + this.node.height * scaleX
|
||||
this.overlapNode = null
|
||||
this.prevNode = null
|
||||
this.nextNode = null
|
||||
this.placeholder.size(0, 0)
|
||||
bfsWalk(this.mindMap.renderer.root, (node) => {
|
||||
if (node.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(node, false)
|
||||
}
|
||||
if (node === this.node || this.node.isParent(node)) {
|
||||
return
|
||||
}
|
||||
if (this.overlapNode || this.prevNode && this.nextNode) {
|
||||
return
|
||||
}
|
||||
let {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height
|
||||
} = node
|
||||
let _left = left
|
||||
let _top = top
|
||||
let _bottom = top + height
|
||||
let right = (left + width) * scaleX + translateX
|
||||
let bottom = (top + height) * scaleY + translateY
|
||||
left = left * scaleX + translateX
|
||||
top = top * scaleY + translateY
|
||||
// 检测是否重叠
|
||||
if (!this.overlapNode) {
|
||||
if (
|
||||
left <= checkRight && right >= this.cloneNodeLeft &&
|
||||
top <= checkBottom && bottom >= this.cloneNodeTop
|
||||
) {
|
||||
this.overlapNode = node
|
||||
}
|
||||
}
|
||||
// 检测兄弟节点位置
|
||||
if (!this.prevNode && !this.nextNode && this.node.isBrother(node)) {
|
||||
if (left <= checkRight && right >= this.cloneNodeLeft) {
|
||||
if (this.cloneNodeTop > bottom && this.cloneNodeTop <= bottom + 10) {
|
||||
this.prevNode = node
|
||||
this.placeholder.size(node.width, 10).move(_left, _bottom)
|
||||
} else if (checkBottom < top && checkBottom >= top - 10) {
|
||||
this.nextNode = node
|
||||
this.placeholder.size(node.width, 10).move(_left, _top - 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if (this.overlapNode) {
|
||||
this.mindMap.renderer.setNodeActive(this.overlapNode, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Drag
|
||||
@@ -55,6 +55,8 @@ class Node {
|
||||
this.left = opt.left || 0
|
||||
// top
|
||||
this.top = opt.top || 0
|
||||
// 是否正在拖拽中
|
||||
this.isDrag = false
|
||||
// 父节点
|
||||
this.parent = opt.parent || null
|
||||
// 子节点
|
||||
@@ -676,6 +678,50 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 18:39:14
|
||||
* @Desc: 隐藏节点
|
||||
*/
|
||||
hide() {
|
||||
this.group.hide()
|
||||
if (this.parent) {
|
||||
let index = this.parent.children.indexOf(this)
|
||||
this.parent._lines[index].hide()
|
||||
}
|
||||
// 子节点
|
||||
if (this.children && this.children.length) {
|
||||
asyncRun(this.children.map((item) => {
|
||||
return () =>{
|
||||
item.hide()
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 18:39:14
|
||||
* @Desc: 显示节点
|
||||
*/
|
||||
show() {
|
||||
this.group.show()
|
||||
if (this.parent) {
|
||||
let index = this.parent.children.indexOf(this)
|
||||
this.parent._lines[index].show()
|
||||
}
|
||||
// 子节点
|
||||
if (this.children && this.children.length) {
|
||||
asyncRun(this.children.map((item) => {
|
||||
return () =>{
|
||||
item.show()
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-10 22:01:53
|
||||
@@ -801,6 +847,42 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-25 09:51:37
|
||||
* @Desc: 检测当前节点是否是某个节点的祖先节点
|
||||
*/
|
||||
isParent(node) {
|
||||
if (this === node) {
|
||||
return false
|
||||
}
|
||||
let parent = node.parent
|
||||
while(parent) {
|
||||
if (this === parent) {
|
||||
return true
|
||||
}
|
||||
parent = parent.parent
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-25 10:32:34
|
||||
* @Desc: 检测当前节点是否是某个节点的兄弟节点
|
||||
*/
|
||||
isBrother(node) {
|
||||
if (!this.parent || this === node) {
|
||||
return false
|
||||
}
|
||||
return this.parent.children.find((item) => {
|
||||
return item === node
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-20 22:51:57
|
||||
|
||||
@@ -77,7 +77,6 @@ class Render {
|
||||
// 清除激活状态
|
||||
if (this.activeNodeList.length > 0) {
|
||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||||
this.mindMap.emit('node_active', null, [])
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -109,6 +108,13 @@ class Render {
|
||||
// 下移节点
|
||||
this.downNode = this.downNode.bind(this)
|
||||
this.mindMap.command.add('DOWN_NODE', this.downNode)
|
||||
// 移动节点
|
||||
this.insertAfter = this.insertAfter.bind(this)
|
||||
this.mindMap.command.add('INSERT_AFTER', this.insertAfter)
|
||||
this.insertBefore = this.insertBefore.bind(this)
|
||||
this.mindMap.command.add('INSERT_BEFORE', this.insertBefore)
|
||||
this.moveNodeTo = this.moveNodeTo.bind(this)
|
||||
this.mindMap.command.add('MOVE_NODE_TO', this.moveNodeTo)
|
||||
// 删除节点
|
||||
this.removeNode = this.removeNode.bind(this)
|
||||
this.mindMap.command.add('REMOVE_NODE', this.removeNode)
|
||||
@@ -240,6 +246,9 @@ class Render {
|
||||
* @Desc: 清除当前所有激活节点,并会触发事件
|
||||
*/
|
||||
clearAllActive() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
}
|
||||
this.clearActive()
|
||||
this.mindMap.emit('node_active', null, [])
|
||||
}
|
||||
@@ -457,6 +466,88 @@ class Render {
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-25 10:51:34
|
||||
* @Desc: 将节点移动到另一个节点的前面
|
||||
*/
|
||||
insertBefore(node, exist) {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
let parent = node.parent
|
||||
let childList = parent.children
|
||||
// 要移动节点的索引
|
||||
let index = childList.findIndex((item) => {
|
||||
return item === node
|
||||
})
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
// 目标节点的索引
|
||||
let existIndex = childList.findIndex((item) => {
|
||||
return item === exist
|
||||
})
|
||||
if (existIndex === -1) {
|
||||
return
|
||||
}
|
||||
// 当前节点在目标节点前面
|
||||
if (index < existIndex) {
|
||||
existIndex = existIndex - 1
|
||||
} else {
|
||||
existIndex = existIndex
|
||||
}
|
||||
// 节点实例
|
||||
childList.splice(index, 1)
|
||||
childList.splice(existIndex, 0, node)
|
||||
// 节点数据
|
||||
parent.nodeData.children.splice(index, 1)
|
||||
parent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-25 10:51:34
|
||||
* @Desc: 将节点移动到另一个节点的后面
|
||||
*/
|
||||
insertAfter(node, exist) {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
let parent = node.parent
|
||||
let childList = parent.children
|
||||
// 要移动节点的索引
|
||||
let index = childList.findIndex((item) => {
|
||||
return item === node
|
||||
})
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
// 目标节点的索引
|
||||
let existIndex = childList.findIndex((item) => {
|
||||
return item === exist
|
||||
})
|
||||
if (existIndex === -1) {
|
||||
return
|
||||
}
|
||||
// 当前节点在目标节点前面
|
||||
if (index < existIndex) {
|
||||
existIndex = existIndex
|
||||
} else {
|
||||
existIndex = existIndex + 1
|
||||
}
|
||||
// 节点实例
|
||||
childList.splice(index, 1)
|
||||
childList.splice(existIndex, 0, node)
|
||||
// 节点数据
|
||||
parent.nodeData.children.splice(index, 1)
|
||||
parent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:40:39
|
||||
@@ -533,6 +624,24 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-24 16:54:01
|
||||
* @Desc: 移动一个节点作为另一个节点的子节点
|
||||
*/
|
||||
moveNodeTo(node, toNode) {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
let copyData = copyNodeTree({}, node)
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
toNode.nodeData.children.push(copyData)
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-15 20:09:39
|
||||
|
||||
@@ -35,7 +35,7 @@ class Select {
|
||||
return
|
||||
}
|
||||
this.isMousedown = true
|
||||
let { x, y } = this.toPos(e.clientX, e.clientY)
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseDownX = x
|
||||
this.mouseDownY = y
|
||||
this.createRect(x, y)
|
||||
@@ -44,7 +44,7 @@ class Select {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
let { x, y } = this.toPos(e.clientX, e.clientY)
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseMoveX = x
|
||||
this.mouseMoveY = y
|
||||
if (Math.abs(x - this.mouseDownX) <= 10 && Math.abs(y - this.mouseDownY) <= 10) {
|
||||
@@ -136,18 +136,6 @@ class Select {
|
||||
}).plot([[x, y]])
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 09:20:03
|
||||
* @Desc: 转换位置
|
||||
*/
|
||||
toPos(x, y) {
|
||||
return {
|
||||
x: x - this.mindMap.elRect.left,
|
||||
y: y - this.mindMap.elRect.top
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 10:20:43
|
||||
@@ -175,7 +163,7 @@ class Select {
|
||||
if (node.nodeData.data.isActive) {
|
||||
return ;
|
||||
}
|
||||
this.mindMap.execCommand('SET_NODE_ACTIVE', node, true)
|
||||
this.mindMap.renderer.setNodeActive(node, true)
|
||||
this.mindMap.renderer.addActiveNode(node)
|
||||
})
|
||||
} else if (node.nodeData.data.isActive) {
|
||||
@@ -183,7 +171,7 @@ class Select {
|
||||
if (!node.nodeData.data.isActive) {
|
||||
return ;
|
||||
}
|
||||
this.mindMap.execCommand('SET_NODE_ACTIVE', node, false)
|
||||
this.mindMap.renderer.setNodeActive(node, false)
|
||||
this.mindMap.renderer.removeActiveNode(node)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -104,9 +104,9 @@ class Style {
|
||||
* @Date: 2021-04-13 08:14:34
|
||||
* @Desc: html文字节点
|
||||
*/
|
||||
domText(node) {
|
||||
domText(node, fontSizeScale = 1) {
|
||||
node.style.fontFamily = this.merge('fontFamily')
|
||||
node.style.fontSize = this.merge('fontSize') + 'px'
|
||||
node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px'
|
||||
node.style.fontWeight = this.merge('fontWeight') || 'normal'
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ export default class TextEdit {
|
||||
this.textEditNode.setAttribute('contenteditable', true)
|
||||
document.body.appendChild(this.textEditNode)
|
||||
}
|
||||
node.style.domText(this.textEditNode)
|
||||
node.style.domText(this.textEditNode, this.mindMap.view.scale)
|
||||
this.textEditNode.innerHTML = node.nodeData.data.text.split(/\n/img).join('<br>')
|
||||
this.textEditNode.style.minWidth = rect.width + 10 + 'px'
|
||||
this.textEditNode.style.minHeight = rect.height + 6 + 'px'
|
||||
|
||||
@@ -19,6 +19,7 @@ class View {
|
||||
this.sy = 0
|
||||
this.x = 0
|
||||
this.y = 0
|
||||
this.firstDrag = true
|
||||
this.setTransformData(this.mindMap.opt.viewData)
|
||||
this.bind()
|
||||
}
|
||||
@@ -49,10 +50,18 @@ class View {
|
||||
this.sy = this.y
|
||||
})
|
||||
this.mindMap.event.on('drag', (e, event) => {
|
||||
if (this.firstDrag) {
|
||||
this.firstDrag = false
|
||||
// 清除激活节点
|
||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||||
}
|
||||
this.x = this.sx + event.mousemoveOffset.x
|
||||
this.y = this.sy + event.mousemoveOffset.y
|
||||
this.transform()
|
||||
})
|
||||
this.mindMap.event.on('mouseup', () => {
|
||||
this.firstDrag = true
|
||||
})
|
||||
// 放大缩小视图
|
||||
this.mindMap.event.on('mousewheel', (e, dir) => {
|
||||
// // 放大
|
||||
@@ -97,6 +106,8 @@ class View {
|
||||
this.mindMap.draw.transform({
|
||||
...viewData.transform
|
||||
})
|
||||
this.mindMap.emit('view_data_change', this.getTransformData())
|
||||
this.mindMap.emit('scale', this.scale)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +141,7 @@ class View {
|
||||
transform() {
|
||||
this.mindMap.draw.transform({
|
||||
scale: this.scale,
|
||||
origin: 'left center',
|
||||
// origin: 'center center',
|
||||
translate: [this.x, this.y],
|
||||
})
|
||||
this.mindMap.emit('view_data_change', this.getTransformData())
|
||||
@@ -142,7 +153,6 @@ class View {
|
||||
* @Desc: 恢复
|
||||
*/
|
||||
reset() {
|
||||
// let t = this.mindMap.draw.transform()
|
||||
this.scale = 1
|
||||
this.x = 0
|
||||
this.y = 0
|
||||
|
||||
@@ -27,12 +27,18 @@ export const walk = (root, parent, beforeCallback, afterCallback, isRoot, layerI
|
||||
export const bfsWalk = (root, callback) => {
|
||||
callback(root)
|
||||
let stack = [root]
|
||||
let isStop = false
|
||||
while (stack.length) {
|
||||
if (isStop) {
|
||||
break
|
||||
}
|
||||
let cur = stack.shift()
|
||||
if (cur.children && cur.children.length) {
|
||||
cur.children.forEach((item) => {
|
||||
stack.push(item)
|
||||
callback(item)
|
||||
if(callback(item) === 'stop') {
|
||||
isStop = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,6 @@ export default {
|
||||
storeData(data)
|
||||
})
|
||||
this.$bus.$on('view_data_change', (data) => {
|
||||
console.log(JSON.stringify(data))
|
||||
storeConfig({
|
||||
view: data,
|
||||
})
|
||||
|
||||
@@ -32,12 +32,22 @@ export default {
|
||||
mindMap(val, oldVal) {
|
||||
if (val && !oldVal) {
|
||||
this.mindMap.on("scale", (scale) => {
|
||||
this.scaleNum = (scale * 100).toFixed(0);
|
||||
this.scaleNum = this.toPer(scale);
|
||||
});
|
||||
this.scaleNum = this.toPer(this.mindMap.view.scale)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-25 14:20:16
|
||||
* @Desc: 转换成百分数
|
||||
*/
|
||||
toPer(scale) {
|
||||
return (scale * 100).toFixed(0)
|
||||
},
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 17:10:34
|
||||
|
||||
Reference in New Issue
Block a user