Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions src/components/video-editor/SettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ interface SettingsPanelProps {
onShadowCommit?: () => void;
showBlur?: boolean;
onBlurChange?: (showBlur: boolean) => void;
motionBlurEnabled?: boolean;
onMotionBlurChange?: (enabled: boolean) => void;
motionBlurAmount?: number;
onMotionBlurChange?: (amount: number) => void;
onMotionBlurCommit?: () => void;
borderRadius?: number;
onBorderRadiusChange?: (radius: number) => void;
onBorderRadiusCommit?: () => void;
Expand Down Expand Up @@ -157,8 +158,9 @@ export function SettingsPanel({
onShadowCommit,
showBlur,
onBlurChange,
motionBlurEnabled = false,
motionBlurAmount = 0,
onMotionBlurChange,
onMotionBlurCommit,
borderRadius = 0,
onBorderRadiusChange,
onBorderRadiusCommit,
Expand Down Expand Up @@ -574,14 +576,6 @@ export function SettingsPanel({
</AccordionTrigger>
<AccordionContent className="pb-3">
<div className="grid grid-cols-2 gap-2 mb-3">
<div className="flex items-center justify-between p-2 rounded-lg bg-white/5 border border-white/5">
<div className="text-[10px] font-medium text-slate-300">Motion Blur</div>
<Switch
checked={motionBlurEnabled}
onCheckedChange={onMotionBlurChange}
className="data-[state=checked]:bg-[#34B27B] scale-90"
/>
</div>
<div className="flex items-center justify-between p-2 rounded-lg bg-white/5 border border-white/5">
<div className="text-[10px] font-medium text-slate-300">Blur BG</div>
<Switch
Expand All @@ -593,6 +587,23 @@ export function SettingsPanel({
</div>

<div className="grid grid-cols-2 gap-2">
<div className="p-2 rounded-lg bg-white/5 border border-white/5">
<div className="flex items-center justify-between mb-1">
<div className="text-[10px] font-medium text-slate-300">Motion Blur</div>
<span className="text-[10px] text-slate-500 font-mono">
{motionBlurAmount === 0 ? "off" : motionBlurAmount.toFixed(2)}
</span>
</div>
<Slider
value={[motionBlurAmount]}
onValueChange={(values) => onMotionBlurChange?.(values[0])}
onValueCommit={() => onMotionBlurCommit?.()}
min={0}
max={1}
step={0.01}
className="w-full [&_[role=slider]]:bg-[#34B27B] [&_[role=slider]]:border-[#34B27B] [&_[role=slider]]:h-3 [&_[role=slider]]:w-3"
/>
</div>
<div className="p-2 rounded-lg bg-white/5 border border-white/5">
<div className="flex items-center justify-between mb-1">
<div className="text-[10px] font-medium text-slate-300">Shadow</div>
Expand Down
25 changes: 13 additions & 12 deletions src/components/video-editor/VideoEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default function VideoEditor() {
wallpaper,
shadowIntensity,
showBlur,
motionBlurEnabled,
motionBlurAmount,
borderRadius,
padding,
aspectRatio,
Expand Down Expand Up @@ -139,7 +139,7 @@ export default function VideoEditor() {
wallpaper: normalizedEditor.wallpaper,
shadowIntensity: normalizedEditor.shadowIntensity,
showBlur: normalizedEditor.showBlur,
motionBlurEnabled: normalizedEditor.motionBlurEnabled,
motionBlurAmount: normalizedEditor.motionBlurAmount,
borderRadius: normalizedEditor.borderRadius,
padding: normalizedEditor.padding,
cropRegion: normalizedEditor.cropRegion,
Expand Down Expand Up @@ -198,7 +198,7 @@ export default function VideoEditor() {
wallpaper,
shadowIntensity,
showBlur,
motionBlurEnabled,
motionBlurAmount,
borderRadius,
padding,
cropRegion,
Expand All @@ -220,7 +220,7 @@ export default function VideoEditor() {
wallpaper,
shadowIntensity,
showBlur,
motionBlurEnabled,
motionBlurAmount,
borderRadius,
padding,
cropRegion,
Expand Down Expand Up @@ -294,7 +294,7 @@ export default function VideoEditor() {
wallpaper,
shadowIntensity,
showBlur,
motionBlurEnabled,
motionBlurAmount,
borderRadius,
padding,
cropRegion,
Expand Down Expand Up @@ -347,7 +347,7 @@ export default function VideoEditor() {
wallpaper,
shadowIntensity,
showBlur,
motionBlurEnabled,
motionBlurAmount,
borderRadius,
padding,
cropRegion,
Expand Down Expand Up @@ -933,7 +933,7 @@ export default function VideoEditor() {
showShadow: shadowIntensity > 0,
shadowIntensity,
showBlur,
motionBlurEnabled,
motionBlurAmount,
borderRadius,
padding,
videoPadding: padding,
Expand Down Expand Up @@ -1060,7 +1060,7 @@ export default function VideoEditor() {
showShadow: shadowIntensity > 0,
shadowIntensity,
showBlur,
motionBlurEnabled,
motionBlurAmount,
borderRadius,
padding,
cropRegion,
Expand Down Expand Up @@ -1121,7 +1121,7 @@ export default function VideoEditor() {
speedRegions,
shadowIntensity,
showBlur,
motionBlurEnabled,
motionBlurAmount,
borderRadius,
padding,
cropRegion,
Expand Down Expand Up @@ -1270,7 +1270,7 @@ export default function VideoEditor() {
showShadow={shadowIntensity > 0}
shadowIntensity={shadowIntensity}
showBlur={showBlur}
motionBlurEnabled={motionBlurEnabled}
motionBlurAmount={motionBlurAmount}
borderRadius={borderRadius}
padding={padding}
cropRegion={cropRegion}
Expand Down Expand Up @@ -1369,8 +1369,9 @@ export default function VideoEditor() {
onShadowCommit={commitState}
showBlur={showBlur}
onBlurChange={(v) => pushState({ showBlur: v })}
motionBlurEnabled={motionBlurEnabled}
onMotionBlurChange={(v) => pushState({ motionBlurEnabled: v })}
motionBlurAmount={motionBlurAmount}
onMotionBlurChange={(v) => updateState({ motionBlurAmount: v })}
onMotionBlurCommit={commitState}
borderRadius={borderRadius}
onBorderRadiusChange={(v) => updateState({ borderRadius: v })}
onBorderRadiusCommit={commitState}
Expand Down
15 changes: 7 additions & 8 deletions src/components/video-editor/VideoPlayback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ interface VideoPlaybackProps {
showShadow?: boolean;
shadowIntensity?: number;
showBlur?: boolean;
motionBlurEnabled?: boolean;
motionBlurAmount?: number;
borderRadius?: number;
padding?: number;
cropRegion?: import("./types").CropRegion;
Expand Down Expand Up @@ -113,7 +113,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
showShadow,
shadowIntensity = 0,
showBlur,
motionBlurEnabled = false,
motionBlurAmount = 0,
borderRadius = 0,
padding = 50,
cropRegion,
Expand All @@ -128,7 +128,6 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
},
ref,
) => {
const ZOOM_MOTION_BLUR_AMOUNT = 0.35;
const videoRef = useRef<HTMLVideoElement | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null);
const appRef = useRef<Application | null>(null);
Expand Down Expand Up @@ -169,7 +168,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
const layoutVideoContentRef = useRef<(() => void) | null>(null);
const trimRegionsRef = useRef<TrimRegion[]>([]);
const speedRegionsRef = useRef<SpeedRegion[]>([]);
const motionBlurEnabledRef = useRef(motionBlurEnabled);
const motionBlurAmountRef = useRef(motionBlurAmount);
const motionBlurStateRef = useRef<MotionBlurState>(createMotionBlurState());
const onTimeUpdateRef = useRef(onTimeUpdate);
const onPlayStateChangeRef = useRef(onPlayStateChange);
Expand Down Expand Up @@ -400,8 +399,8 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
}, [speedRegions]);

useEffect(() => {
motionBlurEnabledRef.current = motionBlurEnabled;
}, [motionBlurEnabled]);
motionBlurAmountRef.current = motionBlurAmount;
}, [motionBlurAmount]);

useEffect(() => {
onTimeUpdateRef.current = onTimeUpdate;
Expand Down Expand Up @@ -475,7 +474,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
focusY: DEFAULT_FOCUS.cy,
motionIntensity: 0,
isPlaying: false,
motionBlurAmount: motionBlurEnabledRef.current ? ZOOM_MOTION_BLUR_AMOUNT : 0,
motionBlurAmount: motionBlurAmountRef.current,
});

requestAnimationFrame(() => {
Expand Down Expand Up @@ -739,7 +738,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
motionIntensity,
motionVector,
isPlaying: isPlayingRef.current,
motionBlurAmount: motionBlurEnabledRef.current ? ZOOM_MOTION_BLUR_AMOUNT : 0,
motionBlurAmount: motionBlurAmountRef.current,
transformOverride: transform,
motionBlurState: motionBlurStateRef.current,
frameTimeMs: performance.now(),
Expand Down
11 changes: 8 additions & 3 deletions src/components/video-editor/projectPersistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface ProjectEditorState {
wallpaper: string;
shadowIntensity: number;
showBlur: boolean;
motionBlurEnabled: boolean;
motionBlurAmount: number;
borderRadius: number;
padding: number;
cropRegion: CropRegion;
Expand Down Expand Up @@ -302,8 +302,13 @@ export function normalizeProjectEditor(editor: Partial<ProjectEditorState>): Pro
wallpaper: typeof editor.wallpaper === "string" ? editor.wallpaper : WALLPAPER_PATHS[0],
shadowIntensity: typeof editor.shadowIntensity === "number" ? editor.shadowIntensity : 0,
showBlur: typeof editor.showBlur === "boolean" ? editor.showBlur : false,
motionBlurEnabled:
typeof editor.motionBlurEnabled === "boolean" ? editor.motionBlurEnabled : false,
motionBlurAmount: isFiniteNumber(editor.motionBlurAmount)
? clamp(editor.motionBlurAmount, 0, 1)
: typeof (editor as { motionBlurEnabled?: unknown }).motionBlurEnabled === "boolean"
? (editor as { motionBlurEnabled?: boolean }).motionBlurEnabled
? 0.35
: 0
: 0,
borderRadius: typeof editor.borderRadius === "number" ? editor.borderRadius : 0,
padding: isFiniteNumber(editor.padding) ? clamp(editor.padding, 0, 100) : 50,
cropRegion: {
Expand Down
22 changes: 14 additions & 8 deletions src/components/video-editor/videoPlayback/zoomTransform.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { BlurFilter, Container } from "pixi.js";
import { MotionBlurFilter } from "pixi-filters/motion-blur";

const PEAK_VELOCITY_PPS = 2000;
const MAX_BLUR_PX = 8;
const VELOCITY_THRESHOLD_PPS = 15;
const PEAK_VELOCITY_PPS = 1400;
const MAX_BLUR_PX = 14;
const VELOCITY_THRESHOLD_PPS = 12;
const MAX_AMOUNT_BOOST = 2.2;

function getMotionBlurAmountResponse(motionBlurAmount: number) {
const clampedAmount = Math.min(1, Math.max(0, motionBlurAmount));
// Keep the low end usable while giving the top of the slider substantially more headroom.
return clampedAmount * (1 + (MAX_AMOUNT_BOOST - 1) * clampedAmount);
}

export interface MotionBlurState {
lastFrameTimeMs: number;
Expand Down Expand Up @@ -185,6 +192,7 @@ export function applyZoomTransform({
const dtMs = Math.min(80, Math.max(1, now - motionBlurState.lastFrameTimeMs));
const dtSeconds = dtMs / 1000;
motionBlurState.lastFrameTimeMs = now;
const amountResponse = getMotionBlurAmountResponse(motionBlurAmount);

// Camera displacement this frame (stage-px)
const dx = transform.x - motionBlurState.prevCamX;
Expand All @@ -204,17 +212,15 @@ export function applyZoomTransform({

const normalised = Math.min(1, speed / PEAK_VELOCITY_PPS);
const targetBlur =
speed < VELOCITY_THRESHOLD_PPS
? 0
: normalised * normalised * MAX_BLUR_PX * motionBlurAmount;
speed < VELOCITY_THRESHOLD_PPS ? 0 : normalised * normalised * MAX_BLUR_PX * amountResponse;

const dirMag = Math.sqrt(velocityX * velocityX + velocityY * velocityY) || 1;
const velocityScale = targetBlur * 1.2;
const velocityScale = targetBlur * 2.4;
motionBlurFilter.velocity =
targetBlur > 0
? { x: (velocityX / dirMag) * velocityScale, y: (velocityY / dirMag) * velocityScale }
: { x: 0, y: 0 };
motionBlurFilter.kernelSize = targetBlur > 4 ? 11 : targetBlur > 1.5 ? 9 : 5;
motionBlurFilter.kernelSize = targetBlur > 8 ? 15 : targetBlur > 4 ? 11 : 7;
motionBlurFilter.offset = targetBlur > 0.5 ? -0.2 : 0;

if (blurFilter) {
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useEditorHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface EditorState {
wallpaper: string;
shadowIntensity: number;
showBlur: boolean;
motionBlurEnabled: boolean;
motionBlurAmount: number;
borderRadius: number;
padding: number;
aspectRatio: AspectRatio;
Expand All @@ -35,7 +35,7 @@ export const INITIAL_EDITOR_STATE: EditorState = {
wallpaper: "/wallpapers/wallpaper1.jpg",
shadowIntensity: 0,
showBlur: false,
motionBlurEnabled: false,
motionBlurAmount: 0,
borderRadius: 0,
padding: 50,
aspectRatio: "16:9",
Expand Down
4 changes: 2 additions & 2 deletions src/lib/exporter/frameRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ interface FrameRenderConfig {
showShadow: boolean;
shadowIntensity: number;
showBlur: boolean;
motionBlurEnabled?: boolean;
motionBlurAmount?: number;
borderRadius?: number;
padding?: number;
cropRegion: CropRegion;
Expand Down Expand Up @@ -351,7 +351,7 @@ export class FrameRenderer {
focusY: this.animationState.focusY,
motionIntensity: maxMotionIntensity,
isPlaying: true,
motionBlurAmount: this.config.motionBlurEnabled ? 0.35 : 0,
motionBlurAmount: this.config.motionBlurAmount ?? 0,
motionBlurState: this.motionBlurState,
frameTimeMs: timeMs,
});
Expand Down
4 changes: 2 additions & 2 deletions src/lib/exporter/gifExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface GifExporterConfig {
showShadow: boolean;
shadowIntensity: number;
showBlur: boolean;
motionBlurEnabled?: boolean;
motionBlurAmount?: number;
borderRadius?: number;
padding?: number;
videoPadding?: number;
Expand Down Expand Up @@ -106,7 +106,7 @@ export class GifExporter {
showShadow: this.config.showShadow,
shadowIntensity: this.config.shadowIntensity,
showBlur: this.config.showBlur,
motionBlurEnabled: this.config.motionBlurEnabled,
motionBlurAmount: this.config.motionBlurAmount,
borderRadius: this.config.borderRadius,
padding: this.config.padding,
cropRegion: this.config.cropRegion,
Expand Down
4 changes: 2 additions & 2 deletions src/lib/exporter/videoExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface VideoExporterConfig extends ExportConfig {
showShadow: boolean;
shadowIntensity: number;
showBlur: boolean;
motionBlurEnabled?: boolean;
motionBlurAmount?: number;
borderRadius?: number;
padding?: number;
videoPadding?: number;
Expand Down Expand Up @@ -70,7 +70,7 @@ export class VideoExporter {
showShadow: this.config.showShadow,
shadowIntensity: this.config.shadowIntensity,
showBlur: this.config.showBlur,
motionBlurEnabled: this.config.motionBlurEnabled,
motionBlurAmount: this.config.motionBlurAmount,
borderRadius: this.config.borderRadius,
padding: this.config.padding,
cropRegion: this.config.cropRegion,
Expand Down
Loading