mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-23 19:37:44 +08:00
337 lines
7.9 KiB
JavaScript
337 lines
7.9 KiB
JavaScript
import { getRectRelativePosition } from '../../utils/index'
|
|
|
|
// 获取目标节点在起始节点的目标数组中的索引
|
|
export const getAssociativeLineTargetIndex = (node, toNode) => {
|
|
return node.getData('associativeLineTargets').findIndex(item => {
|
|
return item === toNode.getData('uid')
|
|
})
|
|
}
|
|
|
|
// 计算贝塞尔曲线的控制点
|
|
export const computeCubicBezierPathPoints = (x1, y1, x2, y2) => {
|
|
const min = 5
|
|
let cx1 = x1 + (x2 - x1) / 2
|
|
let cy1 = y1
|
|
let cx2 = cx1
|
|
let cy2 = y2
|
|
if (Math.abs(x1 - x2) <= min) {
|
|
cx1 = x1 + (y2 - y1) / 2
|
|
cx2 = cx1
|
|
}
|
|
if (Math.abs(y1 - y2) <= min) {
|
|
cx1 = x1
|
|
cy1 = y1 - (x2 - x1) / 2
|
|
cx2 = x2
|
|
cy2 = cy1
|
|
}
|
|
return [
|
|
{
|
|
x: cx1,
|
|
y: cy1
|
|
},
|
|
{
|
|
x: cx2,
|
|
y: cy2
|
|
}
|
|
]
|
|
}
|
|
|
|
// 拼接贝塞尔曲线路径
|
|
export const joinCubicBezierPath = (startPoint, endPoint, point1, point2) => {
|
|
return `M ${startPoint.x},${startPoint.y} C ${point1.x},${point1.y} ${point2.x},${point2.y} ${endPoint.x},${endPoint.y}`
|
|
}
|
|
|
|
// 获取节点的位置信息
|
|
const getNodeRect = node => {
|
|
let { left, top, width, height } = node
|
|
return {
|
|
right: left + width,
|
|
bottom: top + height,
|
|
left,
|
|
top,
|
|
width,
|
|
height
|
|
}
|
|
}
|
|
|
|
// 三次贝塞尔曲线
|
|
export const cubicBezierPath = (x1, y1, x2, y2) => {
|
|
let points = computeCubicBezierPathPoints(x1, y1, x2, y2)
|
|
return joinCubicBezierPath(
|
|
{ x: x1, y: y1 },
|
|
{ x: x2, y: y2 },
|
|
points[0],
|
|
points[1]
|
|
)
|
|
}
|
|
|
|
export const calcPoint = (node, e) => {
|
|
const { left, top, translateLeft, translateTop, width, height } = node
|
|
const clientX = e.clientX
|
|
const clientY = e.clientY
|
|
// 中心点的坐标
|
|
const centerX = translateLeft + width / 2
|
|
const centerY = translateTop + height / 2
|
|
const translateCenterX = left + width / 2
|
|
const translateCenterY = top + height / 2
|
|
const theta = Math.atan(height / width)
|
|
// 矩形左上角坐标
|
|
const deltaX = clientX - centerX
|
|
const deltaY = centerY - clientY
|
|
// 方向值
|
|
const direction = Math.atan2(deltaY, deltaX)
|
|
// 默认坐标
|
|
let x = left + width
|
|
let y = top + height
|
|
if (direction < theta && direction >= -theta) {
|
|
// 右边
|
|
// 正切值 = 对边/邻边,对边 = 正切值*邻边
|
|
const range = direction * (width / 2)
|
|
if (direction < theta && direction >= 0) {
|
|
// 中心点上边
|
|
y = translateCenterY - range
|
|
} else if (direction >= -theta && direction < 0) {
|
|
// 中心点下方
|
|
y = translateCenterY - range
|
|
}
|
|
return {
|
|
x,
|
|
y,
|
|
dir: 'right',
|
|
range
|
|
}
|
|
} else if (direction >= theta && direction < Math.PI - theta) {
|
|
// 上边
|
|
y = top
|
|
let range = 0
|
|
if (direction < Math.PI / 2 - theta && direction >= theta) {
|
|
// 正切值 = 对边/邻边,邻边 = 对边/正切值
|
|
const side = height / 2 / direction
|
|
range = -side
|
|
// 中心点右侧
|
|
x = translateCenterX + side
|
|
} else if (
|
|
direction >= Math.PI / 2 - theta &&
|
|
direction < Math.PI - theta
|
|
) {
|
|
// 中心点左侧
|
|
const tanValue = (centerX - clientX) / (centerY - clientY)
|
|
const side = (height / 2) * tanValue
|
|
range = side
|
|
x = translateCenterX - side
|
|
}
|
|
return {
|
|
x,
|
|
y,
|
|
dir: 'top',
|
|
range
|
|
}
|
|
} else if (direction < -theta && direction >= theta - Math.PI) {
|
|
// 下边
|
|
let range = 0
|
|
if (direction >= theta - Math.PI / 2 && direction < -theta) {
|
|
// 中心点右侧
|
|
// 正切值 = 对边/邻边,邻边 = 对边/正切值
|
|
const side = height / 2 / direction
|
|
range = side
|
|
x = translateCenterX - side
|
|
} else if (
|
|
direction < theta - Math.PI / 2 &&
|
|
direction >= theta - Math.PI
|
|
) {
|
|
// 中心点左侧
|
|
const tanValue = (centerX - clientX) / (centerY - clientY)
|
|
const side = (height / 2) * tanValue
|
|
range = -side
|
|
x = translateCenterX + side
|
|
}
|
|
return {
|
|
x,
|
|
y,
|
|
dir: 'bottom',
|
|
range
|
|
}
|
|
}
|
|
// 左边
|
|
x = left
|
|
const tanValue = (centerY - clientY) / (centerX - clientX)
|
|
const range = tanValue * (width / 2)
|
|
if (direction >= -Math.PI && direction < theta - Math.PI) {
|
|
// 中心点右侧
|
|
y = translateCenterY - range
|
|
} else if (direction < Math.PI && direction >= Math.PI - theta) {
|
|
// 中心点左侧
|
|
y = translateCenterY - range
|
|
}
|
|
return {
|
|
x,
|
|
y,
|
|
dir: 'left',
|
|
range
|
|
}
|
|
}
|
|
// 获取节点的连接点
|
|
export const getNodePoint = (node, dir = 'right', range = 0, e = null) => {
|
|
let { left, top, width, height } = node
|
|
if (e) {
|
|
return calcPoint(node, e)
|
|
}
|
|
switch (dir) {
|
|
case 'left':
|
|
return {
|
|
x: left,
|
|
y: top + height / 2 - range,
|
|
dir
|
|
}
|
|
case 'right':
|
|
return {
|
|
x: left + width,
|
|
y: top + height / 2 - range,
|
|
dir
|
|
}
|
|
case 'top':
|
|
return {
|
|
x: left + width / 2 - range,
|
|
y: top,
|
|
dir
|
|
}
|
|
case 'bottom':
|
|
return {
|
|
x: left + width / 2 - range,
|
|
y: top + height,
|
|
dir
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
// 根据两个节点的位置计算节点的连接点
|
|
export const computeNodePoints = (fromNode, toNode) => {
|
|
const fromRect = getNodeRect(fromNode)
|
|
const toRect = getNodeRect(toNode)
|
|
let fromDir = ''
|
|
let toDir = ''
|
|
const dir = getRectRelativePosition(
|
|
{
|
|
x: fromRect.left,
|
|
y: fromRect.top,
|
|
width: fromRect.width,
|
|
height: fromRect.height
|
|
},
|
|
{
|
|
x: toRect.left,
|
|
y: toRect.top,
|
|
width: toRect.width,
|
|
height: toRect.height
|
|
}
|
|
)
|
|
// 起始矩形在结束矩形的什么方向
|
|
switch (dir) {
|
|
case 'left-top':
|
|
fromDir = 'right'
|
|
toDir = 'top'
|
|
break
|
|
case 'right-top':
|
|
fromDir = 'left'
|
|
toDir = 'top'
|
|
break
|
|
case 'right-bottom':
|
|
fromDir = 'left'
|
|
toDir = 'bottom'
|
|
break
|
|
case 'left-bottom':
|
|
fromDir = 'right'
|
|
toDir = 'bottom'
|
|
break
|
|
case 'left':
|
|
fromDir = 'right'
|
|
toDir = 'left'
|
|
break
|
|
case 'right':
|
|
fromDir = 'left'
|
|
toDir = 'right'
|
|
break
|
|
case 'top':
|
|
fromDir = 'right'
|
|
toDir = 'right'
|
|
break
|
|
case 'bottom':
|
|
fromDir = 'left'
|
|
toDir = 'left'
|
|
break
|
|
case 'overlap':
|
|
fromDir = 'right'
|
|
toDir = 'right'
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
return [getNodePoint(fromNode, fromDir), getNodePoint(toNode, toDir)]
|
|
}
|
|
|
|
// 获取节点的关联线路径
|
|
export const getNodeLinePath = (startPoint, endPoint, node, toNode) => {
|
|
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
|
// 控制点
|
|
let controlPoints = []
|
|
let associativeLineTargetControlOffsets = node.getData(
|
|
'associativeLineTargetControlOffsets'
|
|
)
|
|
if (
|
|
associativeLineTargetControlOffsets &&
|
|
associativeLineTargetControlOffsets[targetIndex]
|
|
) {
|
|
// 节点保存了控制点差值
|
|
let offsets = associativeLineTargetControlOffsets[targetIndex]
|
|
controlPoints = [
|
|
{
|
|
x: startPoint.x + offsets[0].x,
|
|
y: startPoint.y + offsets[0].y
|
|
},
|
|
{
|
|
x: endPoint.x + offsets[1].x,
|
|
y: endPoint.y + offsets[1].y
|
|
}
|
|
]
|
|
} else {
|
|
// 没有保存控制点则生成默认的
|
|
controlPoints = computeCubicBezierPathPoints(
|
|
startPoint.x,
|
|
startPoint.y,
|
|
endPoint.x,
|
|
endPoint.y
|
|
)
|
|
}
|
|
// 根据控制点拼接贝塞尔曲线路径
|
|
return {
|
|
path: joinCubicBezierPath(
|
|
startPoint,
|
|
endPoint,
|
|
controlPoints[0],
|
|
controlPoints[1]
|
|
),
|
|
controlPoints
|
|
}
|
|
}
|
|
|
|
// 获取默认的控制点差值
|
|
export const getDefaultControlPointOffsets = (startPoint, endPoint) => {
|
|
let controlPoints = computeCubicBezierPathPoints(
|
|
startPoint.x,
|
|
startPoint.y,
|
|
endPoint.x,
|
|
endPoint.y
|
|
)
|
|
return [
|
|
{
|
|
x: controlPoints[0].x - startPoint.x,
|
|
y: controlPoints[0].y - startPoint.y
|
|
},
|
|
{
|
|
x: controlPoints[1].x - endPoint.x,
|
|
y: controlPoints[1].y - endPoint.y
|
|
}
|
|
]
|
|
}
|