diff --git a/src/components/composition/CompositionElement.tsx b/src/components/composition/CompositionElement.tsx index 10661d2..8bedc79 100644 --- a/src/components/composition/CompositionElement.tsx +++ b/src/components/composition/CompositionElement.tsx @@ -1,5 +1,6 @@ import React, { RefObject, useEffect } from 'react'; import { Sequence, AbsoluteFill, Img, Video, Audio } from '../../engine/components'; +import { useVideoConfig } from '../../engine/player'; import { interpolate } from '../../engine/animation'; import { TimelineElement, TimelineLayer, DesignMD } from '../../types'; import { calculateElementTransitions } from './useTransitions'; @@ -56,6 +57,9 @@ export const CompositionElement: React.FC = ({ if (fontFamily) loadGoogleFont(fontFamily); }, [fontFamily]); + const { width } = useVideoConfig(); + const scaleFactor = width / 1080; + // In image mode: all non-locked elements are interactive (Photoshop model) // In video mode: only elements on the active layer are interactive const isInteractive = isImageMode @@ -128,7 +132,8 @@ export const CompositionElement: React.FC = ({ const ckTolerance = el.chromaKeyTolerance ?? 30; const ckSoftness = el.chromaKeySoftness ?? 10; - const filterStr = `brightness(${el.brightness ?? 100}%) contrast(${el.contrast ?? 100}%) saturate(${el.saturation ?? 100}%)${el.hueRotate ? ` hue-rotate(${el.hueRotate}deg)` : ''}${el.sepia ? ` sepia(${el.sepia}%)` : ''}${el.blurAmount ? ` blur(${el.blurAmount}px)` : ''}`; + const scaledBlurAmount = (el.blurAmount ?? 0) * scaleFactor; + const filterStr = `brightness(${el.brightness ?? 100}%) contrast(${el.contrast ?? 100}%) saturate(${el.saturation ?? 100}%)${el.hueRotate ? ` hue-rotate(${el.hueRotate}deg)` : ''}${el.sepia ? ` sepia(${el.sepia}%)` : ''}${scaledBlurAmount ? ` blur(${scaledBlurAmount}px)` : ''}`; // Contain background: wrap media in a colored container when objectFit='contain' and color is set const hasContainBg = (el.objectFit === 'contain' || !el.objectFit) && !!el.containBgColor; @@ -341,11 +346,11 @@ export const CompositionElement: React.FC = ({ outlineOffset: `${6 / currentScale}px`, pointerEvents: isInteractive || isSelected ? 'auto' : 'none', mixBlendMode: resolvedBlendMode !== 'normal' ? resolvedBlendMode as React.CSSProperties['mixBlendMode'] : undefined, - border: el.borderWidth ? `${el.borderWidth}px ${el.borderStyle ?? 'solid'} ${el.borderColor ?? '#ffffff'}` : undefined, - borderRadius: el.borderRadius ? `${el.borderRadius}px` : undefined, + border: el.borderWidth ? `${el.borderWidth * scaleFactor}px ${el.borderStyle ?? 'solid'} ${el.borderColor ?? '#ffffff'}` : undefined, + borderRadius: el.borderRadius ? `${el.borderRadius * scaleFactor}px` : undefined, overflow: (el.height || el.borderRadius) ? 'hidden' : undefined, boxShadow: el.boxShadowBlur || el.boxShadowX || el.boxShadowY - ? `${el.boxShadowX ?? 0}px ${el.boxShadowY ?? 4}px ${el.boxShadowBlur ?? 10}px ${el.boxShadowColor ?? 'rgba(0,0,0,0.5)'}` + ? `${(el.boxShadowX ?? 0) * scaleFactor}px ${(el.boxShadowY ?? 4) * scaleFactor}px ${(el.boxShadowBlur ?? 10) * scaleFactor}px ${el.boxShadowColor ?? 'rgba(0,0,0,0.5)'}` : undefined, }} onClick={(e) => { e.stopPropagation(); }}