diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-02-27 16:33:57 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-02-27 16:33:57 +0100 |
| commit | 49426f19cd8eaed7cf6c47999b66fee7ee637a56 (patch) | |
| tree | 1b37451529cfcb36e6cae79494c61b9c616077df /apps/web/src/lib/timeline/pixel-utils.ts | |
| parent | 9b94f89def4f96836c4f42a23a73e97fe647a43c (diff) | |
| download | opencut-49426f19cd8eaed7cf6c47999b66fee7ee637a56.tar.gz | |
feat: implement keyframe animation system
Add keyframe support for transform, opacity, and volume properties.
Includes animation engine (interpolation, mutations, resolvers), timeline
markers with selection/snapping, properties panel toggles, keyframe-aware
renderer, and full undo/redo command support.
Also refactor element command constructors to object params, extract
timeline pixel math to pixel-utils.ts, and update cursor rules.
Diffstat (limited to 'apps/web/src/lib/timeline/pixel-utils.ts')
| -rw-r--r-- | apps/web/src/lib/timeline/pixel-utils.ts | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/apps/web/src/lib/timeline/pixel-utils.ts b/apps/web/src/lib/timeline/pixel-utils.ts new file mode 100644 index 00000000..4c0c243c --- /dev/null +++ b/apps/web/src/lib/timeline/pixel-utils.ts @@ -0,0 +1,79 @@ +import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; + +export const TIMELINE_INDICATOR_LINE_WIDTH_PX = 2; + +function getDevicePixelRatio({ + devicePixelRatio, +}: { + devicePixelRatio?: number; +}): number { + if ( + typeof devicePixelRatio === "number" && + Number.isFinite(devicePixelRatio) && + devicePixelRatio > 0 + ) { + return devicePixelRatio; + } + + if (typeof window === "undefined") { + return 1; + } + + if (Number.isFinite(window.devicePixelRatio) && window.devicePixelRatio > 0) { + return window.devicePixelRatio; + } + + return 1; +} + +export function getTimelinePixelsPerSecond({ + zoomLevel, +}: { + zoomLevel: number; +}): number { + return TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel; +} + +export function timelineTimeToPixels({ + time, + zoomLevel, +}: { + time: number; + zoomLevel: number; +}): number { + return time * getTimelinePixelsPerSecond({ zoomLevel }); +} + +export function snapPixelToDeviceGrid({ + pixel, + devicePixelRatio, +}: { + pixel: number; + devicePixelRatio?: number; +}): number { + const safeDevicePixelRatio = getDevicePixelRatio({ devicePixelRatio }); + return Math.round(pixel * safeDevicePixelRatio) / safeDevicePixelRatio; +} + +export function timelineTimeToSnappedPixels({ + time, + zoomLevel, + devicePixelRatio, +}: { + time: number; + zoomLevel: number; + devicePixelRatio?: number; +}): number { + const rawPixel = timelineTimeToPixels({ time, zoomLevel }); + return snapPixelToDeviceGrid({ pixel: rawPixel, devicePixelRatio }); +} + +export function getCenteredLineLeft({ + centerPixel, + lineWidthPx = TIMELINE_INDICATOR_LINE_WIDTH_PX, +}: { + centerPixel: number; + lineWidthPx?: number; +}): number { + return centerPixel - lineWidthPx / 2; +} |
