forked from Tadrisino/MathApp_frontend
lineFit runs before first paint (no 150ms debounce): no flash of unfitted text
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,8 +20,6 @@ const MIN_CHARS = 14;
|
||||
const MAX_GAP = 2.2;
|
||||
|
||||
const observed = new WeakSet<HTMLElement>();
|
||||
const queue = new Set<HTMLElement>();
|
||||
let frame = 0;
|
||||
|
||||
/** پهنای طبیعیِ هر خط (با ترازِ start) از مستطیلهای Range. */
|
||||
function lineWidths(el: HTMLElement, fontSize: number): number[] {
|
||||
@@ -86,25 +84,26 @@ export function fit(el: HTMLElement) {
|
||||
el.style.wordSpacing = best ? `${best}em` : '';
|
||||
}
|
||||
|
||||
function schedule(el: HTMLElement) {
|
||||
queue.add(el);
|
||||
if (frame) return;
|
||||
frame = window.setTimeout(() => {
|
||||
frame = 0;
|
||||
const batch = [...queue];
|
||||
queue.clear();
|
||||
for (const el of batch) fit(el);
|
||||
}, 150);
|
||||
}
|
||||
// همهی اجراها همزمان و پیش از اولین نقاشیِ صفحهاند (MutationObserver/ResizeObserver پیش از paint
|
||||
// صدا زده میشوند)، پس متنِ خام و اصلاحنشده هرگز دیده نمیشود؛ تأخیرِ قبلی (۱۵۰ms) یک چشمک میساخت.
|
||||
const lastWidth = new WeakMap<HTMLElement, number>();
|
||||
|
||||
function watch(el: HTMLElement) {
|
||||
if (observed.has(el)) return;
|
||||
observed.add(el);
|
||||
resizer.observe(el);
|
||||
fit(el);
|
||||
}
|
||||
|
||||
const resizer = new ResizeObserver((entries) => {
|
||||
for (const e of entries) schedule(e.target as HTMLElement);
|
||||
for (const e of entries) {
|
||||
const el = e.target as HTMLElement;
|
||||
const width = e.contentRect.width;
|
||||
// تغییرِ ارتفاعِ خودِ fit نباید دوباره fit را راه بیندازد
|
||||
if (lastWidth.get(el) === width) continue;
|
||||
lastWidth.set(el, width);
|
||||
fit(el);
|
||||
}
|
||||
});
|
||||
|
||||
function scan(node: ParentNode) {
|
||||
@@ -116,12 +115,14 @@ export function startLineFit() {
|
||||
const root = document.getElementById('root')!;
|
||||
scan(root);
|
||||
new MutationObserver((records) => {
|
||||
const changed = new Set<HTMLElement>();
|
||||
for (const r of records) {
|
||||
r.addedNodes.forEach((n) => { if (n instanceof HTMLElement) scan(n); });
|
||||
// متنِ داخلِ پاراگراف عوض شده (مثلاً پاسخِ در حالِ پخش)
|
||||
// متنِ داخلِ پاراگراف عوض شده (سوالِ بعدی، پاسخِ در حالِ پخش)
|
||||
const host = (r.target instanceof HTMLElement ? r.target : r.target.parentElement)?.closest<HTMLElement>(SELECTOR);
|
||||
if (host) schedule(host);
|
||||
if (host) changed.add(host);
|
||||
}
|
||||
changed.forEach(fit);
|
||||
}).observe(root, { childList: true, subtree: true, characterData: true });
|
||||
void document.fonts.ready.then(() => document.querySelectorAll<HTMLElement>(SELECTOR).forEach(schedule));
|
||||
void document.fonts.ready.then(() => document.querySelectorAll<HTMLElement>(SELECTOR).forEach(fit));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user