diff --git a/simple-mind-map/src/plugins/Scrollbar.js b/simple-mind-map/src/plugins/Scrollbar.js new file mode 100644 index 00000000..c34b9b7a --- /dev/null +++ b/simple-mind-map/src/plugins/Scrollbar.js @@ -0,0 +1,180 @@ +import { throttle } from '../utils/index' + +// 滚动条插件 +class Scrollbar { + // 构造函数 + constructor(opt) { + this.mindMap = opt.mindMap + this.scrollbarWrapSize = { + width: 0, // 水平滚动条的容器宽度 + height: 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() { + // 当前拖拽的滚动条类型 + this.currentScrollType = '' + this.isMousedown = false + this.mousedownPos = { + x: 0, + y: 0 + } + this.startViewPos = { + x: 0, + y: 0 + } + // 思维导图实际高度 + this.chartHeight = 0 + this.chartWidth = 0 + } + + // 设置滚动条容器的大小,指滚动条容器的大小,对于水平滚动条,即宽度,对于垂直滚动条,即高度 + setScrollBarWrapSize(width, height) { + this.scrollbarWrapSize.width = width + this.scrollbarWrapSize.height = height + } + + // 计算滚动条大小和位置 + calculationScrollbar() { + const rect = this.mindMap.draw.rbox() + // 减去画布距离浏览器窗口左上角的距离 + 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 // 画布高度 + const paddingY = canvasHeight / 2 // 首尾允许超出的距离,默认为高度的一半 + const chartHeight = rect.height + paddingY * 2 // 思维导图高度 + this.chartHeight = chartHeight + const chartTop = rect.y - paddingY // 思维导图顶部距画布顶部的距离 + const height = Math.min((canvasHeight / chartHeight) * 100, 100) // 滚动条高度 = 画布高度 / 思维导图高度 + let top = (-chartTop / chartHeight) * 100 // 滚动条距离 = 思维导图顶部距画布顶部的距离 / 思维导图高度 + // 判断是否到达边界 + if (top < 0) { + top = 0 + } + if (top > 100 - height) { + top = 100 - height + } + + // 水平滚动条 + const canvasWidth = this.mindMap.width + const paddingX = canvasWidth / 2 + const chartWidth = rect.width + paddingX * 2 + this.chartWidth = chartWidth + const chartLeft = rect.x - paddingX + const width = Math.min((canvasWidth / chartWidth) * 100, 100) + let left = (-chartLeft / chartWidth) * 100 + if (left < 0) { + left = 0 + } + if (left > 100 - width) { + left = 100 - width + } + + const res = { + // 垂直滚动条 + vertical: { + top, + height + }, + // 水平滚动条 + horizontal: { + left, + width + } + } + + return res + } + + onMousedown(e, type) { + e.preventDefault() + 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 + } + } + + 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) + } 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) + } + } + + onMouseup() { + this.isMousedown = false + this.reset() + } + + // 插件被卸载前做的事情 + beforePluginDestroy() { + this.unBindEvent() + } +} + +Scrollbar.instanceName = 'scrollbar' + +export default Scrollbar diff --git a/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index 1d4d6c78..15ff7891 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -22,6 +22,7 @@ + @@ -41,6 +42,7 @@ import TouchEvent from 'simple-mind-map/src/plugins/TouchEvent.js' import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js' import SearchPlugin from 'simple-mind-map/src/plugins/Search.js' import Painter from 'simple-mind-map/src/plugins/Painter.js' +import ScrollbarPlugin from 'simple-mind-map/src/plugins/Scrollbar.js' import OutlineSidebar from './OutlineSidebar' import Style from './Style' import BaseStyle from './BaseStyle' @@ -71,6 +73,7 @@ import NodeIconToolbar from './NodeIconToolbar.vue' import OutlineEdit from './OutlineEdit.vue' import { showLoading, hideLoading } from '@/utils/loading' import handleClipboardText from '@/utils/handleClipboardText' +import Scrollbar from './Scrollbar.vue' // 注册插件 MindMap.usePlugin(MiniMap) @@ -86,6 +89,7 @@ MindMap.usePlugin(MiniMap) .usePlugin(TouchEvent) .usePlugin(SearchPlugin) .usePlugin(Painter) + .usePlugin(ScrollbarPlugin) // 注册自定义主题 customThemeList.forEach(item => { @@ -117,7 +121,8 @@ export default { Search, NodeIconSidebar, NodeIconToolbar, - OutlineEdit + OutlineEdit, + Scrollbar }, data() { return { @@ -235,7 +240,7 @@ export default { storeConfig({ view: data }) - }, 1000) + }, 300) }) }, @@ -344,7 +349,8 @@ export default { 'transforming-dom-to-images', 'generalization_node_contextmenu', 'painter_start', - 'painter_end' + 'painter_end', + 'scrollbar_change' ].forEach(event => { this.mindMap.on(event, (...args) => { this.$bus.$emit(event, ...args) diff --git a/web/src/pages/Edit/components/Scrollbar.vue b/web/src/pages/Edit/components/Scrollbar.vue new file mode 100644 index 00000000..828ef680 --- /dev/null +++ b/web/src/pages/Edit/components/Scrollbar.vue @@ -0,0 +1,147 @@ + + + + +