fix: 调整字幕/标题字体大小基准值(1080p/720p)
按用户指定值统一调整: - 字幕:64(1080p) / 40(720p) - 大标题:90(1080p) / 64(720p) - 小标题:72(1080p) / 50(720p) - 标题 PNG 大标题:104(1080p) / 64(720p) - 封面 PNG 大标题:144(1080p) / 96(720p)(144×0.667=96,已一致) ASS 的 margin/outline 仍按分辨率比例缩放,fontSize 直接 hardcode 为指定值。标题 PNG 大标题使用独立字体大小(与 ASS 大标题区分), 小标题保持 ASS 小标题值。
This commit is contained in:
@@ -188,47 +188,56 @@ export default function SubtitleBurning() {
|
||||
// 构建 ASS Style 参数
|
||||
const videoDurationMs = (alignment?.duration || 0) * 1000;
|
||||
|
||||
const buildSubtitleStyle = (preset: TitlePreset, scale: number = 1): Partial<AssStyle> => ({
|
||||
fontSize: Math.round(64 * scale),
|
||||
primaryColor: htmlColorToAss(preset.primaryColor),
|
||||
outlineColor: htmlColorToAss(preset.outlineColor),
|
||||
backColor: htmlColorToAss(preset.backColor),
|
||||
borderStyle: preset.borderStyle,
|
||||
outline: Math.round(preset.outline * scale),
|
||||
shadow: 0,
|
||||
alignment: 2,
|
||||
marginV: Math.round(480 * scale),
|
||||
marginL: Math.round(160 * scale),
|
||||
marginR: Math.round(160 * scale),
|
||||
});
|
||||
const buildSubtitleStyle = (preset: TitlePreset, scale: number = 1): Partial<AssStyle> => {
|
||||
const is720 = scale <= 0.7;
|
||||
return {
|
||||
fontSize: is720 ? 40 : 64,
|
||||
primaryColor: htmlColorToAss(preset.primaryColor),
|
||||
outlineColor: htmlColorToAss(preset.outlineColor),
|
||||
backColor: htmlColorToAss(preset.backColor),
|
||||
borderStyle: preset.borderStyle,
|
||||
outline: Math.round(preset.outline * scale),
|
||||
shadow: 0,
|
||||
alignment: 2,
|
||||
marginV: Math.round(480 * scale),
|
||||
marginL: Math.round(160 * scale),
|
||||
marginR: Math.round(160 * scale),
|
||||
};
|
||||
};
|
||||
|
||||
const buildMainTitleStyle = (preset: TitlePreset, scale: number = 1): Partial<AssStyle> => ({
|
||||
fontSize: Math.round(96 * scale),
|
||||
primaryColor: htmlColorToAss(preset.primaryColor),
|
||||
outlineColor: htmlColorToAss(preset.outlineColor),
|
||||
backColor: htmlColorToAss(preset.backColor),
|
||||
borderStyle: preset.borderStyle,
|
||||
outline: Math.round(preset.outline * scale),
|
||||
shadow: 0,
|
||||
alignment: 8,
|
||||
marginV: Math.round(320 * scale),
|
||||
marginL: Math.round(160 * scale),
|
||||
marginR: Math.round(160 * scale),
|
||||
});
|
||||
const buildMainTitleStyle = (preset: TitlePreset, scale: number = 1): Partial<AssStyle> => {
|
||||
const is720 = scale <= 0.7;
|
||||
return {
|
||||
fontSize: is720 ? 64 : 90,
|
||||
primaryColor: htmlColorToAss(preset.primaryColor),
|
||||
outlineColor: htmlColorToAss(preset.outlineColor),
|
||||
backColor: htmlColorToAss(preset.backColor),
|
||||
borderStyle: preset.borderStyle,
|
||||
outline: Math.round(preset.outline * scale),
|
||||
shadow: 0,
|
||||
alignment: 8,
|
||||
marginV: Math.round(320 * scale),
|
||||
marginL: Math.round(160 * scale),
|
||||
marginR: Math.round(160 * scale),
|
||||
};
|
||||
};
|
||||
|
||||
const buildSubTitleStyle = (preset: TitlePreset, scale: number = 1): Partial<AssStyle> => ({
|
||||
fontSize: Math.round(80 * scale),
|
||||
primaryColor: htmlColorToAss(preset.primaryColor),
|
||||
outlineColor: htmlColorToAss(preset.outlineColor),
|
||||
backColor: htmlColorToAss(preset.backColor),
|
||||
borderStyle: preset.borderStyle,
|
||||
outline: Math.round(preset.outline * scale),
|
||||
shadow: 0,
|
||||
alignment: 8,
|
||||
marginV: Math.round(440 * scale),
|
||||
marginL: Math.round(160 * scale),
|
||||
marginR: Math.round(160 * scale),
|
||||
});
|
||||
const buildSubTitleStyle = (preset: TitlePreset, scale: number = 1): Partial<AssStyle> => {
|
||||
const is720 = scale <= 0.7;
|
||||
return {
|
||||
fontSize: is720 ? 50 : 72,
|
||||
primaryColor: htmlColorToAss(preset.primaryColor),
|
||||
outlineColor: htmlColorToAss(preset.outlineColor),
|
||||
backColor: htmlColorToAss(preset.backColor),
|
||||
borderStyle: preset.borderStyle,
|
||||
outline: Math.round(preset.outline * scale),
|
||||
shadow: 0,
|
||||
alignment: 8,
|
||||
marginV: Math.round(440 * scale),
|
||||
marginL: Math.round(160 * scale),
|
||||
marginR: Math.round(160 * scale),
|
||||
};
|
||||
};
|
||||
|
||||
// 用 text-shadow 模拟 ASS 外描边(比 WebkitTextStroke 的居中描边更准确)
|
||||
const getOutlineShadow = (color: string, outlineWidth: number): string => {
|
||||
@@ -391,10 +400,12 @@ export default function SubtitleBurning() {
|
||||
|
||||
if ((mainTitle && mtStyle) || (subTitle && stStyle)) {
|
||||
const { generateTitlePngDataUrl } = await import('../../utils/titlePngGenerator');
|
||||
// 标题 PNG 使用独立的字体大小(与 ASS 不同)
|
||||
const titlePngMainStyle = mtStyle ? { ...mtStyle, fontSize: targetWidth === 720 ? 64 : 104 } : undefined;
|
||||
const pngDataUrl = await generateTitlePngDataUrl(
|
||||
mainTitle || undefined,
|
||||
subTitle || undefined,
|
||||
mtStyle,
|
||||
titlePngMainStyle,
|
||||
stStyle,
|
||||
targetWidth,
|
||||
targetHeight,
|
||||
|
||||
Reference in New Issue
Block a user