diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js index abe4aa8aef..faa110d4b3 100644 --- a/lib/timeline/Core.js +++ b/lib/timeline/Core.js @@ -281,12 +281,13 @@ class Core { this.range.stopRolling(); // Depending on the mouse wheel used chose the delta (some mice have the hardware for both) - const delta = isCurrentlyScrollingWithVerticalScrollWheel - ? deltaY - : deltaX; + const delta = isCurrentlyScrollingWithVerticalScrollWheel ? deltaY : deltaX; - // Calculate a single scroll jump relative to the range scale - let diff = ((delta / 120) * (this.range.end - this.range.start)) / 20; + // Calculate visual range (excluding hidden periods) for consistent scroll speed + const absoluteRange = this.range.end - this.range.start; + const hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.range.start, this.range.end); + const visualRange = absoluteRange - hiddenDuration; + let diff = delta / 120 * visualRange / 20; // Invert scroll direction // ...unless the user uses a horizontal mouse-wheel as found on the "Logi Master" series. @@ -298,17 +299,27 @@ class Core { ) diff = -diff; - // calculate new start and end - const newStart = this.range.start + diff; - const newEnd = this.range.end + diff; - const options = { - animation: false, - byUser: true, - event: event, - }; - this.range.setRange(newStart, newEnd, options); - - event.preventDefault(); + // Apply visual shift preserving visual span (like pinch-to-zoom does) + const currentVisualSpan = visualRange; + let newStart = this.range.start + diff; + let newEnd = this.range.end + diff; + + // If crossing hidden boundaries, preserve visual span + const newHiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, newStart, newEnd); + const newAbsoluteSpan = currentVisualSpan + newHiddenDuration; + newEnd = newStart + newAbsoluteSpan; + + // snapping times away from hidden zones (same as pinch-to-zoom) + const safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, diff, true); + const safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -diff, true); + + const options = { + animation: false, + byUser: true, + event: event + }; + this.range.setRange(safeStart, safeEnd, options); + event.preventDefault(); // Here in case of any future behaviour following after return;