import React from "react";
import {Img, staticFile} from "remotion";
import type {VQMouthCoverLayer, VQMouthShape} from "../types";
export type VQLipSyncedStandeeImageProps = Readonly<{
imagePath: string;
mouthImageDir: string;
mouth: VQMouthShape;
mouthCoverLayer?: VQMouthCoverLayer;
width: number | string;
maxHeight: number | string;
height?: number | string;
transform?: string;
filter?: string;
}>;
// 用途: 立ち絵の元口を口形画像の直前で隠すかどうか判定する。
// 使用方法: VQLipSyncedStandeeImage 内で mouthCoverLayer と現在の mouth を渡して使う。
// オプションや引数詳細: visibleWhen が "activeMouth" の場合は rest で非表示にし、hiddenMouths に含まれる口形でも非表示にする。
const shouldShowMouthCoverLayer = (
mouthCoverLayer: VQMouthCoverLayer | undefined,
mouth: VQMouthShape
) =>
Boolean(
mouthCoverLayer &&
!mouthCoverLayer.hiddenMouths?.includes(mouth) &&
(mouthCoverLayer.visibleWhen !== "activeMouth" || mouth !== "rest")
);
export const VQLipSyncedStandeeImage: React.FC<
VQLipSyncedStandeeImageProps
> = ({
imagePath,
mouthImageDir,
mouth,
mouthCoverLayer,
width,
maxHeight,
height,
transform,
filter,
}) => {
return (
<div
style={{
position: "relative",
zIndex: 1,
width,
height,
maxHeight,
lineHeight: 0,
transform,
}}
>
<Img
src={staticFile(imagePath)}
style={{
width: "100%",
height: height ? "100%" : undefined,
maxHeight,
objectFit: "contain",
filter,
}}
/>
{shouldShowMouthCoverLayer(mouthCoverLayer, mouth) ? (
<div
style={{
position: "absolute",
left: mouthCoverLayer?.left,
top: mouthCoverLayer?.top,
width: mouthCoverLayer?.width,
height: mouthCoverLayer?.height,
backgroundColor: mouthCoverLayer?.backgroundColor,
borderRadius: mouthCoverLayer?.borderRadius ?? "999px",
opacity: mouthCoverLayer?.opacity ?? 1,
pointerEvents: "none",
}}
/>
) : null}
<Img
src={staticFile(`${mouthImageDir}/${mouth}.png`)}
style={{
position: "absolute",
inset: 0,
width: "100%",
height: "100%",
objectFit: "contain",
pointerEvents: "none",
}}
/>
</div>
);
};