@antv/x6
Version:
JavaScript diagramming library that uses SVG and HTML for rendering.
172 lines (151 loc) • 5.22 kB
text/typescript
import { Graph } from './graph'
import { ModifierKey } from '../types'
import { Dom, NumberExt } from '../util'
import { Disposable, IDisablable } from '../common'
export class MouseWheel extends Disposable implements IDisablable {
public readonly target: HTMLElement | Document
public readonly container: HTMLElement
protected cumulatedFactor = 1
protected currentScale: number | null
protected startPos: { x: number; y: number }
private mousewheelHandle: Dom.MouseWheelHandle
protected get graph() {
return this.options.graph
}
constructor(public readonly options: MouseWheel.Options) {
super()
const scroller = this.graph.scroller.widget
this.container = scroller ? scroller.container : this.graph.container
this.target = this.options.global ? document : this.container
this.mousewheelHandle = new Dom.MouseWheelHandle(
this.target,
this.onMouseWheel.bind(this),
this.allowMouseWheel.bind(this),
)
if (this.options.enabled) {
this.enable(true)
}
}
get disabled() {
return this.options.enabled !== true
}
enable(force?: boolean) {
if (this.disabled || force) {
this.options.enabled = true
this.graph.options.mousewheel.enabled = true
this.mousewheelHandle.enable()
}
}
disable() {
if (!this.disabled) {
this.options.enabled = false
this.graph.options.mousewheel.enabled = false
this.mousewheelHandle.disable()
}
}
protected allowMouseWheel(evt: JQueryMousewheel.JQueryMousewheelEventObject) {
const e = (evt.originalEvent || evt) as WheelEvent
const guard = this.options.guard
return (
(guard == null || guard.call(this.graph, e)) &&
ModifierKey.isMatch(e, this.options.modifiers)
)
}
protected onMouseWheel(evt: JQueryMousewheel.JQueryMousewheelEventObject) {
const e = (evt.originalEvent || evt) as WheelEvent
const guard = this.options.guard
if (
(guard == null || guard.call(this.graph, e)) &&
ModifierKey.isMatch(e, this.options.modifiers)
) {
const factor = this.options.factor || 1.2
if (this.currentScale == null) {
this.startPos = { x: evt.clientX, y: evt.clientY }
this.currentScale = this.graph.scroller.widget
? this.graph.scroller.widget.zoom()
: this.graph.transform.getScale().sx
}
const delta = evt.deltaY
if (delta < 0) {
// zoomin
// ------
// Switches to 1% zoom steps below 15%
if (this.currentScale < 0.15) {
this.cumulatedFactor = (this.currentScale + 0.01) / this.currentScale
} else {
// Uses to 5% zoom steps for better grid rendering in
// webkit and to avoid rounding errors for zoom steps
this.cumulatedFactor =
Math.round(this.currentScale * factor * 20) / 20 / this.currentScale
}
} else {
// zoomout
// -------
// Switches to 1% zoom steps below 15%
if (this.currentScale <= 0.15) {
this.cumulatedFactor = (this.currentScale - 0.01) / this.currentScale
} else {
// Uses to 5% zoom steps for better grid rendering in
// webkit and to avoid rounding errors for zoom steps
this.cumulatedFactor =
Math.round(this.currentScale * (1 / factor) * 20) /
20 /
this.currentScale
}
}
this.cumulatedFactor = Math.max(
0.01,
Math.min(this.currentScale * this.cumulatedFactor, 160) /
this.currentScale,
)
const scroller = this.graph.scroller.widget
const currentScale = this.currentScale!
let targetScale = this.graph.transform.clampScale(
currentScale * this.cumulatedFactor,
)
const minScale = this.options.minScale || Number.MIN_SAFE_INTEGER
const maxScale = this.options.maxScale || Number.MAX_SAFE_INTEGER
targetScale = NumberExt.clamp(targetScale, minScale, maxScale)
if (targetScale !== currentScale) {
if (scroller) {
if (this.options.zoomAtMousePosition) {
const origin = this.graph.coord.clientToLocalPoint(this.startPos)
scroller.zoom(targetScale, {
absolute: true,
center: origin.clone(),
})
} else {
scroller.zoom(targetScale, { absolute: true })
}
} else if (this.options.zoomAtMousePosition) {
const origin = this.graph.coord.clientToGraphPoint(this.startPos)
this.graph.zoom(targetScale, {
absolute: true,
center: origin.clone(),
})
} else {
this.graph.zoom(targetScale, { absolute: true })
}
}
this.currentScale = null
this.cumulatedFactor = 1
}
}
.dispose()
dispose() {
this.disable()
}
}
export namespace MouseWheel {
export interface Options {
graph: Graph
enabled?: boolean
global?: boolean
factor?: number
minScale?: number
maxScale?: number
modifiers?: string | ModifierKey[] | null
guard?: (this: Graph, e: WheelEvent) => boolean
zoomAtMousePosition?: boolean
}
}