- Introduced a new markdown file documenting the optimizations made to the journey motion rendering, including changes to CSS and React components to improve performance on low-end devices. - Implemented a test-only JavaScript file to log layout work during automatic stop changes, tracking metrics such as name resizes, font writes, and heading replacements. - Enhanced the journey rendering by memoizing components and reducing unnecessary re-renders, aligning with web.dev recommendations for animations and layout thrashing prevention.
39 lines
2.1 KiB
JavaScript
39 lines
2.1 KiB
JavaScript
// Test-only observers: no prototype patches, production telemetry or state changes.
|
|
// Logs layout work over 1200 ms after each automatic stop change, not target FPS.
|
|
let focus = null, run = null;
|
|
const boxes = new Set();
|
|
const resize = new ResizeObserver(entries => {
|
|
if (run) run.nameResizes += entries.filter(entry => boxes.has(entry.target)).length;
|
|
});
|
|
const mutations = new MutationObserver(records => {
|
|
const cells = Array.from(document.querySelectorAll('.service-info[data-active="true"] .journey-station'));
|
|
const next = cells.findIndex(cell => cell.getAttribute('data-focused') === 'true');
|
|
if (next < 0) { focus = null; return; }
|
|
if (focus !== null && focus !== next) {
|
|
if (run) { clearTimeout(run.timer); finish(); }
|
|
run = { from: focus + 1, to: next + 1, profile: document.querySelector('.board')?.getAttribute('data-profile'),
|
|
nameResizes: 0, fontWrites: 0, headingReplacements: 0, started: performance.now(), timer: null };
|
|
run.timer = setTimeout(finish, 1200);
|
|
}
|
|
focus = next;
|
|
for (const box of document.querySelectorAll('.service-info[data-active="true"] .journey-station-name')) {
|
|
if (!boxes.has(box)) { boxes.add(box); resize.observe(box); }
|
|
}
|
|
for (const box of boxes) if (!document.contains(box)) { resize.unobserve(box); boxes.delete(box); }
|
|
if (!run) return;
|
|
for (const record of records) {
|
|
if (record.type === 'attributes' && record.attributeName === 'style'
|
|
&& record.target.matches('.journey-station-name .destination-heading')) run.fontWrites++;
|
|
if (record.type === 'childList' && record.target.matches('.journey-station-name')) {
|
|
run.headingReplacements += Array.from(record.removedNodes).filter(node => node.nodeType === 1 && node.matches('.destination-heading')).length;
|
|
}
|
|
}
|
|
});
|
|
function finish() {
|
|
if (!run) return;
|
|
const { timer, started, ...result } = run;
|
|
run = null;
|
|
console.info('JOURNEY_PERF ' + JSON.stringify({ ...result, windowMs: Math.round(performance.now() - started) }));
|
|
}
|
|
mutations.observe(document.body, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-focused', 'style'] });
|