From 5bd80b1333d4299f537d5be96a4030447aa8146d Mon Sep 17 00:00:00 2001 From: Ulas Date: Thu, 16 Apr 2026 13:50:38 +0200 Subject: [PATCH] fix: reduce page transition lag on Android (closes #48) Two causes of ~1s navigation delay fixed: 1. glass.css Section 19 was extending the page-in animation from 0.20s to 0.30s using spring easing. Reverted to 0.20s in / 0.12s out. 2. During transitions, dozens of backdrop-filter layers (widgets, cards, inputs, toolbars) were composited simultaneously for both the outgoing and incoming page, overloading mid-range Android GPUs. Added html.navigating class: router.js sets it at transition start, glass.css overrides all app-content backdrop-filters to none while active, animationend removes it once the in-animation completes. --- CHANGELOG.md | 5 +++++ package.json | 2 +- public/router.js | 15 +++++++++++++++ public/styles/glass.css | 15 +++++++++++++-- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9754ad7..147264d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.6] - 2026-04-16 + +### Fixed +- Android PWA: page transitions were taking ~1 second, making navigation feel sluggish. Two root causes addressed: (1) `glass.css` extended the page-in animation duration from `0.20s` to `0.30s` with a spring easing (`ease-glass`) — reverted to `0.20s` in and `0.12s` out to match the layout baseline. (2) During transitions, dozens of `backdrop-filter` composited layers (widgets, task cards, inputs, toolbars) were all rendered simultaneously for both the outgoing and incoming page, overwhelming mid-range Android GPUs. Added `html.navigating` state: `router.js` sets this class for the duration of each page transition, and `glass.css` overrides all `backdrop-filter` in the content area to `none` for that window, then restores them once the animation ends (closes #48). + ## [0.20.5] - 2026-04-16 ### Fixed diff --git a/package.json b/package.json index cfd5a95..9989e5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oikos", - "version": "0.20.5", + "version": "0.20.6", "description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.", "main": "server/index.js", "type": "module", diff --git a/public/router.js b/public/router.js index 4e97e0b..d6ba5e4 100644 --- a/public/router.js +++ b/public/router.js @@ -239,6 +239,10 @@ async function renderPage(route, previousPath = null) { const outClass = direction === 'right' ? 'page-transition--out-left' : 'page-transition--out-right'; const inClass = direction === 'right' ? 'page-transition--in-right' : 'page-transition--in-left'; + // Performance: backdrop-filter während Übergang deaktivieren (Android-Optimierung). + // glass.css setzt alle backdrop-filter im app-content auf none solange diese Klasse aktiv ist. + document.documentElement.classList.add('navigating'); + // Alte Seite kurz ausfaden, falls vorhanden const oldPage = content.querySelector('.page-transition'); if (oldPage) { @@ -259,7 +263,18 @@ async function renderPage(route, previousPath = null) { pageWrapper.style.opacity = ''; pageWrapper.classList.add(inClass); + // navigating-Klasse nach Ende der Einblend-Animation entfernen. + // Fallback-Timeout falls animationend nicht feuert (z.B. prefers-reduced-motion). + const navEndTimeout = setTimeout(() => { + document.documentElement.classList.remove('navigating'); + }, 300); + pageWrapper.addEventListener('animationend', () => { + clearTimeout(navEndTimeout); + document.documentElement.classList.remove('navigating'); + }, { once: true }); + } catch (err) { + document.documentElement.classList.remove('navigating'); console.error('[Router] Seiten-Render-Fehler:', err); renderError(app, err); } diff --git a/public/styles/glass.css b/public/styles/glass.css index d347389..c69de28 100644 --- a/public/styles/glass.css +++ b/public/styles/glass.css @@ -400,16 +400,27 @@ textarea.form-input { * ================================================================ */ .page-transition--in-right, .page-transition--in-left { - animation-duration: 0.30s; + animation-duration: 0.20s; animation-timing-function: var(--ease-glass); } .page-transition--out-left, .page-transition--out-right { - animation-duration: 0.14s; + animation-duration: 0.12s; animation-timing-function: var(--ease-out); } +/* Performance: backdrop-filter deaktivieren während Seitenübergängen. + * Auf Android verursachen viele gleichzeitige backdrop-filter-Layer + * (Widgets, Cards, Inputs) hohen GPU-Aufwand → Transition wirkt träge. + * html.navigating wird von router.js für die Dauer des Übergangs gesetzt. */ +html.navigating .app-content *, +html.navigating .app-content *::before, +html.navigating .app-content *::after { + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; +} + /* ================================================================ * 20. List-Stagger — Spring Timing * ================================================================ */