mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-03-02 06:47:46 +08:00
Merge c15dd40efc into 9eab537b2e
This commit is contained in:
commit
0d03b26c06
@ -21,6 +21,10 @@ function initDragHandle() {
|
||||
this.dragHandleMousedownBodyCursor = ''
|
||||
// 鼠标按下时记录当前节点的left值
|
||||
this.dragHandleMousedownLeft = 0
|
||||
// 参考节点宽度列表
|
||||
this.referenceNodeWidths = []
|
||||
// 节点宽度与文本宽度的差值
|
||||
this.widthDiff = 0
|
||||
|
||||
this.onDragMousemoveHandle = this.onDragMousemoveHandle.bind(this)
|
||||
window.addEventListener('mousemove', this.onDragMousemoveHandle)
|
||||
@ -29,6 +33,18 @@ function initDragHandle() {
|
||||
this.mindMap.on('node_mouseup', this.onDragMouseupHandle)
|
||||
}
|
||||
|
||||
// 收集同级节点宽度
|
||||
function collectSiblingNodeWidths() {
|
||||
this.referenceNodeWidths = []
|
||||
if (this.parent && this.parent.children) {
|
||||
this.parent.children.forEach(node => {
|
||||
if (node !== this) {
|
||||
this.referenceNodeWidths.push(node.width)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
function onDragMousemoveHandle(e) {
|
||||
if (!this.isDragHandleMousedown) return
|
||||
@ -51,6 +67,25 @@ function onDragMousemoveHandle(e) {
|
||||
let newWidth =
|
||||
this.dragHandleMousedownCustomTextWidth +
|
||||
(this.dragHandleIndex === 0 ? -ox : ox) / scaleX
|
||||
|
||||
// 吸附逻辑
|
||||
const currentTotalWidth = newWidth + this.widthDiff
|
||||
const SNAP_THRESHOLD = 5
|
||||
let snapped = false
|
||||
for (const width of this.referenceNodeWidths) {
|
||||
if (Math.abs(currentTotalWidth - width) <= SNAP_THRESHOLD) {
|
||||
newWidth = width - this.widthDiff
|
||||
snapped = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 视觉反馈
|
||||
if (this._dragHandleNodes && this._dragHandleNodes[this.dragHandleIndex]) {
|
||||
this._dragHandleNodes[this.dragHandleIndex].fill({
|
||||
color: snapped ? '#409eff' : 'transparent'
|
||||
})
|
||||
}
|
||||
newWidth = Math.max(newWidth, minNodeTextModifyWidth)
|
||||
if (maxNodeTextModifyWidth !== -1) {
|
||||
newWidth = Math.min(newWidth, maxNodeTextModifyWidth)
|
||||
@ -87,6 +122,10 @@ function onDragMouseupHandle() {
|
||||
this.dragHandleMousedownX = 0
|
||||
this.dragHandleIndex = 0
|
||||
this.dragHandleMousedownCustomTextWidth = 0
|
||||
// 重置手柄颜色
|
||||
if (this._dragHandleNodes) {
|
||||
this._dragHandleNodes.forEach(node => node.fill({ color: 'transparent' }))
|
||||
}
|
||||
this.setData({
|
||||
customTextWidth: this.customTextWidth
|
||||
})
|
||||
@ -119,6 +158,9 @@ function createDragHandleNode() {
|
||||
: this.customTextWidth
|
||||
this.dragHandleMousedownBodyCursor = document.body.style.cursor
|
||||
this.dragHandleMousedownLeft = this.left
|
||||
// 计算差值和收集参考宽度
|
||||
this.widthDiff = this.width - this.dragHandleMousedownCustomTextWidth
|
||||
this.collectSiblingNodeWidths()
|
||||
this.isDragHandleMousedown = true
|
||||
})
|
||||
})
|
||||
@ -146,6 +188,7 @@ function updateDragHandle() {
|
||||
|
||||
export default {
|
||||
initDragHandle,
|
||||
collectSiblingNodeWidths,
|
||||
onDragMousemoveHandle,
|
||||
onDragMouseupHandle,
|
||||
createDragHandleNode,
|
||||
|
||||
@ -7,6 +7,7 @@ class NodeImgAdjust {
|
||||
constructor({ mindMap }) {
|
||||
this.mindMap = mindMap
|
||||
this.handleEl = null // 自定义元素,用来渲染临时图片、调整按钮
|
||||
this.resizeBtnEl = null // 调整按钮元素
|
||||
this.isShowHandleEl = false // 自定义元素是否在显示中
|
||||
this.node = null // 当前节点实例
|
||||
this.img = null // 当前节点的图片节点
|
||||
@ -21,6 +22,12 @@ class NodeImgAdjust {
|
||||
this.currentImgWidth = 0 // 当前拖拽实时图片的大小
|
||||
this.currentImgHeight = 0
|
||||
this.isAdjusted = false // 是否是拖拽结束后的渲染期间
|
||||
this.isAdjusted = false // 是否是拖拽结束后的渲染期间
|
||||
this.referenceWidths = [] // 参考宽度列表
|
||||
this.startClientX = 0
|
||||
this.startClientY = 0
|
||||
this.startImgWidth = 0
|
||||
this.startImgHeight = 0
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
@ -164,6 +171,7 @@ class NodeImgAdjust {
|
||||
cursor: nwse-resize;
|
||||
`
|
||||
btnEl.className = 'node-image-resize'
|
||||
this.resizeBtnEl = btnEl
|
||||
// 给按钮元素绑定事件
|
||||
btnEl.addEventListener('mouseenter', () => {
|
||||
// 移入按钮,会触发节点图片的移出事件,所以需要再次显示按钮
|
||||
@ -235,11 +243,37 @@ class NodeImgAdjust {
|
||||
this.isMousedown = true
|
||||
this.mousedownDrawTransform = this.mindMap.draw.transform()
|
||||
// 隐藏节点实际图片
|
||||
this.hideNodeImage()
|
||||
// this.hideNodeImage()
|
||||
this.mousedownOffset.x = e.clientX - this.rect.x2
|
||||
this.mousedownOffset.y = e.clientY - this.rect.y2
|
||||
// 记录初始状态
|
||||
this.startClientX = e.clientX
|
||||
this.startClientY = e.clientY
|
||||
const { width, height } = this.node.getData('imageSize')
|
||||
this.startImgWidth = width
|
||||
this.startImgHeight = height
|
||||
// 将节点图片渲染到自定义元素上
|
||||
this.handleEl.style.backgroundImage = `url(${this.node.getData('image')})`
|
||||
// this.handleEl.style.backgroundImage = `url(${this.node.getData('image')})`
|
||||
// 收集参考宽度
|
||||
this.referenceWidths = this.collectImageWidths()
|
||||
}
|
||||
|
||||
// 收集所有带图片节点的宽度
|
||||
collectImageWidths() {
|
||||
const widths = new Set()
|
||||
const walk = node => {
|
||||
if (node.getData('image') && node !== this.node) {
|
||||
const { width } = node.getData('imageSize') || {}
|
||||
if (width) widths.add(width)
|
||||
}
|
||||
if (node.children) {
|
||||
node.children.forEach(walk)
|
||||
}
|
||||
}
|
||||
if (this.mindMap.renderer.root) {
|
||||
walk(this.mindMap.renderer.root)
|
||||
}
|
||||
return Array.from(widths)
|
||||
}
|
||||
|
||||
// 鼠标移动
|
||||
@ -279,32 +313,71 @@ class NodeImgAdjust {
|
||||
imgMaxWidth = imgMaxWidth * scaleX
|
||||
imgMaxHeight = imgMaxHeight * scaleY
|
||||
// 计算当前拖拽位置对应的图片的实时大小
|
||||
let newWidth = Math.abs(e.clientX - this.rect.x - this.mousedownOffset.x)
|
||||
let newHeight = Math.abs(e.clientY - this.rect.y - this.mousedownOffset.y)
|
||||
// 逻辑尺寸变化量
|
||||
const deltaX = (e.clientX - this.startClientX) / scaleX
|
||||
// 逻辑尺寸
|
||||
let newWidth = this.startImgWidth + deltaX
|
||||
|
||||
// 转换为屏幕像素用于吸附判断
|
||||
let screenWidth = newWidth * scaleX
|
||||
|
||||
// 吸附逻辑
|
||||
const SNAP_THRESHOLD = 5
|
||||
let snapped = false
|
||||
for (const width of this.referenceWidths) {
|
||||
const refScreenWidth = width * scaleX
|
||||
if (Math.abs(screenWidth - refScreenWidth) <= SNAP_THRESHOLD) {
|
||||
newWidth = width
|
||||
screenWidth = refScreenWidth
|
||||
snapped = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let newHeight = newWidth / oRatio
|
||||
|
||||
// 限制最小值
|
||||
if (newWidth < minImgResizeWidth) newWidth = minImgResizeWidth
|
||||
if (newHeight < minImgResizeHeight) newHeight = minImgResizeHeight
|
||||
// 限制最大值
|
||||
if (newWidth > imgMaxWidth) newWidth = imgMaxWidth
|
||||
if (newHeight > imgMaxHeight) newHeight = imgMaxHeight
|
||||
const [actWidth, actHeight] = resizeImgSizeByOriginRatio(
|
||||
imageOriginWidth,
|
||||
imageOriginHeight,
|
||||
newWidth,
|
||||
newHeight
|
||||
)
|
||||
this.currentImgWidth = actWidth
|
||||
this.currentImgHeight = actHeight
|
||||
this.updateHandleElSize()
|
||||
|
||||
this.currentImgWidth = newWidth
|
||||
this.currentImgHeight = newHeight
|
||||
|
||||
// 更新handleEl的位置和大小
|
||||
|
||||
this.node.getData().imageSize = {
|
||||
width: this.currentImgWidth,
|
||||
height: this.currentImgHeight,
|
||||
custom: true
|
||||
}
|
||||
this.mindMap.render()
|
||||
|
||||
// 更新按钮状态
|
||||
if (snapped) {
|
||||
if (this.resizeBtnEl) {
|
||||
this.resizeBtnEl.style.backgroundColor = '#409eff'
|
||||
}
|
||||
} else {
|
||||
if (this.resizeBtnEl) {
|
||||
this.resizeBtnEl.style.backgroundColor = 'rgba(0, 0, 0, 0.3)'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标松开
|
||||
onMouseup() {
|
||||
if (!this.isMousedown) return
|
||||
// 显示节点实际图片
|
||||
this.showNodeImage()
|
||||
// this.showNodeImage()
|
||||
// 隐藏自定义元素
|
||||
this.hideHandleEl()
|
||||
// 重置按钮颜色
|
||||
if (this.resizeBtnEl) {
|
||||
this.resizeBtnEl.style.backgroundColor = 'rgba(0, 0, 0, 0.3)'
|
||||
}
|
||||
// 更新节点图片为新的大小
|
||||
const { image, imageTitle } = this.node.getData()
|
||||
const { scaleX, scaleY } = this.mousedownDrawTransform
|
||||
@ -331,6 +404,18 @@ class NodeImgAdjust {
|
||||
|
||||
// 渲染完成事件
|
||||
onRenderEnd() {
|
||||
if (this.isMousedown && this.node) {
|
||||
// 拖拽过程中渲染完成,需要更新操作柄位置
|
||||
// 必须重新获取 img 元素,因为 render 后 DOM 可能重建
|
||||
const img = this.node.group.findOne('image')
|
||||
if (img) {
|
||||
this.img = img
|
||||
this.rect = img.rbox()
|
||||
// 强制更新位置,确保跟随
|
||||
this.setHandleElRect()
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!this.isAdjusted) {
|
||||
this.hideHandleEl()
|
||||
return
|
||||
|
||||
Loading…
Reference in New Issue
Block a user