🟡 P1-4: 字体大小设置实时预览 — PreviewPlayer叠加标题/字幕预览层
CI/CD Pipeline / Deploy Staging (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Failing after 71h47m46s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 71h49m21s

- PreviewPlayer新增titleSettings/subtitleSettings props
- 手机预览区内叠加标题文字(位置/字体/大小/粗体/斜体/描边/阴影实时反映)
- 字幕预览叠加层(位置/字体/大小实时反映)
- EditingPlanner传递titleSettings/subtitleSettings到PreviewPlayer
- 新增.ep-preview-title/.ep-preview-subtitle CSS样式

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
灵应
2026-07-06 13:03:10 +08:00
parent 414a4a9020
commit f99ee8a2aa
3 changed files with 94 additions and 0 deletions
@@ -601,6 +601,34 @@
color: var(--ep-text-secondary, #9ca3af);
}
/* 标题/字幕实时预览叠加层 */
.ep-preview-title,
.ep-preview-subtitle {
position: absolute;
left: 4px;
right: 4px;
text-align: center;
color: #fff;
line-height: 1.3;
pointer-events: none;
z-index: 5;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ep-preview-title {
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7);
}
.ep-preview-subtitle {
background: rgba(0, 0, 0, 0.55);
padding: 2px 4px;
border-radius: 3px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}
/* 封面预览 */
.ep-cover-preview {
width: 150px;
@@ -708,6 +708,8 @@ const EditingPlanner: React.FC = () => {
currentCoverScheme={currentCoverScheme}
coverSchemes={COVER_SCHEMES}
aiCoverLoading={aiCoverLoading}
titleSettings={titleSettings}
subtitleSettings={subtitleSettings}
onClipSelect={handleClipSelect}
onCoverSchemeChange={setCurrentCoverScheme}
onPlayPause={() => setIsPlaying(!isPlaying)}
@@ -19,6 +19,26 @@ interface CoverScheme {
label: string;
}
interface TitleSettings {
aiAutoSelect: boolean;
title: string;
position: string;
font: string;
size: number;
bold: boolean;
italic: boolean;
stroke: boolean;
shadow: boolean;
}
interface SubtitleSettings {
enabled: boolean;
position: string;
font: string;
size: number;
animation: string;
}
interface PreviewPlayerProps {
clips: ClipData[];
selectedClipId: string | null;
@@ -26,6 +46,8 @@ interface PreviewPlayerProps {
currentCoverScheme: string;
coverSchemes: CoverScheme[];
aiCoverLoading: boolean;
titleSettings?: TitleSettings;
subtitleSettings?: SubtitleSettings;
onClipSelect: (clipId: string) => void;
onCoverSchemeChange: (scheme: string) => void;
onPlayPause: () => void;
@@ -46,6 +68,8 @@ const PreviewPlayer: React.FC<PreviewPlayerProps> = ({
currentCoverScheme,
coverSchemes,
aiCoverLoading,
titleSettings,
subtitleSettings,
onCoverSchemeChange,
onPlayPause,
onAiGenerateCover,
@@ -78,6 +102,46 @@ const PreviewPlayer: React.FC<PreviewPlayerProps> = ({
) : (
<span className="ep-phone-empty-hint"></span>
)}
{/* 标题实时预览 */}
{titleSettings && !titleSettings.aiAutoSelect && titleSettings.title && (
<div
className="ep-preview-title"
style={{
fontSize: `${Math.min(titleSettings.size, 20)}px`,
fontFamily: titleSettings.font,
fontWeight: titleSettings.bold ? "bold" : "normal",
fontStyle: titleSettings.italic ? "italic" : "normal",
textShadow: titleSettings.shadow
? "2px 2px 4px rgba(0,0,0,0.5)"
: "none",
WebkitTextStroke: titleSettings.stroke
? "1px rgba(0,0,0,0.6)"
: "none",
top: titleSettings.position === "top" ? "8px" : titleSettings.position === "center" ? "50%" : "auto",
bottom: titleSettings.position === "bottom" ? "30px" : "auto",
transform: titleSettings.position === "center" ? "translateY(-50%)" : "none",
}}
>
{titleSettings.title}
</div>
)}
{/* 字幕实时预览 */}
{subtitleSettings?.enabled && (
<div
className="ep-preview-subtitle"
style={{
fontSize: `${Math.min(subtitleSettings.size, 14)}px`,
fontFamily: subtitleSettings.font,
top: subtitleSettings.position === "top" ? "8px" : subtitleSettings.position === "center" ? "50%" : "auto",
bottom: subtitleSettings.position === "bottom" ? "8px" : "auto",
transform: subtitleSettings.position === "center" ? "translateY(-50%)" : "none",
}}
>
</div>
)}
</div>
</div>