mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-22 19:07:45 +08:00
321 lines
10 KiB
JavaScript
321 lines
10 KiB
JavaScript
import { bfsWalk, throttle } from './utils'
|
||
import Base from './layouts/Base'
|
||
|
||
// 节点拖动类
|
||
|
||
class Drag extends Base {
|
||
// 构造函数
|
||
constructor({ mindMap }) {
|
||
super(mindMap.renderer)
|
||
this.mindMap = mindMap
|
||
this.reset()
|
||
this.bindEvent()
|
||
}
|
||
|
||
// 复位
|
||
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
|
||
// 鼠标移动的距离距鼠标按下的位置距离多少以上才认为是拖动事件
|
||
this.checkDragOffset = 10
|
||
}
|
||
|
||
// 绑定事件
|
||
bindEvent() {
|
||
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
|
||
this.mindMap.on('node_mousedown', (node, e) => {
|
||
if (this.mindMap.opt.readonly || node.isGeneralization) {
|
||
return
|
||
}
|
||
if (e.which !== 1 || node.isRoot) {
|
||
return
|
||
}
|
||
e.preventDefault()
|
||
// 计算鼠标按下的位置距离节点左上角的距离
|
||
this.drawTransform = this.mindMap.draw.transform()
|
||
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
|
||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||
this.offsetX = x - (node.left * scaleX + translateX)
|
||
this.offsetY = y - (node.top * scaleY + translateY)
|
||
this.node = node
|
||
this.isMousedown = true
|
||
this.mouseDownX = x
|
||
this.mouseDownY = y
|
||
})
|
||
this.mindMap.on('mousemove', e => {
|
||
if (this.mindMap.opt.readonly) {
|
||
return
|
||
}
|
||
if (!this.isMousedown) {
|
||
return
|
||
}
|
||
this.mindMap.emit('node_dragging', this.node)
|
||
e.preventDefault()
|
||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||
this.mouseMoveX = x
|
||
this.mouseMoveY = y
|
||
if (
|
||
Math.abs(x - this.mouseDownX) <= this.checkDragOffset &&
|
||
Math.abs(y - this.mouseDownY) <= this.checkDragOffset &&
|
||
!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)
|
||
}
|
||
|
||
// 鼠标松开事件
|
||
onMouseup(e) {
|
||
if (!this.isMousedown) {
|
||
return
|
||
}
|
||
this.isMousedown = false
|
||
let _nodeIsDrag = this.node.isDrag
|
||
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)
|
||
} else if (_nodeIsDrag && this.mindMap.opt.enableFreeDrag) {
|
||
// 自定义位置
|
||
let { x, y } = this.mindMap.toPos(
|
||
e.clientX - this.offsetX,
|
||
e.clientY - this.offsetY
|
||
)
|
||
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
|
||
x = (x - translateX) / scaleX
|
||
y = (y - translateY) / scaleY
|
||
this.node.left = x
|
||
this.node.top = y
|
||
this.node.customLeft = x
|
||
this.node.customTop = y
|
||
this.mindMap.execCommand('SET_NODE_CUSTOM_POSITION', this.node, x, y)
|
||
this.mindMap.render()
|
||
}
|
||
this.reset()
|
||
this.mindMap.emit('node_dragend')
|
||
}
|
||
|
||
// 创建克隆节点
|
||
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.styleLine(this.line, this.node)
|
||
// 同级位置占位符
|
||
this.placeholder = this.draw.rect().fill({
|
||
color: this.node.style.merge('lineColor', true)
|
||
})
|
||
this.mindMap.draw.add(this.clone)
|
||
}
|
||
}
|
||
|
||
// 移除克隆节点
|
||
removeCloneNode() {
|
||
if (!this.clone) {
|
||
return
|
||
}
|
||
this.clone.remove()
|
||
this.line.remove()
|
||
this.placeholder.remove()
|
||
}
|
||
|
||
// 拖动中
|
||
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()
|
||
}
|
||
|
||
// 检测重叠节点
|
||
checkOverlapNode() {
|
||
if (!this.drawTransform) {
|
||
return
|
||
}
|
||
let x = this.mouseMoveX
|
||
let y = this.mouseMoveY
|
||
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 nodeRect = this.getNodeRect(node)
|
||
let oneFourthHeight = nodeRect.height / 4
|
||
// 前一个和后一个节点
|
||
let checkList = node.parent ? node.parent.children.filter((item) => {
|
||
return item !== this.node
|
||
}) : []
|
||
let index = checkList.findIndex((item) => {
|
||
return item === node
|
||
})
|
||
let prevBrother = null
|
||
let nextBrother = null
|
||
if (index !== -1) {
|
||
if (index - 1 >= 0) {
|
||
prevBrother = checkList[index - 1]
|
||
}
|
||
if (index + 1 <= checkList.length - 1) {
|
||
nextBrother = checkList[index + 1]
|
||
}
|
||
}
|
||
// 和前一个兄弟节点的距离
|
||
let prevBrotherOffset = 0
|
||
if (prevBrother) {
|
||
let prevNodeRect = this.getNodeRect(prevBrother)
|
||
prevBrotherOffset = nodeRect.top - prevNodeRect.bottom
|
||
// 间距小于10就当它不存在
|
||
prevBrotherOffset = prevBrotherOffset >= 10 ? prevBrotherOffset / 2 : 0
|
||
} else {
|
||
// 没有前一个兄弟节点,那么假设和前一个节点的距离为20
|
||
prevBrotherOffset = 10
|
||
}
|
||
// 和后一个兄弟节点的距离
|
||
let nextBrotherOffset = 0
|
||
if (nextBrother) {
|
||
let nextNodeRect = this.getNodeRect(nextBrother)
|
||
nextBrotherOffset = nextNodeRect.top - nodeRect.bottom
|
||
nextBrotherOffset = nextBrotherOffset >= 10 ? nextBrotherOffset / 2 : 0
|
||
} else {
|
||
nextBrotherOffset = 10
|
||
}
|
||
if (nodeRect.left <= x && nodeRect.right >= x) {
|
||
// 检测兄弟节点位置
|
||
if (!this.overlapNode && !this.prevNode && !this.nextNode && !node.isRoot) {
|
||
let checkIsPrevNode = nextBrotherOffset > 0 ? // 距离下一个兄弟节点的距离大于0
|
||
y > nodeRect.bottom && y <= (nodeRect.bottom + nextBrotherOffset) : // 那么在当前节点外底部判断
|
||
y >= nodeRect.bottom - oneFourthHeight && y <= nodeRect.bottom // 否则在当前节点内底部1/4区间判断
|
||
let checkIsNextNode = prevBrotherOffset > 0 ? // 距离上一个兄弟节点的距离大于0
|
||
y < nodeRect.top && y >= (nodeRect.top - prevBrotherOffset) : // 那么在当前节点外底部判断
|
||
y >= nodeRect.top && y <= nodeRect.top + oneFourthHeight
|
||
if (checkIsPrevNode) {
|
||
this.prevNode = node
|
||
let size = nextBrotherOffset > 0 ? nextBrotherOffset : 5
|
||
this.placeholder.size(node.width, size).move(nodeRect.originLeft, nodeRect.originBottom)
|
||
} else if (checkIsNextNode) {
|
||
this.nextNode = node
|
||
let size = prevBrotherOffset > 0 ? prevBrotherOffset : 5
|
||
this.placeholder.size(node.width, size).move(nodeRect.originLeft, nodeRect.originTop - size)
|
||
}
|
||
}
|
||
// 检测是否重叠
|
||
if (!this.overlapNode && !this.prevNode && !this.nextNode) {
|
||
if (
|
||
nodeRect.top + (prevBrotherOffset > 0 ? 0 : oneFourthHeight) <= y &&
|
||
nodeRect.bottom - (nextBrotherOffset > 0 ? 0 : oneFourthHeight) >= y
|
||
) {
|
||
this.overlapNode = node
|
||
}
|
||
}
|
||
}
|
||
})
|
||
if (this.overlapNode) {
|
||
this.mindMap.renderer.setNodeActive(this.overlapNode, true)
|
||
}
|
||
}
|
||
|
||
// 计算节点的位置尺寸信息
|
||
getNodeRect(node) {
|
||
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
|
||
let { left, top, width, height } = node
|
||
let originLeft = left
|
||
let originTop = top
|
||
let originBottom = top + height
|
||
let right = (left + width) * scaleX + translateX
|
||
let bottom = (top + height) * scaleY + translateY
|
||
left = left * scaleX + translateX
|
||
top = top * scaleY + translateY
|
||
return {
|
||
width,
|
||
height,
|
||
left,
|
||
top,
|
||
right,
|
||
bottom,
|
||
originLeft,
|
||
originTop,
|
||
originBottom
|
||
}
|
||
}
|
||
}
|
||
|
||
Drag.instanceName = 'drag'
|
||
|
||
export default Drag
|