mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-22 02:47:46 +08:00
282 lines
8.4 KiB
JavaScript
282 lines
8.4 KiB
JavaScript
import { throttle } from '../utils/index'
|
|
import { CONSTANTS } from '../constants/constant'
|
|
|
|
// 滚动条插件
|
|
class Scrollbar {
|
|
// 构造函数
|
|
constructor(opt) {
|
|
this.mindMap = opt.mindMap
|
|
this.scrollbarWrapSize = {
|
|
width: 0, // 水平滚动条的容器宽度
|
|
height: 0 // 垂直滚动条的容器高度
|
|
}
|
|
// 思维导图实际高度
|
|
this.chartHeight = 0
|
|
this.chartWidth = 0
|
|
this.reset()
|
|
this.bindEvent()
|
|
}
|
|
|
|
// 复位数据
|
|
reset() {
|
|
// 当前拖拽的滚动条类型
|
|
this.currentScrollType = ''
|
|
this.isMousedown = false
|
|
this.mousedownPos = {
|
|
x: 0,
|
|
y: 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)
|
|
this.mindMap.on('resize', 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)
|
|
this.mindMap.off('resize', this.updateScrollbar)
|
|
}
|
|
|
|
// 渲染后、数据改变需要更新滚动条
|
|
updateScrollbar() {
|
|
// 当前正在拖拽滚动条时不需要更新
|
|
if (this.isMousedown) return
|
|
const res = this.calculationScrollbar()
|
|
this.emitEvent(res)
|
|
}
|
|
|
|
// 发送滚动条改变事件
|
|
emitEvent(data) {
|
|
this.mindMap.emit('scrollbar_change', data)
|
|
}
|
|
|
|
// 设置滚动条容器的大小,指滚动条容器的大小,对于水平滚动条,即宽度,对于垂直滚动条,即高度
|
|
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.y -= 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()
|
|
e.stopPropagation()
|
|
this.currentScrollType = type
|
|
this.isMousedown = true
|
|
this.mousedownPos = {
|
|
x: e.clientX,
|
|
y: e.clientY
|
|
}
|
|
// 保存滚动条当前的位置
|
|
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
|
|
}
|
|
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 + 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()
|
|
const rootCenterOffset = this.mindMap.renderer.layout.getRootCenterOffset(
|
|
rootRect.width,
|
|
rootRect.height
|
|
)
|
|
if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) {
|
|
// 滚动条新位置
|
|
let oy = offset
|
|
// 判断是否达到首尾
|
|
if (oy <= 0) {
|
|
oy = 0
|
|
}
|
|
const 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
|
|
// 图形新位置
|
|
const chartTop =
|
|
oyPx +
|
|
yOffset -
|
|
paddingY * t.scaleY +
|
|
paddingY -
|
|
rootCenterOffset.y * t.scaleY +
|
|
((this.mindMap.height - this.mindMap.initHeight) / 2) * t.scaleY // 画布宽高改变了,但是思维导图元素变换的中心点依旧是原有位置,所以需要加上中心点变化量
|
|
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
|
|
}
|
|
const 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
|
|
// 图形新位置
|
|
const chartLeft =
|
|
oxPx +
|
|
xOffset -
|
|
paddingX * t.scaleX +
|
|
paddingX -
|
|
rootCenterOffset.x * t.scaleX +
|
|
((this.mindMap.width - this.mindMap.initWidth) / 2) * t.scaleX // 画布宽高改变了,但是思维导图元素变换的中心点依旧是原有位置,所以需要加上中心点变化量
|
|
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)
|
|
}
|
|
|
|
// 插件被移除前做的事情
|
|
beforePluginRemove() {
|
|
this.unBindEvent()
|
|
}
|
|
|
|
// 插件被卸载前做的事情
|
|
beforePluginDestroy() {
|
|
this.unBindEvent()
|
|
}
|
|
}
|
|
|
|
Scrollbar.instanceName = 'scrollbar'
|
|
|
|
export default Scrollbar
|