mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-21 10:27:44 +08:00
Feat:重构滚动条,优化使用体验
This commit is contained in:
parent
2590e21807
commit
ea95ae2b5d
@ -249,6 +249,10 @@ export const CONSTANTS = {
|
|||||||
PASTE_TYPE: {
|
PASTE_TYPE: {
|
||||||
CLIP_BOARD: 'clipBoard',
|
CLIP_BOARD: 'clipBoard',
|
||||||
CANVAS: 'canvas'
|
CANVAS: 'canvas'
|
||||||
|
},
|
||||||
|
SCROLL_BAR_DIR: {
|
||||||
|
VERTICAL: 'vertical',
|
||||||
|
HORIZONTAL: 'horizontal'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -171,7 +171,6 @@ class View {
|
|||||||
|
|
||||||
// 平移x方式到
|
// 平移x方式到
|
||||||
translateXTo(x) {
|
translateXTo(x) {
|
||||||
if (x === 0) return
|
|
||||||
this.x = x
|
this.x = x
|
||||||
this.transform()
|
this.transform()
|
||||||
}
|
}
|
||||||
@ -185,7 +184,6 @@ class View {
|
|||||||
|
|
||||||
// 平移y方向到
|
// 平移y方向到
|
||||||
translateYTo(y) {
|
translateYTo(y) {
|
||||||
if (y === 0) return
|
|
||||||
this.y = y
|
this.y = y
|
||||||
this.transform()
|
this.transform()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { throttle } from '../utils/index'
|
import { throttle } from '../utils/index'
|
||||||
|
import { CONSTANTS } from '../constants/constant'
|
||||||
|
|
||||||
// 滚动条插件
|
// 滚动条插件
|
||||||
class Scrollbar {
|
class Scrollbar {
|
||||||
@ -9,45 +10,13 @@ class Scrollbar {
|
|||||||
width: 0, // 水平滚动条的容器宽度
|
width: 0, // 水平滚动条的容器宽度
|
||||||
height: 0 // 垂直滚动条的容器高度
|
height: 0 // 垂直滚动条的容器高度
|
||||||
}
|
}
|
||||||
|
// 思维导图实际高度
|
||||||
|
this.chartHeight = 0
|
||||||
|
this.chartWidth = 0
|
||||||
this.reset()
|
this.reset()
|
||||||
this.bindEvent()
|
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() {
|
reset() {
|
||||||
// 当前拖拽的滚动条类型
|
// 当前拖拽的滚动条类型
|
||||||
@ -57,13 +26,41 @@ class Scrollbar {
|
|||||||
x: 0,
|
x: 0,
|
||||||
y: 0
|
y: 0
|
||||||
}
|
}
|
||||||
this.startViewPos = {
|
// 鼠标按下时,滚动条位置
|
||||||
x: 0,
|
this.mousedownScrollbarPos = 0
|
||||||
y: 0
|
}
|
||||||
}
|
|
||||||
// 思维导图实际高度
|
// 绑定事件
|
||||||
this.chartHeight = 0
|
bindEvent() {
|
||||||
this.chartWidth = 0
|
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
|
const elRect = this.mindMap.elRect
|
||||||
rect.x -= elRect.left
|
rect.x -= elRect.left
|
||||||
rect.x2 -= elRect.left
|
|
||||||
rect.y -= elRect.top
|
rect.y -= elRect.top
|
||||||
rect.y2 -= elRect.top
|
|
||||||
|
|
||||||
// 垂直滚动条
|
// 垂直滚动条
|
||||||
const canvasHeight = this.mindMap.height // 画布高度
|
const canvasHeight = this.mindMap.height // 画布高度
|
||||||
@ -129,46 +124,129 @@ class Scrollbar {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 滚动条鼠标按下事件处理函数
|
||||||
onMousedown(e, type) {
|
onMousedown(e, type) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
this.currentScrollType = type
|
this.currentScrollType = type
|
||||||
this.isMousedown = true
|
this.isMousedown = true
|
||||||
this.mousedownPos = {
|
this.mousedownPos = {
|
||||||
x: e.clientX,
|
x: e.clientX,
|
||||||
y: e.clientY
|
y: e.clientY
|
||||||
}
|
}
|
||||||
// 保存视图当前的偏移量
|
// 保存滚动条当前的位置
|
||||||
let transformData = this.mindMap.view.getTransformData()
|
const styles = window.getComputedStyle(e.target)
|
||||||
this.startViewPos = {
|
if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) {
|
||||||
x: transformData.state.x,
|
this.mousedownScrollbarPos = Number.parseFloat(styles.top)
|
||||||
y: transformData.state.y
|
} else {
|
||||||
|
this.mousedownScrollbarPos = Number.parseFloat(styles.left)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 鼠标移动事件处理函数
|
||||||
onMousemove(e) {
|
onMousemove(e) {
|
||||||
if (!this.isMousedown) {
|
if (!this.isMousedown) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.currentScrollType === 'vertical') {
|
e.preventDefault()
|
||||||
const oy = e.clientY - this.mousedownPos.y
|
e.stopPropagation()
|
||||||
const oyPercentage = -oy / this.scrollbarWrapSize.height
|
if (this.currentScrollType === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) {
|
||||||
const oyPx = oyPercentage * this.chartHeight
|
const oy = e.clientY - this.mousedownPos.y + this.mousedownScrollbarPos
|
||||||
// 在视图最初偏移量上累加更新量
|
this.updateMindMapView(CONSTANTS.SCROLL_BAR_DIR.VERTICAL, oy)
|
||||||
this.mindMap.view.translateYTo(oyPx + this.startViewPos.y)
|
|
||||||
} else {
|
} else {
|
||||||
const ox = e.clientX - this.mousedownPos.x
|
const ox = e.clientX - this.mousedownPos.x + this.mousedownScrollbarPos
|
||||||
const oxPercentage = -ox / this.scrollbarWrapSize.width
|
this.updateMindMapView(CONSTANTS.SCROLL_BAR_DIR.HORIZONTAL, ox)
|
||||||
const oxPx = oxPercentage * this.chartWidth
|
|
||||||
// 在视图最初偏移量上累加更新量
|
|
||||||
this.mindMap.view.translateXTo(oxPx + this.startViewPos.x)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 鼠标松开事件处理函数
|
||||||
onMouseup() {
|
onMouseup() {
|
||||||
this.isMousedown = false
|
this.isMousedown = false
|
||||||
this.reset()
|
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() {
|
beforePluginDestroy() {
|
||||||
this.unBindEvent()
|
this.unBindEvent()
|
||||||
|
|||||||
@ -1,18 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="scrollbarContainer" :class="{ isDark: isDark }">
|
<div class="scrollbarContainer" :class="{ isDark: isDark }">
|
||||||
<!-- 竖向 -->
|
<!-- 竖向 -->
|
||||||
<div class="scrollbar verticalScrollbar" ref="verticalScrollbarRef">
|
<div
|
||||||
|
class="scrollbar verticalScrollbar"
|
||||||
|
ref="verticalScrollbarRef"
|
||||||
|
@click="onVerticalScrollbarClick"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="scrollbarInner"
|
class="scrollbarInner"
|
||||||
:style="verticalScrollbarStyle"
|
:style="verticalScrollbarStyle"
|
||||||
|
@click.stop
|
||||||
@mousedown="onVerticalScrollbarMousedown"
|
@mousedown="onVerticalScrollbarMousedown"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 横向 -->
|
<!-- 横向 -->
|
||||||
<div class="scrollbar horizontalScrollbar" ref="horizontalScrollbarRef">
|
<div
|
||||||
|
class="scrollbar horizontalScrollbar"
|
||||||
|
ref="horizontalScrollbarRef"
|
||||||
|
@click="onHorizontalScrollbarClick"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="scrollbarInner"
|
class="scrollbarInner"
|
||||||
:style="horizontalScrollbarStyle"
|
:style="horizontalScrollbarStyle"
|
||||||
|
@click.stop
|
||||||
@mousedown="onHorizontalScrollbarMousedown"
|
@mousedown="onHorizontalScrollbarMousedown"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
@ -67,11 +77,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 调用插件方法更新滚动条位置和大小
|
// 调用插件方法更新滚动条位置和大小
|
||||||
updateScrollbar() {
|
updateScrollbar({ vertical, horizontal }) {
|
||||||
const {
|
|
||||||
vertical,
|
|
||||||
horizontal
|
|
||||||
} = this.mindMap.scrollbar.calculationScrollbar()
|
|
||||||
this.verticalScrollbarStyle = {
|
this.verticalScrollbarStyle = {
|
||||||
top: vertical.top + '%',
|
top: vertical.top + '%',
|
||||||
height: vertical.height + '%'
|
height: vertical.height + '%'
|
||||||
@ -87,9 +93,19 @@ export default {
|
|||||||
this.mindMap.scrollbar.onMousedown(e, 'vertical')
|
this.mindMap.scrollbar.onMousedown(e, 'vertical')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 垂直滚动条点击事件调用插件方法
|
||||||
|
onVerticalScrollbarClick(e) {
|
||||||
|
this.mindMap.scrollbar.onClick(e, 'vertical')
|
||||||
|
},
|
||||||
|
|
||||||
// 水平滚动条按下事件调用插件方法
|
// 水平滚动条按下事件调用插件方法
|
||||||
onHorizontalScrollbarMousedown(e) {
|
onHorizontalScrollbarMousedown(e) {
|
||||||
this.mindMap.scrollbar.onMousedown(e, 'horizontal')
|
this.mindMap.scrollbar.onMousedown(e, 'horizontal')
|
||||||
|
},
|
||||||
|
|
||||||
|
// 水平滚动条点击事件调用插件方法
|
||||||
|
onHorizontalScrollbarClick(e) {
|
||||||
|
this.mindMap.scrollbar.onClick(e, 'horizontal')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user