diff --git a/simple-mind-map/src/constants/constant.js b/simple-mind-map/src/constants/constant.js index cc316228..d37e6ade 100644 --- a/simple-mind-map/src/constants/constant.js +++ b/simple-mind-map/src/constants/constant.js @@ -249,6 +249,10 @@ export const CONSTANTS = { PASTE_TYPE: { CLIP_BOARD: 'clipBoard', CANVAS: 'canvas' + }, + SCROLL_BAR_DIR: { + VERTICAL: 'vertical', + HORIZONTAL: 'horizontal' } } diff --git a/simple-mind-map/src/core/view/View.js b/simple-mind-map/src/core/view/View.js index 78e8a549..5682f6ee 100644 --- a/simple-mind-map/src/core/view/View.js +++ b/simple-mind-map/src/core/view/View.js @@ -171,7 +171,6 @@ class View { // 平移x方式到 translateXTo(x) { - if (x === 0) return this.x = x this.transform() } @@ -185,7 +184,6 @@ class View { // 平移y方向到 translateYTo(y) { - if (y === 0) return this.y = y this.transform() } diff --git a/simple-mind-map/src/plugins/Scrollbar.js b/simple-mind-map/src/plugins/Scrollbar.js index c34b9b7a..ab906741 100644 --- a/simple-mind-map/src/plugins/Scrollbar.js +++ b/simple-mind-map/src/plugins/Scrollbar.js @@ -1,4 +1,5 @@ import { throttle } from '../utils/index' +import { CONSTANTS } from '../constants/constant' // 滚动条插件 class Scrollbar { @@ -9,45 +10,13 @@ class Scrollbar { width: 0, // 水平滚动条的容器宽度 height: 0 // 垂直滚动条的容器高度 } + // 思维导图实际高度 + this.chartHeight = 0 + this.chartWidth = 0 this.reset() this.bindEvent() } - // 绑定事件 - bindEvent() { - this.onMousemove = this.onMousemove.bind(this) - this.onMouseup = this.onMouseup.bind(this) - this.onNodeTreeRenderEnd = this.onNodeTreeRenderEnd.bind(this) - this.onViewDataChange = throttle(this.onViewDataChange, 16, this) // 加个节流 - this.mindMap.on('mousemove', this.onMousemove) - this.mindMap.on('mouseup', this.onMouseup) - this.mindMap.on('node_tree_render_end', this.onNodeTreeRenderEnd) - this.mindMap.on('view_data_change', this.onViewDataChange) - } - - // 解绑事件 - unBindEvent() { - this.mindMap.off('mousemove', this.onMousemove) - this.mindMap.off('mouseup', this.onMouseup) - this.mindMap.off('node_tree_render_end', this.onNodeTreeRenderEnd) - this.mindMap.off('view_data_change', this.onViewDataChange) - } - - // 每次渲染后需要更新滚动条 - onNodeTreeRenderEnd() { - this.emitEvent() - } - - // 思维导图视图数据改变需要更新滚动条 - onViewDataChange() { - this.emitEvent() - } - - // 发送滚动条改变事件 - emitEvent() { - this.mindMap.emit('scrollbar_change') - } - // 复位数据 reset() { // 当前拖拽的滚动条类型 @@ -57,13 +26,41 @@ class Scrollbar { x: 0, y: 0 } - this.startViewPos = { - x: 0, - y: 0 - } - // 思维导图实际高度 - this.chartHeight = 0 - this.chartWidth = 0 + // 鼠标按下时,滚动条位置 + this.mousedownScrollbarPos = 0 + } + + // 绑定事件 + bindEvent() { + this.onMousemove = this.onMousemove.bind(this) + this.onMouseup = this.onMouseup.bind(this) + this.updateScrollbar = this.updateScrollbar.bind(this) + this.updateScrollbar = throttle(this.updateScrollbar, 16, this) // 加个节流 + this.mindMap.on('mousemove', this.onMousemove) + this.mindMap.on('mouseup', this.onMouseup) + this.mindMap.on('node_tree_render_end', this.updateScrollbar) + this.mindMap.on('view_data_change', this.updateScrollbar) + } + + // 解绑事件 + unBindEvent() { + this.mindMap.off('mousemove', this.onMousemove) + this.mindMap.off('mouseup', this.onMouseup) + this.mindMap.off('node_tree_render_end', this.updateScrollbar) + this.mindMap.off('view_data_change', this.updateScrollbar) + } + + // 渲染后、数据改变需要更新滚动条 + updateScrollbar() { + // 当前正在拖拽滚动条时不需要更新 + if (this.isMousedown) return + const res = this.calculationScrollbar() + this.emitEvent(res) + } + + // 发送滚动条改变事件 + emitEvent(data) { + this.mindMap.emit('scrollbar_change', data) } // 设置滚动条容器的大小,指滚动条容器的大小,对于水平滚动条,即宽度,对于垂直滚动条,即高度 @@ -78,9 +75,7 @@ class Scrollbar { // 减去画布距离浏览器窗口左上角的距离 const elRect = this.mindMap.elRect rect.x -= elRect.left - rect.x2 -= elRect.left rect.y -= elRect.top - rect.y2 -= elRect.top // 垂直滚动条 const canvasHeight = this.mindMap.height // 画布高度 @@ -129,46 +124,129 @@ class Scrollbar { return res } + // 滚动条鼠标按下事件处理函数 onMousedown(e, type) { e.preventDefault() + e.stopPropagation() this.currentScrollType = type this.isMousedown = true this.mousedownPos = { x: e.clientX, y: e.clientY } - // 保存视图当前的偏移量 - let transformData = this.mindMap.view.getTransformData() - this.startViewPos = { - x: transformData.state.x, - y: transformData.state.y + // 保存滚动条当前的位置 + const styles = window.getComputedStyle(e.target) + if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) { + this.mousedownScrollbarPos = Number.parseFloat(styles.top) + } else { + this.mousedownScrollbarPos = Number.parseFloat(styles.left) } } + // 鼠标移动事件处理函数 onMousemove(e) { if (!this.isMousedown) { return } - if (this.currentScrollType === 'vertical') { - const oy = e.clientY - this.mousedownPos.y - const oyPercentage = -oy / this.scrollbarWrapSize.height - const oyPx = oyPercentage * this.chartHeight - // 在视图最初偏移量上累加更新量 - this.mindMap.view.translateYTo(oyPx + this.startViewPos.y) + e.preventDefault() + e.stopPropagation() + if (this.currentScrollType === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) { + const oy = e.clientY - this.mousedownPos.y + this.mousedownScrollbarPos + this.updateMindMapView(CONSTANTS.SCROLL_BAR_DIR.VERTICAL, oy) } else { - const ox = e.clientX - this.mousedownPos.x - const oxPercentage = -ox / this.scrollbarWrapSize.width - const oxPx = oxPercentage * this.chartWidth - // 在视图最初偏移量上累加更新量 - this.mindMap.view.translateXTo(oxPx + this.startViewPos.x) + const ox = e.clientX - this.mousedownPos.x + this.mousedownScrollbarPos + this.updateMindMapView(CONSTANTS.SCROLL_BAR_DIR.HORIZONTAL, ox) } } + // 鼠标松开事件处理函数 onMouseup() { this.isMousedown = false this.reset() } + // 更新视图 + updateMindMapView(type, offset) { + const scrollbarData = this.calculationScrollbar() + const t = this.mindMap.draw.transform() + const drawRect = this.mindMap.draw.rbox() + const rootRect = this.mindMap.renderer.root.group.rbox() + if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) { + // 滚动条新位置 + let oy = offset + // 判断是否达到首尾 + if (oy <= 0) { + oy = 0 + } + let max = + ((100 - scrollbarData.vertical.height) / 100) * + this.scrollbarWrapSize.height + if (oy >= max) { + oy = max + } + // 转换成百分比 + const oyPercentage = (oy / this.scrollbarWrapSize.height) * 100 + // 转换成相对于图形高度的距离 + const oyPx = (-oyPercentage / 100) * this.chartHeight + // 节点中心点到图形最上方的距离 + const yOffset = rootRect.y - drawRect.y + // 内边距 + const paddingY = this.mindMap.height / 2 + // 图形新位置 + let chartTop = oyPx + yOffset - paddingY * t.scaleY + paddingY + this.mindMap.view.translateYTo(chartTop) + this.emitEvent({ + horizontal: scrollbarData.horizontal, + vertical: { + top: oyPercentage, + height: scrollbarData.vertical.height + } + }) + } else { + // 滚动条新位置 + let ox = offset + // 判断是否达到首尾 + if (ox <= 0) { + ox = 0 + } + let max = + ((100 - scrollbarData.horizontal.width) / 100) * + this.scrollbarWrapSize.width + if (ox >= max) { + ox = max + } + // 转换成百分比 + const oxPercentage = (ox / this.scrollbarWrapSize.width) * 100 + // 转换成相对于图形高度的距离 + const oxPx = (-oxPercentage / 100) * this.chartWidth + // 节点中心点到图形最左边的距离 + const xOffset = rootRect.x - drawRect.x + // 内边距 + const paddingX = this.mindMap.width / 2 + // 图形新位置 + let chartLeft = oxPx + xOffset - paddingX * t.scaleX + paddingX + this.mindMap.view.translateXTo(chartLeft) + this.emitEvent({ + vertical: scrollbarData.vertical, + horizontal: { + left: oxPercentage, + width: scrollbarData.horizontal.width + } + }) + } + } + + // 滚动条的点击事件 + onClick(e, type) { + let offset = 0 + if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) { + offset = e.clientY - e.currentTarget.getBoundingClientRect().top + } else { + offset = e.clientX - e.currentTarget.getBoundingClientRect().left + } + this.updateMindMapView(type, offset) + } + // 插件被卸载前做的事情 beforePluginDestroy() { this.unBindEvent() diff --git a/web/src/pages/Edit/components/Scrollbar.vue b/web/src/pages/Edit/components/Scrollbar.vue index 828ef680..14fe3c43 100644 --- a/web/src/pages/Edit/components/Scrollbar.vue +++ b/web/src/pages/Edit/components/Scrollbar.vue @@ -1,18 +1,28 @@