From 00e3beebfc4a7e8a01a7d8547bd2b9818d93a68e Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 27 Oct 2023 14:23:50 +0200 Subject: [PATCH] Better view --- src/index.tsx | 3 ++ src/util/betterView.ts | 92 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/util/betterView.ts diff --git a/src/index.tsx b/src/index.tsx index 4c13f0d27..d64b190a6 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -13,6 +13,7 @@ import { } from './config'; import { enableStrict, requestMutation } from './lib/fasterdom/fasterdom'; import { selectTabState } from './global/selectors'; +import { betterView } from './util/betterView'; import { establishMultitabRole, subscribeToMasterChange } from './util/establishMultitabRole'; import { requestGlobal, subscribeToMultitabBroadcastChannel } from './util/multitab'; import { checkAndAssignPermanentWebVersion } from './util/permanentWebVersion'; @@ -81,6 +82,8 @@ async function init() { , document.getElementById('root')!, ); + + betterView(); }); if (DEBUG) { diff --git a/src/util/betterView.ts b/src/util/betterView.ts new file mode 100644 index 000000000..cbde96f34 --- /dev/null +++ b/src/util/betterView.ts @@ -0,0 +1,92 @@ +import { animate } from './animation'; +import { fastRaf } from './schedulers'; +import { IS_IOS } from './windowEnvironment'; + +const TEST_INTERVAL = 5000; // 5 sec +const FRAMES_TO_TEST = 10; +const REDUCED_FPS = 35; + +let isImproved = false; + +export function betterView() { + if (!IS_IOS) return; + + let interval: number | undefined; + let lastFocusAt = Date.now(); + + function setupInterval() { + if (interval || isImproved) return; + + interval = window.setInterval(testAndImprove, TEST_INTERVAL); + } + + window.addEventListener('focus', () => { + const now = Date.now(); + if (now - lastFocusAt < 100) return; // iOS triggers two `focus` events for some reason + lastFocusAt = now; + + setupInterval(); + testAndImprove(); + }); + + window.addEventListener('blur', () => { + clearInterval(interval); + interval = undefined; + }); + + if (document.hasFocus()) { + setupInterval(); + testAndImprove(); + } +} + +async function testAndImprove() { + const fps = await testFps(); + if (fps <= REDUCED_FPS) { + improveView(); + } +} + +function testFps() { + return new Promise((resolve) => { + const frames: number[] = []; + let lastFrameAt = performance.now(); + + animate(() => { + const now = performance.now(); + frames.push(now - lastFrameAt); + lastFrameAt = now; + + if (frames.length === FRAMES_TO_TEST) { + const mean = frames.sort()[Math.floor(frames.length / 2)]; + resolve(Math.round(1000 / mean)); + return false; + } + + return true; + }, fastRaf); + }); +} + +function improveView() { + isImproved = true; + + const containerEl = document.createElement('div'); + containerEl.style.cssText = 'position: absolute; top: 0; left: 0; width: 0; height: 100%; overflow: hidden;'; + + const boosterEl = document.createElement('div'); + const height = window.screen.height * 1.5; + boosterEl.style.cssText = `width: 0; height: ${height}px; transform: translateX(100%); transition: transform 100ms;`; + boosterEl.innerHTML = ' '; + + containerEl.appendChild(boosterEl); + document.body.appendChild(containerEl); + + requestAnimationFrame(() => { + boosterEl.addEventListener('transitionend', () => { + containerEl.remove(); + }); + + boosterEl.style.transform = ''; + }); +}