Performance: stop iPhone freezes (blur over live map, runaway animation, eager charts)
Publish Container / publish (push) Successful in 2m57s
Publish Container / publish (push) Successful in 2m57s
Main app - No backdrop-filter over the live map, animated sky, or sticky headings (it re-blurs every frame; on iPhone that is a classic stutter/lock-up); top/bottom bars keep a light 8px blur - Sky effect only draws while the hero is on screen and the Now tab is active, ~30fps cap, fewer particles on phones; fixed a stale animation-frame id that could stop restarts Dashboard - Charts are built only when the Charts tab is opened (was ~11 charts on every page load), and the 2-minute refresh no longer rebuilds them in the background - On phones: max 360 points per chart (was 720), no entry animation, device pixel ratio capped at 2 - Lighter header blur, none on small buttons/cards Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dde59e3865
commit
fda8dca435
5 files changed
+53
-13
No files matched your search
+16
-7
@@ -53,7 +53,10 @@ function skyFor(code, isDay, stationRaining) {
|
||||
return isDay ? 'clear-day' : 'clear-night';
|
||||
}
|
||||
|
||||
const fx = { raf: 0, kind: null, parts: [], w: 0, h: 0, flash: 0 };
|
||||
const fx = { raf: 0, kind: null, parts: [], w: 0, h: 0, flash: 0, last: 0, onScreen: true };
|
||||
const PHONE = window.matchMedia('(max-width: 640px)').matches;
|
||||
const fxShouldRun = () => fx.parts.length && !REDUCED && !document.hidden && fx.onScreen && document.body.dataset.tab === 'conditions';
|
||||
function fxKick() { if (!fx.raf && fxShouldRun()) fx.raf = requestAnimationFrame(fxFrame); }
|
||||
function fxResize(canvas) {
|
||||
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
||||
fx.w = canvas.clientWidth; fx.h = canvas.clientHeight;
|
||||
@@ -61,7 +64,8 @@ function fxResize(canvas) {
|
||||
canvas.getContext('2d').setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
}
|
||||
function fxSeed(kind) {
|
||||
const n = kind === 'storm' ? 130 : kind === 'rain' ? 85 : kind === 'snow' ? 70 : kind === 'clear-night' ? 55 : 0;
|
||||
const base = kind === 'storm' ? 130 : kind === 'rain' ? 85 : kind === 'snow' ? 70 : kind === 'clear-night' ? 55 : 0;
|
||||
const n = Math.round(base * (PHONE ? 0.55 : 1));
|
||||
fx.parts = Array.from({ length: n }, () => ({
|
||||
x: Math.random() * fx.w, y: Math.random() * fx.h,
|
||||
v: kind === 'snow' ? 0.4 + Math.random() * 0.9 : kind === 'clear-night' ? 0 : 6 + Math.random() * 7,
|
||||
@@ -70,8 +74,11 @@ function fxSeed(kind) {
|
||||
}));
|
||||
}
|
||||
function fxFrame(t) {
|
||||
fx.raf = 0;
|
||||
const canvas = $('sky-fx');
|
||||
if (!canvas) return;
|
||||
if (!canvas || !fxShouldRun()) return; // hidden tab / scrolled away / app in background: stop drawing entirely
|
||||
if (t - fx.last < 33) { fx.raf = requestAnimationFrame(fxFrame); return; } // cap at ~30 fps
|
||||
fx.last = t;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, fx.w, fx.h);
|
||||
const kind = fx.kind;
|
||||
@@ -100,21 +107,23 @@ function fxFrame(t) {
|
||||
ctx.beginPath(); ctx.arc(p.x, p.y * 0.8, p.r, 0, Math.PI * 2); ctx.fill();
|
||||
}
|
||||
}
|
||||
if (!REDUCED && !document.hidden) fx.raf = requestAnimationFrame(fxFrame);
|
||||
fx.raf = requestAnimationFrame(fxFrame);
|
||||
}
|
||||
function setSky(kind) {
|
||||
const hero = $('hero'), canvas = $('sky-fx');
|
||||
if (!hero || !canvas) return;
|
||||
hero.dataset.sky = kind;
|
||||
cancelAnimationFrame(fx.raf);
|
||||
fx.raf = 0;
|
||||
fx.kind = kind;
|
||||
fxResize(canvas);
|
||||
fxSeed(kind);
|
||||
if (fx.parts.length) fxFrame(performance.now());
|
||||
if (fx.parts.length) fxKick();
|
||||
else canvas.getContext('2d').clearRect(0, 0, fx.w, fx.h);
|
||||
}
|
||||
window.addEventListener('resize', () => { const c = $('sky-fx'); if (c && fx.kind) { fxResize(c); fxSeed(fx.kind); } });
|
||||
document.addEventListener('visibilitychange', () => { if (!document.hidden && fx.parts.length && !REDUCED) { cancelAnimationFrame(fx.raf); fxFrame(performance.now()); } });
|
||||
document.addEventListener('visibilitychange', fxKick);
|
||||
if ('IntersectionObserver' in window && $('hero')) new IntersectionObserver((es) => { fx.onScreen = es[0].isIntersecting; fxKick(); }).observe($('hero'));
|
||||
|
||||
// ── Nowcast: next 2 hours from 15-minute precipitation ───────────────────────
|
||||
function nowcastSlots(data) {
|
||||
@@ -459,7 +468,7 @@ window.AppUI = {
|
||||
centerMap,
|
||||
refreshGeoStatus: () => GeoStatus.refresh(),
|
||||
onAir(air) { renderAqi(air); },
|
||||
refreshFx() { const c = $('sky-fx'); if (c && fx.kind) { cancelAnimationFrame(fx.raf); fxResize(c); fxSeed(fx.kind); if (fx.parts.length) fxFrame(performance.now()); } },
|
||||
refreshFx() { const c = $('sky-fx'); if (c && fx.kind) { cancelAnimationFrame(fx.raf); fx.raf = 0; fxResize(c); fxSeed(fx.kind); fxKick(); } },
|
||||
onStation() { syncTopbarLocation(); renderStationContext(); },
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user