@antv/x6-next
Version:
JavaScript diagramming library that uses SVG and HTML for rendering.
200 lines (174 loc) • 5.37 kB
text/typescript
import { ModifierKey, Dom } from '@antv/x6-common'
import { Base } from './base'
export class PanningManager extends Base {
private panning: boolean
private clientX: number
private clientY: number
private mousewheelHandle: Dom.MouseWheelHandle
protected get widgetOptions() {
return this.options.panning
}
get pannable() {
return this.widgetOptions && this.widgetOptions.enabled === true
}
protected init() {
this.startListening()
this.updateClassName()
}
protected startListening() {
const eventTypes = this.widgetOptions.eventTypes
if (!eventTypes) {
return
}
if (eventTypes.includes('leftMouseDown')) {
this.graph.on('blank:mousedown', this.preparePanning, this)
this.graph.on('node:unhandled:mousedown', this.preparePanning, this)
this.graph.on('edge:unhandled:mousedown', this.preparePanning, this)
}
if (eventTypes.includes('rightMouseDown')) {
this.onRightMouseDown = this.onRightMouseDown.bind(this)
Dom.Event.on(this.graph.container, 'mousedown', this.onRightMouseDown)
}
if (eventTypes.includes('mouseWheel')) {
this.mousewheelHandle = new Dom.MouseWheelHandle(
this.graph.container,
this.onMouseWheel.bind(this),
this.allowMouseWheel.bind(this),
)
this.mousewheelHandle.enable()
}
}
protected stopListening() {
const eventTypes = this.widgetOptions.eventTypes
if (!eventTypes) {
return
}
if (eventTypes.includes('leftMouseDown')) {
this.graph.off('blank:mousedown', this.preparePanning, this)
this.graph.off('node:unhandled:mousedown', this.preparePanning, this)
this.graph.off('edge:unhandled:mousedown', this.preparePanning, this)
}
if (eventTypes.includes('rightMouseDown')) {
Dom.Event.off(this.graph.container, 'mousedown', this.onRightMouseDown)
}
if (eventTypes.includes('mouseWheel')) {
if (this.mousewheelHandle) {
this.mousewheelHandle.disable()
}
}
}
protected preparePanning({ e }: { e: Dom.MouseDownEvent }) {
// todo 暂时删除 selection 的判断
if (this.allowPanning(e, true)) {
this.startPanning(e)
}
}
allowPanning(e: JQuery.MouseDownEvent, strict?: boolean) {
return (
this.pannable &&
ModifierKey.isMatch(e, this.widgetOptions.modifiers, strict)
)
}
protected startPanning(evt: Dom.MouseDownEvent) {
const e = this.view.normalizeEvent(evt)
this.clientX = e.clientX
this.clientY = e.clientY
this.panning = true
this.updateClassName()
Dom.Event.on(document.body, {
'mousemove.panning touchmove.panning': this.pan.bind(this),
'mouseup.panning touchend.panning': this.stopPanning.bind(this),
'mouseleave.panning': this.stopPanning.bind(this),
})
Dom.Event.on(window as any, 'mouseup.panning', this.stopPanning.bind(this))
}
protected pan(evt: Dom.MouseMoveEvent) {
const e = this.view.normalizeEvent(evt)
const dx = e.clientX - this.clientX
const dy = e.clientY - this.clientY
this.clientX = e.clientX
this.clientY = e.clientY
this.graph.translateBy(dx, dy)
}
// eslint-disable-next-line
protected stopPanning(e: JQuery.MouseUpEvent) {
this.panning = false
this.updateClassName()
Dom.Event.off(document.body, '.panning')
Dom.Event.off(window as any, '.panning')
}
protected updateClassName() {
const container = this.view.container
const panning = this.view.prefixClassName('graph-panning')
const pannable = this.view.prefixClassName('graph-pannable')
if (this.pannable) {
if (this.panning) {
Dom.addClass(container, panning)
Dom.removeClass(container, pannable)
} else {
Dom.removeClass(container, panning)
Dom.addClass(container, pannable)
}
} else {
Dom.removeClass(container, panning)
Dom.removeClass(container, pannable)
}
}
protected onRightMouseDown(e: Dom.MouseDownEvent) {
if (e.button === 2 && this.allowPanning(e, true)) {
this.startPanning(e)
}
}
protected allowMouseWheel(e: WheelEvent) {
return this.pannable && !e.ctrlKey
}
protected onMouseWheel(e: WheelEvent, deltaX: number, deltaY: number) {
if (!e.ctrlKey) {
this.graph.translateBy(-deltaX, -deltaY)
}
}
autoPanning(x: number, y: number) {
const buffer = 10
const graphArea = this.graph.getGraphArea()
let dx = 0
let dy = 0
if (x <= graphArea.left + buffer) {
dx = -buffer
}
if (y <= graphArea.top + buffer) {
dy = -buffer
}
if (x >= graphArea.right - buffer) {
dx = buffer
}
if (y >= graphArea.bottom - buffer) {
dy = buffer
}
if (dx !== 0 || dy !== 0) {
this.graph.translateBy(-dx, -dy)
}
}
enablePanning() {
if (!this.pannable) {
this.widgetOptions.enabled = true
this.updateClassName()
}
}
disablePanning() {
if (this.pannable) {
this.widgetOptions.enabled = false
this.updateClassName()
}
}
dispose() {
this.stopListening()
}
}
export namespace PanningManager {
type EventType = 'leftMouseDown' | 'rightMouseDown' | 'mouseWheel'
export interface Options {
enabled?: boolean
modifiers?: string | ModifierKey[] | null
eventTypes?: EventType[]
}
}