49 lines
1.3 KiB
TypeScript
49 lines
1.3 KiB
TypeScript
|
|
import { type TimelineState, xToTime } from './renderer';
|
||
|
|
|
||
|
|
export type { TimelineState };
|
||
|
|
|
||
|
|
const MIN_VISIBLE_RANGE = 0.1;
|
||
|
|
const MIN_ZOOM = 1;
|
||
|
|
|
||
|
|
export function clampZoom(zoom: number, duration: number): number {
|
||
|
|
const maxZoom = duration / MIN_VISIBLE_RANGE;
|
||
|
|
return Math.max(MIN_ZOOM, Math.min(zoom, maxZoom));
|
||
|
|
}
|
||
|
|
|
||
|
|
export function computeZoom(
|
||
|
|
state: TimelineState,
|
||
|
|
anchorTime: number,
|
||
|
|
factor: number,
|
||
|
|
duration: number
|
||
|
|
): { visibleStart: number; visibleEnd: number; zoom: number } {
|
||
|
|
const currentRange = state.visibleEnd - state.visibleStart;
|
||
|
|
const newRange = currentRange / factor;
|
||
|
|
const clampedRange = Math.max(MIN_VISIBLE_RANGE, Math.min(newRange, duration));
|
||
|
|
|
||
|
|
const anchorRatio = (anchorTime - state.visibleStart) / currentRange;
|
||
|
|
let newStart = anchorTime - anchorRatio * clampedRange;
|
||
|
|
let newEnd = newStart + clampedRange;
|
||
|
|
|
||
|
|
if (newStart < 0) {
|
||
|
|
newStart = 0;
|
||
|
|
newEnd = clampedRange;
|
||
|
|
}
|
||
|
|
if (newEnd > duration) {
|
||
|
|
newEnd = duration;
|
||
|
|
newStart = Math.max(0, newEnd - clampedRange);
|
||
|
|
}
|
||
|
|
|
||
|
|
const newZoom = clampZoom(duration / clampedRange, duration);
|
||
|
|
|
||
|
|
return { visibleStart: newStart, visibleEnd: newEnd, zoom: newZoom };
|
||
|
|
}
|
||
|
|
|
||
|
|
export function handleClick(
|
||
|
|
x: number,
|
||
|
|
state: TimelineState,
|
||
|
|
duration: number
|
||
|
|
): number {
|
||
|
|
const time = xToTime(x, state);
|
||
|
|
return Math.max(0, Math.min(time, duration));
|
||
|
|
}
|