UNPKG

leo-mind-map

Version:

一个简单的web在线思维导图

337 lines (327 loc) 8.07 kB
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 } ] }