@antv/x6-next
Version:
JavaScript diagramming library that uses SVG and HTML for rendering.
148 lines (128 loc) • 4.18 kB
text/typescript
import { ModifierKey, Dom, NumberExt } from '@antv/x6-common'
import { Base } from './base'
export class MouseWheel extends Base {
public target: HTMLElement | Document
public container: HTMLElement
protected cumulatedFactor = 1
protected currentScale: number | null
protected startPos: { x: number; y: number }
private mousewheelHandle: Dom.MouseWheelHandle
protected get widgetOptions() {
return this.options.mousewheel
}
protected init() {
this.container = this.graph.container
this.target = this.widgetOptions.global ? document : this.container
this.mousewheelHandle = new Dom.MouseWheelHandle(
this.target,
this.onMouseWheel.bind(this),
this.allowMouseWheel.bind(this),
)
if (this.widgetOptions.enabled) {
this.enable(true)
}
}
get disabled() {
return this.widgetOptions.enabled !== true
}
enable(force?: boolean) {
if (this.disabled || force) {
this.widgetOptions.enabled = true
this.mousewheelHandle.enable()
}
}
disable() {
if (!this.disabled) {
this.widgetOptions.enabled = false
this.mousewheelHandle.disable()
}
}
protected allowMouseWheel(e: WheelEvent) {
const guard = this.widgetOptions.guard
return (
(guard == null || guard.call(e)) &&
ModifierKey.isMatch(e, this.widgetOptions.modifiers)
)
}
protected onMouseWheel(e: WheelEvent) {
const guard = this.widgetOptions.guard
if (
(guard == null || guard.call(e)) &&
ModifierKey.isMatch(e, this.widgetOptions.modifiers)
) {
const factor = this.widgetOptions.factor || 1.2
if (this.currentScale == null) {
this.startPos = { x: e.clientX, y: e.clientY }
this.currentScale = this.graph.transform.getScale().sx
}
const delta = e.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 currentScale = this.currentScale!
let targetScale = this.graph.transform.clampScale(
currentScale * this.cumulatedFactor,
)
const minScale = this.widgetOptions.minScale || Number.MIN_SAFE_INTEGER
const maxScale = this.widgetOptions.maxScale || Number.MAX_SAFE_INTEGER
targetScale = NumberExt.clamp(targetScale, minScale, maxScale)
if (targetScale !== currentScale) {
if (this.widgetOptions.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() {
this.disable()
}
}
export namespace MouseWheel {
export interface Options {
enabled?: boolean
global?: boolean
factor?: number
minScale?: number
maxScale?: number
modifiers?: string | ModifierKey[] | null
guard?: (e: WheelEvent) => boolean
zoomAtMousePosition?: boolean
}
}