@@ -25,6 +25,7 @@ import {
2525 type StyledRenderRect ,
2626 type WebcamLayoutPreset ,
2727} from "@/lib/compositeLayout" ;
28+ import { getCssClipPath } from "@/lib/webcamMaskShapes" ;
2829import {
2930 type AspectRatio ,
3031 formatAspectRatioForCSS ,
@@ -67,6 +68,7 @@ interface VideoPlaybackProps {
6768 videoPath : string ;
6869 webcamVideoPath ?: string ;
6970 webcamLayoutPreset : WebcamLayoutPreset ;
71+ webcamMaskShape ?: import ( "./types" ) . WebcamMaskShape ;
7072 webcamPosition ?: { cx : number ; cy : number } | null ;
7173 onWebcamPositionChange ?: ( position : { cx : number ; cy : number } ) => void ;
7274 onWebcamPositionDragEnd ?: ( ) => void ;
@@ -116,6 +118,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
116118 videoPath,
117119 webcamVideoPath,
118120 webcamLayoutPreset,
121+ webcamMaskShape,
119122 webcamPosition,
120123 onWebcamPositionChange,
121124 onWebcamPositionDragEnd,
@@ -281,6 +284,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
281284 webcamDimensions,
282285 webcamLayoutPreset,
283286 webcamPosition,
287+ webcamMaskShape,
284288 } ) ;
285289
286290 if ( result ) {
@@ -311,6 +315,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
311315 webcamDimensions ,
312316 webcamLayoutPreset ,
313317 webcamPosition ,
318+ webcamMaskShape ,
314319 ] ) ;
315320
316321 useEffect ( ( ) => {
@@ -1215,31 +1220,47 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
12151220 : "none" ,
12161221 } }
12171222 />
1218- { webcamVideoPath && (
1219- < video
1220- ref = { webcamVideoRef }
1221- src = { webcamVideoPath }
1222- className = { `absolute object-cover ${ webcamLayoutPreset === "picture-in-picture" ? "cursor-grab active:cursor-grabbing" : "pointer-events-none" } ` }
1223- style = { {
1224- left : webcamLayout ?. x ?? 0 ,
1225- top : webcamLayout ?. y ?? 0 ,
1226- width : webcamLayout ?. width ?? 0 ,
1227- height : webcamLayout ?. height ?? 0 ,
1228- borderRadius : webcamLayout ?. borderRadius ?? 0 ,
1229- boxShadow : webcamCssBoxShadow ,
1230- zIndex : 20 ,
1231- opacity : webcamLayout ? 1 : 0 ,
1232- backgroundColor : "#000" ,
1233- } }
1234- onPointerDown = { handleWebcamPointerDown }
1235- onPointerMove = { handleWebcamPointerMove }
1236- onPointerUp = { handleWebcamPointerUp }
1237- onPointerLeave = { handleWebcamPointerUp }
1238- muted
1239- preload = "metadata"
1240- playsInline
1241- />
1242- ) }
1223+ { webcamVideoPath &&
1224+ ( ( ) => {
1225+ const clipPath = getCssClipPath ( webcamLayout ?. maskShape ?? "rectangle" ) ;
1226+ const useClipPath = ! ! clipPath ;
1227+ return (
1228+ < div
1229+ className = "absolute"
1230+ style = { {
1231+ left : webcamLayout ?. x ?? 0 ,
1232+ top : webcamLayout ?. y ?? 0 ,
1233+ width : webcamLayout ?. width ?? 0 ,
1234+ height : webcamLayout ?. height ?? 0 ,
1235+ zIndex : 20 ,
1236+ opacity : webcamLayout ? 1 : 0 ,
1237+ filter :
1238+ useClipPath && webcamCssBoxShadow !== "none"
1239+ ? `drop-shadow(${ webcamCssBoxShadow } )`
1240+ : undefined ,
1241+ } }
1242+ >
1243+ < video
1244+ ref = { webcamVideoRef }
1245+ src = { webcamVideoPath }
1246+ className = { `w-full h-full object-cover ${ webcamLayoutPreset === "picture-in-picture" ? "cursor-grab active:cursor-grabbing" : "pointer-events-none" } ` }
1247+ style = { {
1248+ borderRadius : useClipPath ? 0 : ( webcamLayout ?. borderRadius ?? 0 ) ,
1249+ clipPath : clipPath ?? undefined ,
1250+ boxShadow : useClipPath ? "none" : webcamCssBoxShadow ,
1251+ backgroundColor : "#000" ,
1252+ } }
1253+ onPointerDown = { handleWebcamPointerDown }
1254+ onPointerMove = { handleWebcamPointerMove }
1255+ onPointerUp = { handleWebcamPointerUp }
1256+ onPointerLeave = { handleWebcamPointerUp }
1257+ muted
1258+ preload = "metadata"
1259+ playsInline
1260+ />
1261+ </ div >
1262+ ) ;
1263+ } ) ( ) }
12431264 { /* Only render overlay after PIXI and video are fully initialized */ }
12441265 { pixiReady && videoReady && (
12451266 < div
0 commit comments