diff --git a/packages/system/src/utils/general.ts b/packages/system/src/utils/general.ts index c7e58878..0bad94c9 100644 --- a/packages/system/src/utils/general.ts +++ b/packages/system/src/utils/general.ts @@ -173,3 +173,5 @@ export const getTransformForBounds = ( return [x, y, clampedZoom]; }; + +export const isMacOs = () => typeof navigator !== 'undefined' && navigator?.userAgent?.indexOf('Mac') >= 0; diff --git a/packages/system/src/xypanzoom/XYPanZoom.ts b/packages/system/src/xypanzoom/XYPanZoom.ts index a5b28c66..1faded97 100644 --- a/packages/system/src/xypanzoom/XYPanZoom.ts +++ b/packages/system/src/xypanzoom/XYPanZoom.ts @@ -10,7 +10,7 @@ import { PanZoomInstance, } from '../types'; import { clamp } from '../utils'; -import { getD3Transition, viewportToTransform } from './utils'; +import { getD3Transition, viewportToTransform, wheelDelta } from './utils'; import { createPanOnScrollHandler, createPanZoomEndHandler, @@ -65,6 +65,7 @@ export function XYPanZoom({ ); const d3ZoomHandler = d3Selection.on('wheel.zoom')!; + d3ZoomInstance.wheelDelta(wheelDelta); function setTransform(transform: ZoomTransform, options?: PanZoomTransformOptions) { if (d3Selection) { diff --git a/packages/system/src/xypanzoom/eventhandler.ts b/packages/system/src/xypanzoom/eventhandler.ts index 84deb929..b2b46b48 100644 --- a/packages/system/src/xypanzoom/eventhandler.ts +++ b/packages/system/src/xypanzoom/eventhandler.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ import type { D3ZoomEvent } from 'd3-zoom'; import { pointer } from 'd3-selection'; @@ -11,7 +12,8 @@ import { type OnDraggingChange, type OnTransformChange, } from '../types'; -import { isRightClickPan, isWrappedWithClass, transformToViewport, viewChanged } from './utils'; +import { isRightClickPan, isWrappedWithClass, transformToViewport, viewChanged, wheelDelta } from './utils'; +import { clamp, isMacOs } from '../utils'; export type ZoomPanValues = { isZoomingOrPanning: boolean; @@ -75,13 +77,15 @@ export function createPanOnScrollHandler({ event.stopImmediatePropagation(); const currentZoom = d3Selection.property('__zoom').k || 1; + const _isMacOs = isMacOs(); - if (event.ctrlKey && zoomOnPinch) { + // macos sets ctrlKey=true for pinch gesture on a trackpad + if (event.ctrlKey && zoomOnPinch && _isMacOs) { const point = pointer(event); - // taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js - const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10; + const pinchDelta = wheelDelta(event); const zoom = currentZoom * Math.pow(2, pinchDelta); - d3Zoom.scaleTo(d3Selection, zoom, point); + // @ts-ignore + d3Zoom.scaleTo(d3Selection, zoom, point, event); return; } @@ -89,8 +93,14 @@ export function createPanOnScrollHandler({ // increase scroll speed in firefox // firefox: deltaMode === 1; chrome: deltaMode === 0 const deltaNormalize = event.deltaMode === 1 ? 20 : 1; - const deltaX = panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize; - const deltaY = panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize; + let deltaX = panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize; + let deltaY = panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize; + + // this enables vertical scrolling with shift + scroll on windows + if (!_isMacOs && event.shiftKey && panOnScrollMode !== PanOnScrollMode.Vertical) { + deltaX = event.deltaY * deltaNormalize; + deltaY = 0; + } d3Zoom.translateBy( d3Selection, diff --git a/packages/system/src/xypanzoom/utils.ts b/packages/system/src/xypanzoom/utils.ts index bbd7d070..cad2b481 100644 --- a/packages/system/src/xypanzoom/utils.ts +++ b/packages/system/src/xypanzoom/utils.ts @@ -1,6 +1,7 @@ import { type ZoomTransform, zoomIdentity } from 'd3-zoom'; import { type D3SelectionInstance, type Viewport } from '../types'; +import { isMacOs } from '../utils'; export const viewChanged = (prevViewport: Viewport, eventViewport: any): boolean => prevViewport.x !== eventViewport.x || prevViewport.y !== eventViewport.y || prevViewport.zoom !== eventViewport.k; @@ -21,3 +22,9 @@ export const isRightClickPan = (panOnDrag: boolean | number[], usedButton: numbe export const getD3Transition = (selection: D3SelectionInstance, duration = 0) => typeof duration === 'number' && duration > 0 ? selection.transition().duration(duration) : selection; + +export const wheelDelta = (event: any) => { + const factor = event.ctrlKey && isMacOs() ? 10 : 1; + + return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * factor; +};