From 02e2d432dd8e7ea3d5a48175bbe9829dec3c4a28 Mon Sep 17 00:00:00 2001
From: wanglin2 <1013335014@qq.com>
Date: Fri, 28 Apr 2023 17:10:39 +0800
Subject: [PATCH 1/6] =?UTF-8?q?Feature=EF=BC=9A=E5=85=B3=E8=81=94=E7=BA=BF?=
=?UTF-8?q?=E6=94=AF=E6=8C=81=E6=96=87=E6=9C=AC=E5=BC=80=E5=8F=91=E4=B8=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
simple-mind-map/index.js | 4 +-
simple-mind-map/src/AssociativeLine.js | 325 ++++--------------
simple-mind-map/src/KeyCommand.js | 2 +-
simple-mind-map/src/themes/default.js | 8 +
.../src/utils/associativeLineControls.js | 240 +++++++++++++
.../src/utils/associativeLineText.js | 149 ++++++++
6 files changed, 477 insertions(+), 251 deletions(-)
create mode 100644 simple-mind-map/src/utils/associativeLineControls.js
create mode 100644 simple-mind-map/src/utils/associativeLineText.js
diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js
index b60c3417..32a23c7b 100644
--- a/simple-mind-map/index.js
+++ b/simple-mind-map/index.js
@@ -120,7 +120,9 @@ const defaultOpt = {
// }
],
// 节点最大缓存数量
- maxNodeCacheCount: 1000
+ maxNodeCacheCount: 1000,
+ // 关联线默认文字
+ defaultAssociativeLineText: '关联'
}
// 思维导图
diff --git a/simple-mind-map/src/AssociativeLine.js b/simple-mind-map/src/AssociativeLine.js
index d915084c..71bf7ea3 100644
--- a/simple-mind-map/src/AssociativeLine.js
+++ b/simple-mind-map/src/AssociativeLine.js
@@ -3,13 +3,13 @@ import { v4 as uuid } from 'uuid'
import {
getAssociativeLineTargetIndex,
computeCubicBezierPathPoints,
- joinCubicBezierPath,
cubicBezierPath,
getNodePoint,
computeNodePoints,
- getNodeLinePath,
- getDefaultControlPointOffsets
+ getNodeLinePath
} from './utils/associativeLineUtils'
+import associativeLineControlsMethods from './utils/associativeLineControls'
+import associativeLineTextMethods from './utils/associativeLineText'
// 关联线类
class AssociativeLine {
@@ -46,6 +46,14 @@ class AssociativeLine {
}
// 节流一下,不然很卡
this.checkOverlapNode = throttle(this.checkOverlapNode, 100, this)
+ // 控制点相关方法
+ Object.keys(associativeLineControlsMethods).forEach(item => {
+ this[item] = associativeLineControlsMethods[item].bind(this)
+ })
+ // 关联线文字相关方法
+ Object.keys(associativeLineTextMethods).forEach(item => {
+ this[item] = associativeLineTextMethods[item].bind(this)
+ })
this.bindEvent()
}
@@ -177,36 +185,46 @@ class AssociativeLine {
.stroke({ width: associativeLineActiveWidth, color: 'transparent' })
.fill({ color: 'none' })
clickPath.plot(pathStr)
+ // 文字
+ let text = this.createText({ path, clickPath, node, toNode, startPoint, endPoint, controlPoints })
// 点击事件
clickPath.click(e => {
e.stopPropagation()
- // 如果当前存在激活节点,那么取消激活节点
- if (this.mindMap.renderer.activeNodeList.length > 0) {
- this.clearActiveNodes()
- } else {
- // 否则清除当前的关联线的激活状态,如果有的话
- this.clearActiveLine()
- // 保存当前激活的关联线信息
- this.activeLine = [path, clickPath, node, toNode]
- // 让不可见的点击线显示
- clickPath.stroke({ color: associativeLineActiveColor })
- // 渲染控制点和连线
- this.renderControls(
- startPoint,
- endPoint,
- controlPoints[0],
- controlPoints[1]
- )
- this.mindMap.emit(
- 'associative_line_click',
- path,
- clickPath,
- node,
- toNode
- )
- }
+ this.setActiveLine({ path, clickPath, text, node, toNode, startPoint, endPoint, controlPoints })
})
- this.lineList.push([path, clickPath, node, toNode])
+ // 渲染关联线文字
+ this.renderText(this.getText(node, toNode), path, text)
+ this.lineList.push([path, clickPath, text, node, toNode])
+ }
+
+ // 激活某根关联线
+ setActiveLine({ path, clickPath, text, node, toNode, startPoint, endPoint, controlPoints }) {
+ let {
+ associativeLineActiveColor
+ } = this.mindMap.themeConfig
+ // 如果当前存在激活节点,那么取消激活节点
+ if (this.mindMap.renderer.activeNodeList.length > 0) {
+ this.clearActiveNodes()
+ } else {
+ // 否则清除当前的关联线的激活状态,如果有的话
+ this.clearActiveLine()
+ // 保存当前激活的关联线信息
+ this.activeLine = [path, clickPath, text, node, toNode]
+ // 让不可见的点击线显示
+ clickPath.stroke({ color: associativeLineActiveColor })
+ // 如果没有输入过关联线文字,那么显示默认文字
+ if (!this.getText(node, toNode)) {
+ this.renderText(this.mindMap.opt.defaultAssociativeLineText, path, text)
+ }
+ // 渲染控制点和连线
+ this.renderControls(
+ startPoint,
+ endPoint,
+ controlPoints[0],
+ controlPoints[1]
+ )
+ this.mindMap.emit('associative_line_click', path, clickPath, node, toNode)
+ }
}
// 移除所有连接线
@@ -214,6 +232,7 @@ class AssociativeLine {
this.lineList.forEach(line => {
line[0].remove()
line[1].remove()
+ line[2].remove()
})
this.lineList = []
}
@@ -253,7 +272,8 @@ class AssociativeLine {
updateCreatingLine(e) {
let { x, y } = this.getTransformedEventPos(e)
let startPoint = getNodePoint(this.creatingStartNode)
- let pathStr = cubicBezierPath(startPoint.x, startPoint.y, x, y)
+ let offsetX = x > startPoint.x ? -10 : 10
+ let pathStr = cubicBezierPath(startPoint.x, startPoint.y, x + offsetX, y)
this.creatingLine.plot(pathStr)
this.checkOverlapNode(x, y)
}
@@ -349,20 +369,33 @@ class AssociativeLine {
// 删除连接线
removeLine() {
if (!this.activeLine) return
- let [, , node, toNode] = this.activeLine
+ let [, , , node, toNode] = this.activeLine
this.removeControls()
- let { associativeLineTargets, associativeLineTargetControlOffsets } =
+ let { associativeLineTargets, associativeLineTargetControlOffsets, associativeLineText } =
node.nodeData.data
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
+ // 更新关联线文本数据
+ let newAssociativeLineText = {}
+ if (associativeLineText) {
+ Object.keys(associativeLineText).forEach((item) => {
+ if (item !== toNode.nodeData.data.id) {
+ newAssociativeLineText[item] = associativeLineText[item]
+ }
+ })
+ }
this.mindMap.execCommand('SET_NODE_DATA', node, {
+ // 目标
associativeLineTargets: associativeLineTargets.filter((_, index) => {
return index !== targetIndex
}),
+ // 偏移量
associativeLineTargetControlOffsets: associativeLineTargetControlOffsets
? associativeLineTargetControlOffsets.filter((_, index) => {
return index !== targetIndex
})
- : []
+ : [],
+ // 文本
+ associativeLineText: newAssociativeLineText
})
}
@@ -376,9 +409,16 @@ class AssociativeLine {
// 清除激活的线
clearActiveLine() {
if (this.activeLine) {
- this.activeLine[1].stroke({
+ let [, clickPath, text, node, toNode] = this.activeLine
+ clickPath.stroke({
color: 'transparent'
})
+ // 隐藏关联线文本编辑框
+ this.hideEditTextBox()
+ // 如果当前关联线没有文字,则清空文字节点
+ if (!this.getText(node, toNode)) {
+ text.clear()
+ }
this.activeLine = null
this.removeControls()
}
@@ -391,6 +431,7 @@ class AssociativeLine {
this.lineList.forEach(line => {
line[0].hide()
line[1].hide()
+ line[2].hide()
})
this.hideControls()
}
@@ -401,225 +442,11 @@ class AssociativeLine {
this.lineList.forEach(line => {
line[0].show()
line[1].show()
+ line[2].show()
})
this.showControls()
this.isNodeDragging = false
}
-
- // 创建控制点、连线节点
- createControlNodes() {
- let { associativeLineActiveColor } = this.mindMap.themeConfig
- // 连线
- this.controlLine1 = this.draw
- .line()
- .stroke({ color: associativeLineActiveColor, width: 2 })
- this.controlLine2 = this.draw
- .line()
- .stroke({ color: associativeLineActiveColor, width: 2 })
- // 控制点
- this.controlPoint1 = this.createOneControlNode('controlPoint1')
- this.controlPoint2 = this.createOneControlNode('controlPoint2')
- }
-
- // 创建控制点
- createOneControlNode(pointKey) {
- let { associativeLineActiveColor } = this.mindMap.themeConfig
- return this.draw
- .circle(this.controlPointDiameter)
- .stroke({ color: associativeLineActiveColor })
- .fill({ color: '#fff' })
- .click(e => {
- e.stopPropagation()
- })
- .mousedown(e => {
- this.onControlPointMousedown(e, pointKey)
- })
- }
-
- // 控制点的鼠标按下事件
- onControlPointMousedown(e, pointKey) {
- e.stopPropagation()
- this.isControlPointMousedown = true
- this.mousedownControlPointKey = pointKey
- }
-
- // 控制点的鼠标移动事件
- onControlPointMousemove(e) {
- if (
- !this.isControlPointMousedown ||
- !this.mousedownControlPointKey ||
- !this[this.mousedownControlPointKey]
- )
- return
- e.stopPropagation()
- e.preventDefault()
- let radius = this.controlPointDiameter / 2
- // 转换鼠标当前的位置
- let { x, y } = this.getTransformedEventPos(e)
- this.controlPointMousemoveState.pos = {
- x,
- y
- }
- // 更新当前拖拽的控制点的位置
- this[this.mousedownControlPointKey].x(x - radius).y(y - radius)
- let [path, clickPath, node, toNode] = this.activeLine
- let [startPoint, endPoint] = computeNodePoints(node, toNode)
- this.controlPointMousemoveState.startPoint = startPoint
- this.controlPointMousemoveState.endPoint = endPoint
- let targetIndex = getAssociativeLineTargetIndex(node, toNode)
- this.controlPointMousemoveState.targetIndex = targetIndex
- let offsets = []
- let associativeLineTargetControlOffsets = node.nodeData.data.associativeLineTargetControlOffsets
- if (!associativeLineTargetControlOffsets) {
- // 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
- offsets = getDefaultControlPointOffsets(startPoint, endPoint)
- } else {
- offsets = associativeLineTargetControlOffsets[targetIndex]
- }
- let point1 = null
- let point2 = null
- // 拖拽的是控制点1
- if (this.mousedownControlPointKey === 'controlPoint1') {
- point1 = {
- x,
- y
- }
- point2 = {
- x: endPoint.x + offsets[1].x,
- y: endPoint.y + offsets[1].y
- }
- // 更新控制点1的连线
- this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
- } else {
- // 拖拽的是控制点2
- point1 = {
- x: startPoint.x + offsets[0].x,
- y: startPoint.y + offsets[0].y
- }
- point2 = {
- x,
- y
- }
- // 更新控制点2的连线
- this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
- }
- // 更新关联线
- let pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
- path.plot(pathStr)
- clickPath.plot(pathStr)
- }
-
- // 控制点的鼠标移动事件
- onControlPointMouseup(e) {
- if (!this.isControlPointMousedown) return
- e.stopPropagation()
- e.preventDefault()
- let { pos, startPoint, endPoint, targetIndex } =
- this.controlPointMousemoveState
- let [, , node] = this.activeLine
- let offsetList = []
- let associativeLineTargetControlOffsets = node.nodeData.data.associativeLineTargetControlOffsets
- if (!associativeLineTargetControlOffsets) {
- // 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
- offsetList[targetIndex] = getDefaultControlPointOffsets(startPoint, endPoint)
- } else {
- offsetList = associativeLineTargetControlOffsets
- }
- let offset1 = null
- let offset2 = null
- if (this.mousedownControlPointKey === 'controlPoint1') {
- // 更新控制点1数据
- offset1 = {
- x: pos.x - startPoint.x,
- y: pos.y - startPoint.y
- }
- offset2 = offsetList[targetIndex][1]
- } else {
- // 更新控制点2数据
- offset1 = offsetList[targetIndex][0]
- offset2 = {
- x: pos.x - endPoint.x,
- y: pos.y - endPoint.y
- }
- }
- offsetList[targetIndex] = [offset1, offset2]
- this.mindMap.execCommand('SET_NODE_DATA', node, {
- associativeLineTargetControlOffsets: offsetList
- })
- // 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后
- setTimeout(() => {
- this.resetControlPoint()
- }, 0)
- }
-
- // 复位控制点移动
- resetControlPoint() {
- this.isControlPointMousedown = false
- this.mousedownControlPointKey = ''
- this.controlPointMousemoveState = {
- pos: null,
- startPoint: null,
- endPoint: null,
- targetIndex: ''
- }
- }
-
- // 渲染控制点
- renderControls(startPoint, endPoint, point1, point2) {
- if (!this.controlLine1) {
- this.createControlNodes()
- }
- let radius = this.controlPointDiameter / 2
- // 控制点和起终点的连线
- this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
- this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
- // 控制点
- this.controlPoint1.x(point1.x - radius).y(point1.y - radius)
- this.controlPoint2.x(point2.x - radius).y(point2.y - radius)
- }
-
- // 删除控制点
- removeControls() {
- if (!this.controlLine1) return
- ;[
- this.controlLine1,
- this.controlLine2,
- this.controlPoint1,
- this.controlPoint2
- ].forEach(item => {
- item.remove()
- })
- this.controlLine1 = null
- this.controlLine2 = null
- this.controlPoint1 = null
- this.controlPoint2 = null
- }
-
- // 隐藏控制点
- hideControls() {
- if (!this.controlLine1) return
- ;[
- this.controlLine1,
- this.controlLine2,
- this.controlPoint1,
- this.controlPoint2
- ].forEach(item => {
- item.hide()
- })
- }
-
- // 显示控制点
- showControls() {
- if (!this.controlLine1) return
- ;[
- this.controlLine1,
- this.controlLine2,
- this.controlPoint1,
- this.controlPoint2
- ].forEach(item => {
- item.show()
- })
- }
}
AssociativeLine.instanceName = 'associativeLine'
diff --git a/simple-mind-map/src/KeyCommand.js b/simple-mind-map/src/KeyCommand.js
index 9130e73e..579e219a 100644
--- a/simple-mind-map/src/KeyCommand.js
+++ b/simple-mind-map/src/KeyCommand.js
@@ -46,7 +46,7 @@ export default class KeyCommand {
if (this.mindMap.richText && this.mindMap.richText.showTextEdit) {
return
}
- if (this.mindMap.renderer.textEdit.showTextEdit) {
+ if (this.mindMap.renderer.textEdit.showTextEdit || (this.mindMap.associativeLine && this.mindMap.associativeLine.showTextEdit)) {
return
}
this.isInSvg = false
diff --git a/simple-mind-map/src/themes/default.js b/simple-mind-map/src/themes/default.js
index 0f2ad5ad..2b5e95cd 100644
--- a/simple-mind-map/src/themes/default.js
+++ b/simple-mind-map/src/themes/default.js
@@ -34,6 +34,14 @@ export default {
associativeLineActiveWidth: 8,
// 关联线激活状态的颜色
associativeLineActiveColor: 'rgba(2, 167, 240, 1)',
+ // 关联线文字颜色
+ associativeLineTextColor: 'rgb(51, 51, 51)',
+ // 关联线文字大小
+ associativeLineTextFontSize: 14,
+ // 关联线文字行高
+ associativeLineTextLineHeight: 1.2,
+ // 关联线文字字体
+ associativeLineTextFontFamily: '微软雅黑, Microsoft YaHei',
// 背景颜色
backgroundColor: '#fafafa',
// 背景图片
diff --git a/simple-mind-map/src/utils/associativeLineControls.js b/simple-mind-map/src/utils/associativeLineControls.js
new file mode 100644
index 00000000..7b8432ef
--- /dev/null
+++ b/simple-mind-map/src/utils/associativeLineControls.js
@@ -0,0 +1,240 @@
+import {
+ getAssociativeLineTargetIndex,
+ joinCubicBezierPath,
+ computeNodePoints,
+ getDefaultControlPointOffsets
+} from './associativeLineUtils'
+
+// 创建控制点、连线节点
+function createControlNodes() {
+ let { associativeLineActiveColor } = this.mindMap.themeConfig
+ // 连线
+ this.controlLine1 = this.draw
+ .line()
+ .stroke({ color: associativeLineActiveColor, width: 2 })
+ this.controlLine2 = this.draw
+ .line()
+ .stroke({ color: associativeLineActiveColor, width: 2 })
+ // 控制点
+ this.controlPoint1 = this.createOneControlNode('controlPoint1')
+ this.controlPoint2 = this.createOneControlNode('controlPoint2')
+}
+
+// 创建控制点
+function createOneControlNode(pointKey) {
+ let { associativeLineActiveColor } = this.mindMap.themeConfig
+ return this.draw
+ .circle(this.controlPointDiameter)
+ .stroke({ color: associativeLineActiveColor })
+ .fill({ color: '#fff' })
+ .click(e => {
+ e.stopPropagation()
+ })
+ .mousedown(e => {
+ this.onControlPointMousedown(e, pointKey)
+ })
+}
+
+// 控制点的鼠标按下事件
+function onControlPointMousedown(e, pointKey) {
+ e.stopPropagation()
+ this.isControlPointMousedown = true
+ this.mousedownControlPointKey = pointKey
+}
+
+// 控制点的鼠标移动事件
+function onControlPointMousemove(e) {
+ if (
+ !this.isControlPointMousedown ||
+ !this.mousedownControlPointKey ||
+ !this[this.mousedownControlPointKey]
+ )
+ return
+ e.stopPropagation()
+ e.preventDefault()
+ let radius = this.controlPointDiameter / 2
+ // 转换鼠标当前的位置
+ let { x, y } = this.getTransformedEventPos(e)
+ this.controlPointMousemoveState.pos = {
+ x,
+ y
+ }
+ // 更新当前拖拽的控制点的位置
+ this[this.mousedownControlPointKey].x(x - radius).y(y - radius)
+ let [path, clickPath, text, node, toNode] = this.activeLine
+ let [startPoint, endPoint] = computeNodePoints(node, toNode)
+ this.controlPointMousemoveState.startPoint = startPoint
+ this.controlPointMousemoveState.endPoint = endPoint
+ let targetIndex = getAssociativeLineTargetIndex(node, toNode)
+ this.controlPointMousemoveState.targetIndex = targetIndex
+ let offsets = []
+ let associativeLineTargetControlOffsets =
+ node.nodeData.data.associativeLineTargetControlOffsets
+ if (!associativeLineTargetControlOffsets) {
+ // 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
+ offsets = getDefaultControlPointOffsets(startPoint, endPoint)
+ } else {
+ offsets = associativeLineTargetControlOffsets[targetIndex]
+ }
+ let point1 = null
+ let point2 = null
+ // 拖拽的是控制点1
+ if (this.mousedownControlPointKey === 'controlPoint1') {
+ point1 = {
+ x,
+ y
+ }
+ point2 = {
+ x: endPoint.x + offsets[1].x,
+ y: endPoint.y + offsets[1].y
+ }
+ // 更新控制点1的连线
+ this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
+ } else {
+ // 拖拽的是控制点2
+ point1 = {
+ x: startPoint.x + offsets[0].x,
+ y: startPoint.y + offsets[0].y
+ }
+ point2 = {
+ x,
+ y
+ }
+ // 更新控制点2的连线
+ this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
+ }
+ // 更新关联线
+ let pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
+ path.plot(pathStr)
+ clickPath.plot(pathStr)
+ this.updateTextPos(path, text)
+}
+
+// 控制点的鼠标移动事件
+function onControlPointMouseup(e) {
+ if (!this.isControlPointMousedown) return
+ e.stopPropagation()
+ e.preventDefault()
+ let { pos, startPoint, endPoint, targetIndex } =
+ this.controlPointMousemoveState
+ let [, , , node] = this.activeLine
+ let offsetList = []
+ let associativeLineTargetControlOffsets =
+ node.nodeData.data.associativeLineTargetControlOffsets
+ if (!associativeLineTargetControlOffsets) {
+ // 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
+ offsetList[targetIndex] = getDefaultControlPointOffsets(
+ startPoint,
+ endPoint
+ )
+ } else {
+ offsetList = associativeLineTargetControlOffsets
+ }
+ let offset1 = null
+ let offset2 = null
+ if (this.mousedownControlPointKey === 'controlPoint1') {
+ // 更新控制点1数据
+ offset1 = {
+ x: pos.x - startPoint.x,
+ y: pos.y - startPoint.y
+ }
+ offset2 = offsetList[targetIndex][1]
+ } else {
+ // 更新控制点2数据
+ offset1 = offsetList[targetIndex][0]
+ offset2 = {
+ x: pos.x - endPoint.x,
+ y: pos.y - endPoint.y
+ }
+ }
+ offsetList[targetIndex] = [offset1, offset2]
+ this.mindMap.execCommand('SET_NODE_DATA', node, {
+ associativeLineTargetControlOffsets: offsetList
+ })
+ // 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后
+ setTimeout(() => {
+ this.resetControlPoint()
+ }, 0)
+}
+
+// 复位控制点移动
+function resetControlPoint() {
+ this.isControlPointMousedown = false
+ this.mousedownControlPointKey = ''
+ this.controlPointMousemoveState = {
+ pos: null,
+ startPoint: null,
+ endPoint: null,
+ targetIndex: ''
+ }
+}
+
+// 渲染控制点
+function renderControls(startPoint, endPoint, point1, point2) {
+ if (!this.controlLine1) {
+ this.createControlNodes()
+ }
+ let radius = this.controlPointDiameter / 2
+ // 控制点和起终点的连线
+ this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
+ this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
+ // 控制点
+ this.controlPoint1.x(point1.x - radius).y(point1.y - radius)
+ this.controlPoint2.x(point2.x - radius).y(point2.y - radius)
+}
+
+// 删除控制点
+function removeControls() {
+ if (!this.controlLine1) return
+ ;[
+ this.controlLine1,
+ this.controlLine2,
+ this.controlPoint1,
+ this.controlPoint2
+ ].forEach(item => {
+ item.remove()
+ })
+ this.controlLine1 = null
+ this.controlLine2 = null
+ this.controlPoint1 = null
+ this.controlPoint2 = null
+}
+
+// 隐藏控制点
+function hideControls() {
+ if (!this.controlLine1) return
+ ;[
+ this.controlLine1,
+ this.controlLine2,
+ this.controlPoint1,
+ this.controlPoint2
+ ].forEach(item => {
+ item.hide()
+ })
+}
+
+// 显示控制点
+function showControls() {
+ if (!this.controlLine1) return
+ ;[
+ this.controlLine1,
+ this.controlLine2,
+ this.controlPoint1,
+ this.controlPoint2
+ ].forEach(item => {
+ item.show()
+ })
+}
+
+export default {
+ createControlNodes,
+ createOneControlNode,
+ onControlPointMousedown,
+ onControlPointMousemove,
+ onControlPointMouseup,
+ resetControlPoint,
+ renderControls,
+ removeControls,
+ hideControls,
+ showControls
+}
diff --git a/simple-mind-map/src/utils/associativeLineText.js b/simple-mind-map/src/utils/associativeLineText.js
new file mode 100644
index 00000000..58e3b974
--- /dev/null
+++ b/simple-mind-map/src/utils/associativeLineText.js
@@ -0,0 +1,149 @@
+import { Text } from '@svgdotjs/svg.js'
+import { getStrWithBrFromHtml } from './index'
+
+// 创建文字节点
+function createText(data) {
+ let g = this.draw.group()
+ g.click(e => {
+ e.stopPropagation()
+ })
+ g.on('dblclick', e => {
+ e.stopPropagation()
+ if (
+ !this.activeLine ||
+ this.activeLine[3] !== data.node ||
+ this.activeLine[4] !== data.toNode
+ ) {
+ this.setActiveLine({
+ ...data,
+ text: g
+ })
+ }
+ if (!this.activeLine) return
+ this.showEditTextBox(g)
+ })
+ return g
+}
+
+// 显示文本编辑框
+function showEditTextBox(g) {
+ this.mindMap.emit('before_show_text_edit')
+ // 注册回车快捷键
+ this.mindMap.keyCommand.addShortcut('Enter', () => {
+ this.hideEditTextBox()
+ })
+ let rect = g.node.getBoundingClientRect()
+ if (!this.textEditNode) {
+ 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; word-break: break-all;`
+ this.textEditNode.setAttribute('contenteditable', true)
+ this.textEditNode.addEventListener('keyup', e => {
+ e.stopPropagation()
+ })
+ this.textEditNode.addEventListener('click', e => {
+ e.stopPropagation()
+ })
+ document.body.appendChild(this.textEditNode)
+ }
+ let {
+ associativeLineTextFontSize,
+ associativeLineTextFontFamily,
+ associativeLineTextLineHeight
+ } = this.mindMap.themeConfig
+ let [, , , node, toNode] = this.activeLine
+ let textLines = (
+ this.getText(node, toNode) || this.mindMap.opt.defaultAssociativeLineText
+ ).split(/\n/gim)
+ this.textEditNode.style.fontFamily = associativeLineTextFontFamily
+ this.textEditNode.style.fontSize = associativeLineTextFontSize + 'px'
+ this.textEditNode.style.lineHeight = associativeLineTextLineHeight
+ this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
+ this.textEditNode.innerHTML = textLines.join('
')
+ this.textEditNode.style.minWidth = rect.width + 10 + 'px'
+ this.textEditNode.style.minHeight = rect.height + 6 + 'px'
+ this.textEditNode.style.left = rect.left + 'px'
+ this.textEditNode.style.top = rect.top + 'px'
+ this.textEditNode.style.display = 'block'
+ this.showTextEdit = true
+}
+
+// 隐藏文本编辑框
+function hideEditTextBox() {
+ if (!this.showTextEdit) {
+ return
+ }
+ let [path, , text, node, toNode] = this.activeLine
+ let str = getStrWithBrFromHtml(this.textEditNode.innerHTML)
+ this.mindMap.execCommand('SET_NODE_DATA', node, {
+ associativeLineText: {
+ ...(node.nodeData.data.associativeLineText || {}),
+ [toNode.nodeData.data.id]: str
+ }
+ })
+ this.textEditNode.style.display = 'none'
+ this.textEditNode.innerHTML = ''
+ this.showTextEdit = false
+ this.renderText(str, path, text)
+ this.mindMap.emit('hide_text_edit')
+}
+
+// 获取某根关联线的文字
+function getText(node, toNode) {
+ let obj = node.nodeData.data.associativeLineText
+ if (!obj) {
+ return ''
+ }
+ return obj[toNode.nodeData.data.id] || ''
+}
+
+// 渲染关联线文字
+function renderText(str, path, text) {
+ if (!str) return
+ let { associativeLineTextFontSize, associativeLineTextLineHeight } =
+ this.mindMap.themeConfig
+ text.clear()
+ let textArr = str.split(/\n/gim)
+ textArr.forEach((item, index) => {
+ let node = new Text().text(item)
+ node.y(associativeLineTextFontSize * associativeLineTextLineHeight * index)
+ this.styleText(node)
+ text.add(node)
+ })
+ updateTextPos(path, text)
+}
+
+// 给文本设置样式
+function styleText(node) {
+ let {
+ associativeLineTextColor,
+ associativeLineTextFontSize,
+ associativeLineTextFontFamily
+ } = this.mindMap.themeConfig
+ node
+ .fill({
+ color: associativeLineTextColor
+ })
+ .css({
+ 'font-family': associativeLineTextFontFamily,
+ 'font-size': associativeLineTextFontSize
+ })
+}
+
+// 更新关联线文字位置
+function updateTextPos(path, text) {
+ let pathLength = path.length()
+ let centerPoint = path.pointAt(pathLength / 2)
+ let { width: textWidth, height: textHeight } = text.bbox()
+ text.x(centerPoint.x - textWidth / 2)
+ text.y(centerPoint.y - textHeight / 2)
+}
+
+export default {
+ getText,
+ createText,
+ styleText,
+ showEditTextBox,
+ hideEditTextBox,
+ renderText,
+ updateTextPos
+}
From a65cffa58bdb7be25cfbfe897e8746e7d61e4cc0 Mon Sep 17 00:00:00 2001
From: wanglin2 <1013335014@qq.com>
Date: Fri, 28 Apr 2023 17:26:37 +0800
Subject: [PATCH 2/6] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=85=B3=E8=81=94?=
=?UTF-8?q?=E7=BA=BF=E6=96=87=E6=9C=AC=E7=BC=96=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/utils/associativeLineControls.js | 1 +
simple-mind-map/src/utils/associativeLineText.js | 13 ++++++++++---
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/simple-mind-map/src/utils/associativeLineControls.js b/simple-mind-map/src/utils/associativeLineControls.js
index 7b8432ef..105a69ef 100644
--- a/simple-mind-map/src/utils/associativeLineControls.js
+++ b/simple-mind-map/src/utils/associativeLineControls.js
@@ -108,6 +108,7 @@ function onControlPointMousemove(e) {
path.plot(pathStr)
clickPath.plot(pathStr)
this.updateTextPos(path, text)
+ this.updateTextEditBoxPos(text)
}
// 控制点的鼠标移动事件
diff --git a/simple-mind-map/src/utils/associativeLineText.js b/simple-mind-map/src/utils/associativeLineText.js
index 58e3b974..81d51ba5 100644
--- a/simple-mind-map/src/utils/associativeLineText.js
+++ b/simple-mind-map/src/utils/associativeLineText.js
@@ -32,7 +32,7 @@ function showEditTextBox(g) {
this.mindMap.keyCommand.addShortcut('Enter', () => {
this.hideEditTextBox()
})
- let rect = g.node.getBoundingClientRect()
+
if (!this.textEditNode) {
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; word-break: break-all;`
@@ -59,12 +59,18 @@ function showEditTextBox(g) {
this.textEditNode.style.lineHeight = associativeLineTextLineHeight
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
this.textEditNode.innerHTML = textLines.join('
')
+ this.textEditNode.style.display = 'block'
+ this.updateTextEditBoxPos(g)
+ this.showTextEdit = true
+}
+
+// 更新文本编辑框位置
+function updateTextEditBoxPos(g) {
+ let rect = g.node.getBoundingClientRect()
this.textEditNode.style.minWidth = rect.width + 10 + 'px'
this.textEditNode.style.minHeight = rect.height + 6 + 'px'
this.textEditNode.style.left = rect.left + 'px'
this.textEditNode.style.top = rect.top + 'px'
- this.textEditNode.style.display = 'block'
- this.showTextEdit = true
}
// 隐藏文本编辑框
@@ -144,6 +150,7 @@ export default {
styleText,
showEditTextBox,
hideEditTextBox,
+ updateTextEditBoxPos,
renderText,
updateTextPos
}
From 0886ba769828fa67f324973f92a7afdd1b3503cd Mon Sep 17 00:00:00 2001
From: wanglin2 <1013335014@qq.com>
Date: Fri, 28 Apr 2023 21:51:58 +0800
Subject: [PATCH 3/6] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=85=B3=E8=81=94?=
=?UTF-8?q?=E7=BA=BF=E6=96=87=E6=9C=AC=E7=BC=96=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
simple-mind-map/src/AssociativeLine.js | 2 +
.../src/utils/associativeLineText.js | 25 +++++--
web/src/pages/Edit/components/BaseStyle.vue | 73 ++++++++++++++++++-
3 files changed, 92 insertions(+), 8 deletions(-)
diff --git a/simple-mind-map/src/AssociativeLine.js b/simple-mind-map/src/AssociativeLine.js
index 71bf7ea3..c365a8de 100644
--- a/simple-mind-map/src/AssociativeLine.js
+++ b/simple-mind-map/src/AssociativeLine.js
@@ -97,6 +97,8 @@ class AssociativeLine {
window.addEventListener('mouseup', e => {
this.onControlPointMouseup(e)
})
+ // 缩放事件
+ this.mindMap.on('scale', this.onScale)
}
// 创建箭头
diff --git a/simple-mind-map/src/utils/associativeLineText.js b/simple-mind-map/src/utils/associativeLineText.js
index 81d51ba5..f761ab13 100644
--- a/simple-mind-map/src/utils/associativeLineText.js
+++ b/simple-mind-map/src/utils/associativeLineText.js
@@ -4,11 +4,7 @@ import { getStrWithBrFromHtml } from './index'
// 创建文字节点
function createText(data) {
let g = this.draw.group()
- g.click(e => {
- e.stopPropagation()
- })
- g.on('dblclick', e => {
- e.stopPropagation()
+ const setActive = () => {
if (
!this.activeLine ||
this.activeLine[3] !== data.node ||
@@ -19,6 +15,14 @@ function createText(data) {
text: g
})
}
+ }
+ g.click(e => {
+ e.stopPropagation()
+ setActive()
+ })
+ g.on('dblclick', e => {
+ e.stopPropagation()
+ setActive()
if (!this.activeLine) return
this.showEditTextBox(g)
})
@@ -50,13 +54,14 @@ function showEditTextBox(g) {
associativeLineTextFontFamily,
associativeLineTextLineHeight
} = this.mindMap.themeConfig
+ let scale = this.mindMap.view.scale
let [, , , node, toNode] = this.activeLine
let textLines = (
this.getText(node, toNode) || this.mindMap.opt.defaultAssociativeLineText
).split(/\n/gim)
this.textEditNode.style.fontFamily = associativeLineTextFontFamily
- this.textEditNode.style.fontSize = associativeLineTextFontSize + 'px'
- this.textEditNode.style.lineHeight = associativeLineTextLineHeight
+ this.textEditNode.style.fontSize = associativeLineTextFontSize * scale + 'px'
+ this.textEditNode.style.lineHeight = textLines.length > 1 ? associativeLineTextLineHeight : 'normal'
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
this.textEditNode.innerHTML = textLines.join('
')
this.textEditNode.style.display = 'block'
@@ -64,6 +69,11 @@ function showEditTextBox(g) {
this.showTextEdit = true
}
+// 处理画布缩放
+function onScale() {
+ this.hideEditTextBox()
+}
+
// 更新文本编辑框位置
function updateTextEditBoxPos(g) {
let rect = g.node.getBoundingClientRect()
@@ -148,6 +158,7 @@ export default {
getText,
createText,
styleText,
+ onScale,
showEditTextBox,
hideEditTextBox,
updateTextEditBoxPos,
diff --git a/web/src/pages/Edit/components/BaseStyle.vue b/web/src/pages/Edit/components/BaseStyle.vue
index a901d4c0..78cada26 100644
--- a/web/src/pages/Edit/components/BaseStyle.vue
+++ b/web/src/pages/Edit/components/BaseStyle.vue
@@ -295,6 +295,67 @@
+
+