fix(幕布): 修正 G/D 内圈轮廓画不到的问题

- 原先按统一系数 4.2 估算轮廓长度,只够走完外轮廓;G/D 的内圈反轮廓是第二条闭合路径,永远画不到(表现为边缘没填实)

- 改为按字母给轮廓/字宽比(G/D 6.0,V 3.8,S 3.6),并让五个字差不多同时收笔

- 实心补入时机从 1.65s 顺延到 1.8s,压在收笔那一刻
This commit is contained in:
TermiNexus committed 2026-09-29 23:39:38 +08:00
1 parent ac02043415
commit c86f78d549
1 file changed
+19 -14
+19 -14
View File
@@ -447,11 +447,14 @@ function hideResultsSoon(): void {
* 会顺着所有字形轮廓连续走,只有拆成独立元素才能五个字同时起笔 */
const curtainLetters = ['G', 'V', 'S', 'D', 'S'] as const
/** 轮廓长度估算系数。getTotalLength() 只对 <path> 这类几何元素生效,<text> 取不到
* 真实轮廓长度,只能用字宽 getComputedTextLength() 乘经验系数估:
* 大写 D / G 的轮廓周长约为字宽的 4.0 倍、S / V 约 3.0 倍。取 4.2 保证所有笔画都能收尾
* (取小了尾部一段永远画不满),代价是笔画少的 S / V 会比 D / G 稍早停笔 */
const CURTAIN_DASH_FACTOR = 4.2
/** 每个字母"轮廓周长 ÷ 字宽"的估算比(大写,含内轮廓)。
* 带碗形内圈的 G / D 要沿**外轮廓 + 内圈反轮廓两条闭合路径**走完,约为字宽的 5.4 倍;
* V 只有两条斜画、S 是一条蛇形画,约 3.0 ~ 3.5 倍。统一再留 ~12% 余量:
* 系数给小了内圈那段永远画不到(看着就是"边缘没填实"),给大了字母会提前停笔。
* 各字母单算而不是取一个统一系数,是为了让五个字差不多同时收笔。 */
const CURTAIN_LETTER_RATIO: Record<string, number> = { G: 6.0, V: 3.8, S: 3.6, D: 6.0 }
/** 表里没有的字母按这个兜底(单条轮廓的常规估算) */
const CURTAIN_DASH_FALLBACK = 3.6
/** 幕布露出这么多比例即起笔 */
const CURTAIN_DRAW_AT = 0.6
@@ -478,23 +481,23 @@ let curtainGlideUntil = 0
/**
* 幕布初始化(挂载后跑一次):
* ① 先等字体就绪——字体没加载完就量,量到的是回退字体的字宽,字母会排错位;
* ② 逐个字母量宽:按累计宽度排 x,并按估算的轮廓周长写 --dash(虚线的长与偏移初值);
* ② 逐个字母量宽:按累计宽度排 x,并按"字宽 × 该字母轮廓比"写 --dash(虚线的长与偏移初值);
* ③ 按整组实测 bbox 设 viewBox,让五个字母整体铺满这半屏幕布,四周留一点边不贴框。
*/
async function setupCurtain(): Promise<void> {
const svg = curtainSvg.value
const els = letterEls.filter((el): el is SVGTextElement => el !== null)
if (!svg || els.length === 0) return
if (!svg || letterEls.every((el) => el === null)) return
try {
await document.fonts.ready
} catch {
// 字体接口不可用就按当前字体量(退到系统无衬线,位置仍自洽)
}
let x = 0
els.forEach((el) => {
const advance = el.getComputedTextLength()
el.setAttribute('x', String(x))
el.style.setProperty('--dash', String(advance * CURTAIN_DASH_FACTOR))
curtainLetters.forEach((ch, i) => {
const el = letterEls[i]
const advance = el ? el.getComputedTextLength() : 0
el?.setAttribute('x', String(x))
el?.style.setProperty('--dash', String(advance * (CURTAIN_LETTER_RATIO[ch] ?? CURTAIN_DASH_FALLBACK)))
x += advance
})
const box = curtainGroup.value?.getBBox()
@@ -754,14 +757,16 @@ watch(
/* 起笔:虚线偏移归零 → 描边沿着字形轮廓把笔画画出来;随后实心补上,回到原来的实心白字。
transition 只写在 .is-drawn 里:量完写 --dash 那一次改动不能带过渡,
否则偏移会从 0 缓到量好的值(等于提前把字画一遍) */
否则偏移会从 0 缓到量好的值(等于提前把字画一遍)。
实心延迟 1.8s 是压在五个字都收完笔那一刻(系数留了 12% 余量,约九成时长收笔),
让它们同时变实心;太早填会把还没画到的笔画盖住 */
.brand-curtain-letter.is-drawn {
stroke-opacity: 1;
stroke-dashoffset: 0;
fill-opacity: 1;
transition:
stroke-dashoffset 2s cubic-bezier(0.33, 0, 0.55, 1),
fill-opacity 0.5s ease 1.65s;
fill-opacity 0.5s ease 1.8s;
}
/* 末尾占位:与幕布等高(半屏,svh 避开移动端地址栏收缩导致的跳动),透明 → 露出幕布 */