diff --git a/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQStageCornerStandee.tsx b/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQStageCornerStandee.tsx new file mode 100644 index 0000000..84af501 --- /dev/null +++ b/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQStageCornerStandee.tsx @@ -0,0 +1,99 @@ +import React from "react"; +import type {VQMouthShape} from "../types"; +import {VQLipSyncedStandeeImage} from "./VQLipSyncedStandeeImage"; + +export type VQStageCornerStandeeMode = "stage" | "corner"; + +export type VQStageCornerStandeeLayout = Readonly<{ + frameWidth: number | string; + frameHeight: number | string; + right: number | string; + bottom: number | string; + imageWidth: number | string; + imageMaxHeight: number | string; + imageHeight?: number | string; + alignItems?: React.CSSProperties["alignItems"]; + overflow?: React.CSSProperties["overflow"]; + filter?: string; +}>; + +export type VQStageCornerStandeeLayouts = Readonly< + Record +>; + +export const vqDefaultStageCornerStandeeLayouts = { + stage: { + frameWidth: 610, + frameHeight: 760, + right: 315, + bottom: -118, + imageWidth: "100%", + imageHeight: "100%", + imageMaxHeight: "100%", + alignItems: "flex-end", + overflow: "visible", + filter: "drop-shadow(0 18px 40px rgba(31, 42, 68, 0.22))", + }, + corner: { + frameWidth: 420, + frameHeight: 360, + right: 18, + bottom: 240, + imageWidth: 470, + imageHeight: 705, + imageMaxHeight: 705, + alignItems: "flex-start", + overflow: "visible", + filter: "drop-shadow(0 12px 24px rgba(0, 0, 0, 0.32))", + }, +} as const satisfies VQStageCornerStandeeLayouts; + +export type VQStageCornerStandeeProps = Readonly<{ + mode: VQStageCornerStandeeMode; + imagePath: string; + mouthImageDir: string; + mouth: VQMouthShape; + translateY?: number; + layouts?: VQStageCornerStandeeLayouts; + zIndex?: number; +}>; + +export const VQStageCornerStandee: React.FC = ({ + mode, + imagePath, + mouthImageDir, + mouth, + translateY = 0, + layouts = vqDefaultStageCornerStandeeLayouts, + zIndex = 3, +}) => { + const layout = layouts[mode]; + + return ( +
+ +
+ ); +}; diff --git a/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQStillBackground.tsx b/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQStillBackground.tsx new file mode 100644 index 0000000..63d8300 --- /dev/null +++ b/voicevox-remotion-template/src/lib/VQRemotionLib/components/VQStillBackground.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import {AbsoluteFill, Img, staticFile} from "remotion"; +import type {VQStillEvent} from "../types"; + +const remoteUrlPattern = /^https?:\/\//; + +const publicPathForStill = (imagePath: string) => { + const normalizedPath = imagePath.replace(/\\/g, "/"); + const publicPathIndex = normalizedPath.lastIndexOf("/public/"); + + return ( + publicPathIndex >= 0 + ? normalizedPath.slice(publicPathIndex + "/public/".length) + : normalizedPath + ).replace(/^\/+/, ""); +}; + +const srcForStill = (imagePath: string) => + remoteUrlPattern.test(imagePath) + ? imagePath + : staticFile(publicPathForStill(imagePath)); + +export const VQStillBackground: React.FC< + Readonly<{ + still?: VQStillEvent; + style?: React.CSSProperties; + imageStyle?: React.CSSProperties; + }> +> = ({still, style, imageStyle}) => { + if (!still) { + return null; + } + + return ( + + + + ); +}; diff --git a/voicevox-remotion-template/src/lib/VQRemotionLib/index.ts b/voicevox-remotion-template/src/lib/VQRemotionLib/index.ts index 060d7f8..b965a02 100644 --- a/voicevox-remotion-template/src/lib/VQRemotionLib/index.ts +++ b/voicevox-remotion-template/src/lib/VQRemotionLib/index.ts @@ -7,4 +7,6 @@ export * from "./components/VQLipSyncedStandeeImage"; export * from "./components/VQSpeechOverlay"; export * from "./components/VQSpeechSubtitle"; +export * from "./components/VQStageCornerStandee"; +export * from "./components/VQStillBackground"; export * from "./components/VQWarmGradientBackground"; diff --git a/voicevox-remotion-template/src/lib/VQRemotionLib/types.ts b/voicevox-remotion-template/src/lib/VQRemotionLib/types.ts index 11dcaa0..a14d475 100644 --- a/voicevox-remotion-template/src/lib/VQRemotionLib/types.ts +++ b/voicevox-remotion-template/src/lib/VQRemotionLib/types.ts @@ -55,6 +55,23 @@ subtitle?: string; }>; +export type VQStillObjectFit = + | "cover" + | "contain" + | "fill" + | "none" + | "scale-down"; + +export type VQStillEvent = Readonly<{ + type: "still"; + id: string; + imagePath: string; + durationSeconds?: number; + fit?: VQStillObjectFit; + objectPosition?: string; + opacity?: number; +}>; + export type VQMouthResolverContext = Readonly<{ characterId: CharacterId;