feat: canvas-based live preview for crop, rotation, and color#1546
Open
smirk-dev wants to merge 1 commit into
Open
feat: canvas-based live preview for crop, rotation, and color#1546smirk-dev wants to merge 1 commit into
smirk-dev wants to merge 1 commit into
Conversation
Mirror the source <video> onto a <canvas> via a single requestAnimationFrame loop so crop/framing, rotation and brightness/contrast/saturation changes are reflected instantly, without running a full FFmpeg export. The export pipeline is untouched — the canvas is preview-only. - src/lib/previewGeometry.ts: pure, unit-tested geometry/colour helpers that mirror buildVideoFilter (rotate -> fit/fill scale+pad/crop -> eq colour). - src/hooks/useCanvasPreview.ts: rAF render loop; recipe read via ref (no per-change re-subscribe), container size cached via ResizeObserver (no layout thrash), DPR-aware buffer, ctx.filter for colour with CSS-filter fallback. - src/components/VideoPreview.tsx: render the canvas as the live preview with a Live toggle and click/space play-pause; native <video> retained as the frame source. Grouped the overlay control buttons into flex rows. Refs magic-peach#653 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@smirk-dev is attempting to deploy a commit to the magic-peach1's projects Team on Vercel. A member of the Team first needs to authorize it. |
Contributor
👋 Thanks for your PR, @smirk-dev!Welcome to Reframe — a browser-based video editor built for everyone 🎬 What happens next
Quick checklist
Useful links
Happy coding! 🎉 |
Contributor
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The editor now renders a hardware-accelerated canvas live preview that reflects crop/framing, rotation, and brightness/contrast/saturation changes in real time — no full FFmpeg export needed to see results. The export pipeline is completely untouched: the canvas is preview-only, FFmpeg still produces the final output.
Refs #653
Technical details
Canvas/video wiring — a single
requestAnimationFrameloop (useCanvasPreview) mirrors the existing<video>onto a<canvas>withctx.drawImageevery frame. Drawing every frame (not just ontimeupdate) means slider changes update instantly even while paused, and playback stays smooth. The native<video>is kept in the DOM as the frame source (and audio), visually hidden while live; play/pause works via click or Space, seeking via the existing thumbnail strip.Rotation & aspect ratio — geometry mirrors
buildVideoFilterexactly so the preview matches the export: rotate (transpose) → fit/fill scale + pad/crop → colour. Rotation is applied with canvas transforms (translate+rotate, sharper than a CSS element rotate and integrated with the letterbox math); 90/270° swap the source axes. The output frame is letterboxed at the selected preset's aspect ratio inside the 16:9 container, and within that frame the video is fit (letterbox bars) or fill (cover + crop) — matching FFmpeg'sforce_original_aspect_ratio=decrease+padvsincrease+crop.Colour filters —
eq=brightness:contrast:saturationis mapped to a filter string: additive brightnessb → brightness(1+b), contrast/saturation pass through 1:1. Applied viactx.filterwhere supported (so the black letterbox pad stays pure black, like the export), with a CSSfilterfallback on the canvas element for older browsers.Decoupled from export — none of
src/lib/ffmpeg.tschanged. The geometry helpers live insrc/lib/previewGeometry.tsand are shared only by the preview.Performance considerations
requestAnimationFrameloop per preview instance — nosetInterval, no nested timers; cancelled on unmount / when live preview is disabled.ResizeObserverand only re-read on resize — no per-framegetBoundingClientRect, so no layout thrashing.devicePixelRatiowhile CSS size stays stable; canvas buffer/CSS writes are skipped when unchanged.Files changed
src/lib/previewGeometry.tsbuildVideoFilter(rotate → fit/fill scale+pad/crop →eqcolour).src/lib/__tests__/previewGeometry.test.tssrc/hooks/useCanvasPreview.tsrequestAnimationFramerender loop (recipe-via-ref,ResizeObserver, DPR-aware buffer,ctx.filterwith CSS fallback).src/components/VideoPreview.tsx<video>retained as frame source. Overlay control buttons regrouped into flex rows.useVideoEditor.tsalready exposesrecipe+videoReftoVideoPreview(the integration point #653 calls out); the canvas consumes them through it, so no change was needed there.Screen recording
Testing
bun run lint— cleanbun run test— 135 passing (14 new for the geometry/colour helpers)bun run build— static export succeedsAcceptance criteria
requestAnimationFrame, no layout thrashing