Skip to content

Commit cbaf55b

Browse files
committed
fix DrawText render unwait
1 parent fe799ee commit cbaf55b

2 files changed

Lines changed: 128 additions & 0 deletions

File tree

render/src/main.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,18 @@ async fn wait_for_animation_ready(page: &Page) {
129129
page.evaluate(script).await.unwrap();
130130
}
131131

132+
async fn wait_for_draw_text_ready(page: &Page) {
133+
let script = r#"
134+
(async () => {
135+
const api = window.__frameScript;
136+
if (api && typeof api.waitDrawTextReady === "function") {
137+
await api.waitDrawTextReady();
138+
}
139+
})()
140+
"#;
141+
page.evaluate(script).await.unwrap();
142+
}
143+
132144
#[tokio::main]
133145
async fn main() -> Result<(), Box<dyn std::error::Error>> {
134146
let args = std::env::args().collect::<Vec<String>>();
@@ -285,6 +297,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
285297
page.wait_for_navigation().await.unwrap();
286298
wait_for_frame_api(&page).await;
287299
wait_for_animation_ready(&page).await;
300+
wait_for_draw_text_ready(&page).await;
288301

289302
for frame in start..end {
290303
wait_for_next_frame(&page).await;

src/lib/animation/effect/draw-text.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,82 @@ type GlyphPath = {
2828

2929
const clamp01 = (value: number) => Math.min(1, Math.max(0, value))
3030
const fontCache = new Map<string, Promise<Font>>()
31+
const DRAW_TEXT_TRACKER_KEY = "__frameScript_DrawTextTracker"
32+
33+
type DrawTextTracker = {
34+
pending: number
35+
start: () => () => void
36+
wait: () => Promise<void>
37+
}
38+
39+
const getDrawTextTracker = () => {
40+
const g = globalThis as unknown as Record<string, unknown>
41+
const existing = g[DRAW_TEXT_TRACKER_KEY] as DrawTextTracker | undefined
42+
if (existing) return existing
43+
44+
let pending = 0
45+
const waiters = new Set<() => void>()
46+
47+
const notifyIfReady = () => {
48+
if (pending !== 0) return
49+
for (const resolve of Array.from(waiters)) {
50+
resolve()
51+
}
52+
waiters.clear()
53+
}
54+
55+
const tracker: DrawTextTracker = {
56+
get pending() {
57+
return pending
58+
},
59+
start: () => {
60+
pending += 1
61+
let done = false
62+
return () => {
63+
if (done) return
64+
done = true
65+
pending = Math.max(0, pending - 1)
66+
notifyIfReady()
67+
}
68+
},
69+
wait: () => {
70+
if (pending === 0) return Promise.resolve()
71+
return new Promise<void>((resolve) => {
72+
waiters.add(resolve)
73+
})
74+
},
75+
}
76+
77+
g[DRAW_TEXT_TRACKER_KEY] = tracker
78+
return tracker
79+
}
80+
81+
const installDrawTextApi = () => {
82+
if (typeof window === "undefined") return
83+
const tracker = getDrawTextTracker()
84+
const waitDrawTextReady = async () => {
85+
while (true) {
86+
if (tracker.pending === 0) {
87+
if (typeof window.requestAnimationFrame !== "function") {
88+
return
89+
}
90+
await new Promise<void>((resolve) => window.requestAnimationFrame(() => resolve()))
91+
if (tracker.pending === 0) return
92+
}
93+
await tracker.wait()
94+
}
95+
}
96+
97+
;(window as any).__frameScript = {
98+
...(window as any).__frameScript,
99+
waitDrawTextReady,
100+
getDrawTextPending: () => tracker.pending,
101+
}
102+
}
103+
104+
if (typeof window !== "undefined") {
105+
installDrawTextApi()
106+
}
31107

32108
const buildFontUrl = (path: string) => {
33109
const url = new URL("http://localhost:3000/file")
@@ -103,12 +179,32 @@ export const DrawText = ({
103179
const [boxSize, setBoxSize] = useState({ width: 0, height: 0 })
104180
const pathRefs = useRef<Array<SVGPathElement | null>>([])
105181
const [glyphLengths, setGlyphLengths] = useState<number[]>([])
182+
const [glyphLoadId, setGlyphLoadId] = useState(0)
183+
const loadIdRef = useRef(0)
184+
const pendingFinishRef = useRef<(() => void) | null>(null)
185+
186+
const beginPending = () => {
187+
loadIdRef.current += 1
188+
if (!pendingFinishRef.current) {
189+
pendingFinishRef.current = getDrawTextTracker().start()
190+
}
191+
return loadIdRef.current
192+
}
193+
194+
const endPending = () => {
195+
if (pendingFinishRef.current) {
196+
pendingFinishRef.current()
197+
pendingFinishRef.current = null
198+
}
199+
}
106200

107201
useEffect(() => {
202+
const loadId = beginPending()
108203
let cancelled = false
109204
const lines = text.split(/\r?\n/)
110205
if (!fontUrl || lines.length === 0) {
111206
setGlyphs([])
207+
setGlyphLoadId(loadId)
112208
setViewBox((prev) => (prev === "0 0 0 0" ? prev : "0 0 0 0"))
113209
setBoxSize((prev) => (prev.width === 0 && prev.height === 0 ? prev : { width: 0, height: 0 }))
114210
return
@@ -167,12 +263,14 @@ export const DrawText = ({
167263
prev.width === width && prev.height === height ? prev : { width, height },
168264
)
169265
setGlyphs(nextGlyphs)
266+
setGlyphLoadId(loadId)
170267
setGlyphLengths([])
171268
})
172269
.catch((error) => {
173270
if (cancelled) return
174271
console.error("DrawText: failed to load font", error)
175272
setGlyphs([])
273+
setGlyphLoadId(loadId)
176274
setViewBox((prev) => (prev === "0 0 0 0" ? prev : "0 0 0 0"))
177275
setBoxSize((prev) => (prev.width === 0 && prev.height === 0 ? prev : { width: 0, height: 0 }))
178276
})
@@ -208,6 +306,23 @@ export const DrawText = ({
208306
})
209307
}, [glyphs])
210308

309+
useEffect(() => {
310+
if (glyphLoadId !== loadIdRef.current) return
311+
if (glyphs.length === 0) {
312+
endPending()
313+
return
314+
}
315+
if (glyphLengths.length === glyphs.length) {
316+
endPending()
317+
}
318+
}, [glyphLoadId, glyphLengths, glyphs])
319+
320+
useEffect(() => {
321+
return () => {
322+
endPending()
323+
}
324+
}, [])
325+
211326
const drawCount = glyphs.reduce(
212327
(count, glyph, index) => count + (!glyph.isGap && (glyphLengths[index] ?? 0) > 0 ? 1 : 0),
213328
0,

0 commit comments

Comments
 (0)