diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 8ce90bdf9..db0185bcf 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -3328,6 +3328,8 @@ const canvasContentHeight = computed(() => Math.max(1, displayRowCount.value * C const canvasRenderStyleKey = computed( () => `${settingsStore.editorSettings.theme}:${settingsStore.editorSettings.uiScale}:${isDark.value}`, ); +const CANVAS_MOUSE_WHEEL_SCROLL_MULTIPLIER = 1.5; +const CANVAS_TRACKPAD_DELTA_THRESHOLD = 40; let canvasResizeObserver: ResizeObserver | null = null; let canvasDrawFrame = 0; let dataGridIsActive = true; @@ -3443,6 +3445,55 @@ function onCanvasScroll(event: Event) { scheduleCanvasDraw(); } +function canvasWheelDeltaToPixels(delta: number, deltaMode: number, pageSize: number): number { + if (deltaMode === WheelEvent.DOM_DELTA_LINE) return delta * CANVAS_DATA_GRID_ROW_HEIGHT; + if (deltaMode === WheelEvent.DOM_DELTA_PAGE) return delta * pageSize; + return delta; +} + +function shouldAccelerateCanvasWheel(event: WheelEvent): boolean { + if (event.ctrlKey || event.metaKey) return false; + if (event.deltaMode !== WheelEvent.DOM_DELTA_PIXEL) return true; + return ( + Math.abs(event.deltaY) >= CANVAS_TRACKPAD_DELTA_THRESHOLD || + Math.abs(event.deltaX) >= CANVAS_TRACKPAD_DELTA_THRESHOLD + ); +} + +function onCanvasWheel(event: WheelEvent) { + if (!shouldAccelerateCanvasWheel(event)) return; + const scroller = canvasScrollerElement(); + if (!scroller) return; + + const verticalDelta = canvasWheelDeltaToPixels(event.deltaY, event.deltaMode, scroller.clientHeight); + const horizontalDelta = canvasWheelDeltaToPixels(event.deltaX, event.deltaMode, scroller.clientWidth); + const shiftedHorizontalDelta = + event.shiftKey && Math.abs(verticalDelta) > Math.abs(horizontalDelta) ? verticalDelta : 0; + const nextTop = + shiftedHorizontalDelta === 0 + ? Math.max( + 0, + Math.min( + scroller.scrollHeight - scroller.clientHeight, + scroller.scrollTop + verticalDelta * CANVAS_MOUSE_WHEEL_SCROLL_MULTIPLIER, + ), + ) + : scroller.scrollTop; + const nextLeft = Math.max( + 0, + Math.min( + scroller.scrollWidth - scroller.clientWidth, + scroller.scrollLeft + (horizontalDelta + shiftedHorizontalDelta) * CANVAS_MOUSE_WHEEL_SCROLL_MULTIPLIER, + ), + ); + + if (nextTop === scroller.scrollTop && nextLeft === scroller.scrollLeft) return; + event.preventDefault(); + scroller.scrollTop = nextTop; + scroller.scrollLeft = nextLeft; + onCanvasScroll({ target: scroller } as unknown as Event); +} + function onCanvasMouseMove(event: MouseEvent) { const hit = canvasHitTest(event); const hitItem = hit ? displayItemAt(hit.rowIndex) : undefined; @@ -6941,6 +6992,7 @@ const gridContextMenuItems = computed(() => { class="data-grid-scroller canvas-grid-scroller flex-1 overflow-auto overscroll-none bg-background" :class="{ 'is-scrolling': isScrolling }" @scroll="onCanvasScroll" + @wheel="onCanvasWheel" >