diff --git a/packages/playground/src/index.html b/packages/playground/src/index.html
index 087be91..4738f60 100644
--- a/packages/playground/src/index.html
+++ b/packages/playground/src/index.html
@@ -7,6 +7,7 @@
margin: 0;
padding: 0;
height: 100%;
+ background: black;
}
diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx
index 8a62e82..1e92826 100644
--- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx
+++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx
@@ -11,6 +11,7 @@ import styled from 'styled-components'
import {useReceiveVerticalWheelEvent} from '@theatre/studio/panels/SequenceEditorPanel/VerticalScrollContainer'
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
import {useCursorLock} from '@theatre/studio/uiComponents/PointerEventsHandler'
+import type {IRange} from '@theatre/shared/utils/types'
const Container = styled.div`
position: absolute;
@@ -192,7 +193,8 @@ function useHandlePanAndZoom(
)
const oldRange = val(layoutP.clippedSpace.range)
- const scaleFactor = 1 + event.deltaY * 0.03
+ const delta = normalize(event.deltaY, [-50, 50])
+ const scaleFactor = 1 + delta * 0.03
const newRange = mapValues(oldRange, (originalPos) => {
return (
@@ -201,6 +203,33 @@ function useHandlePanAndZoom(
)
})
+ // Set maximum scroll points based on the sequence length.
+ // This is to avoid zooming out to infinity.
+ const sequenceLength = val(layoutP.sheet).getSequence().length
+ const maxEnd = sequenceLength + sequenceLength * 0.25
+
+ val(layoutP.clippedSpace.setRange)(
+ normalizeRange(newRange, [0, maxEnd]),
+ )
+ }
+ // paning
+ else if (event.shiftKey) {
+ event.preventDefault()
+ event.stopPropagation()
+
+ const sequenceLength = val(layoutP.sheet).getSequence().length
+ const oldRange = val(layoutP.clippedSpace.range)
+ const windowSize = oldRange.end - oldRange.start
+ const speed = windowSize / sequenceLength
+
+ const delta = normalize(event.deltaY, [-50, 50])
+ const scaleFactor = delta * 0.05 * speed
+
+ const newRange = mapValues(
+ oldRange,
+ (originalPos) => originalPos + scaleFactor,
+ )
+
val(layoutP.clippedSpace.setRange)(newRange)
}
}
@@ -217,6 +246,17 @@ function useHandlePanAndZoom(
}, [node, layoutP])
}
+function normalize(value: number, [min, max]: [min: number, max: number]) {
+ return Math.max(Math.min(value, max), min)
+}
+
+function normalizeRange(
+ range: IRange,
+ minMax: [min: number, max: number],
+) {
+ return mapValues(range, (pos) => normalize(pos, minMax))
+}
+
function useUpdateScrollFromClippedSpaceRange(
layoutP: Pointer,
node: HTMLDivElement | null,