diff --git a/birb.js b/birb.js index 0b0458b..3715d1f 100644 --- a/birb.js +++ b/birb.js @@ -32,7 +32,7 @@ const HOP_SPEED = CONFIG.hopSpeed; const FLY_SPEED = CONFIG.flySpeed; const HOP_DISTANCE = CONFIG.hopDistance; // Time in milliseconds until the user is considered AFK -const AFK_TIME = (debugMode || isMobile()) ? 0 : 1000 * 30; +const AFK_TIME = debugMode ? 0 : 1000 * 30; const SPRITE_HEIGHT = 32; const MENU_ID = "birb-menu"; const MENU_EXIT_ID = "birb-menu-exit"; @@ -957,11 +957,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI window.addEventListener("scroll", () => { lastActionTimestamp = Date.now(); - // Can't keep up with scrolling on mobile devices so fly down instead - if (isMobile()) { - // focusOnGround(); - } - }); onClick(document, (e) => { @@ -1668,22 +1663,30 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return birdX >= focusedBounds.left && birdX <= focusedBounds.right; } - // function getFocusedElementY() { - // if (focusedElement === null) { - // return 0; - // } - // const rect = focusedElement.getBoundingClientRect(); - // return window.innerHeight - rect.top; - // } - function getFocusedY() { - return window.innerHeight - focusedBounds.top; + return getFullWindowHeight() - focusedBounds.top; } + /** + * @returns The render-safe height of the inner browser window + */ + function getSafeWindowHeight() { + // Necessary because iOS 26 Safari is terrible and won't render + // fixed elements behind the address bar + return window.innerHeight; + } + + /** + * @returns The true height of the inner browser window + */ + function getFullWindowHeight() { + return document.documentElement.clientHeight; + } + function focusOnGround() { console.log("Focusing on ground"); focusedElement = null; - focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight }; + focusedBounds = { left: 0, right: window.innerWidth, top: getSafeWindowHeight() }; flyTo(Math.random() * window.innerWidth, 0); } @@ -1713,7 +1716,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI function updateFocusedElementBounds() { if (focusedElement === null) { // Update ground location to bottom of window - focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight }; + focusedBounds = { left: 0, right: window.innerWidth, top: getFullWindowHeight() }; return; } const rect = focusedElement.getBoundingClientRect(); @@ -1771,6 +1774,13 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI setAnimation(Animations.FLYING); } + /** + * @returns {boolean} Whether the bird should be absolutely positioned + */ + function isAbsolute() { + return focusedElement !== null && (currentState === States.IDLE || currentState === States.HOP); + } + /** * Set the current animation and reset the animation timer * @param {Anim} animation @@ -1792,6 +1802,12 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI if (state === States.IDLE) { setAnimation(Animations.BOB); } + if (isAbsolute()) { + canvas.classList.add("birb-absolute"); + } else { + canvas.classList.remove("birb-absolute"); + } + setY(birdY); } /** @@ -1806,7 +1822,15 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI * @param {number} y */ function setY(y) { - canvas.style.bottom = `${y}px`; + let bottom; + if (isAbsolute()) { + // Position is absolute, convert from fixed + bottom = y - window.scrollY; + } else { + // Position is fixed + bottom = y; + } + canvas.style.bottom = `${bottom}px`; } }); diff --git a/build.js b/build.js index e369220..4169937 100644 --- a/build.js +++ b/build.js @@ -24,7 +24,7 @@ const userScriptHeader = `// ==UserScript== // @name Pocket Bird // @namespace https://idreesinc.com -// @version 2025-10-22-04 +// @version 2025-10-23-01 // @description birb // @author Idrees // @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js diff --git a/dist/birb.js b/dist/birb.js index aa3204d..8e1113a 100644 --- a/dist/birb.js +++ b/dist/birb.js @@ -32,7 +32,7 @@ const HOP_SPEED = CONFIG.hopSpeed; const FLY_SPEED = CONFIG.flySpeed; const HOP_DISTANCE = CONFIG.hopDistance; // Time in milliseconds until the user is considered AFK -const AFK_TIME = (debugMode || isMobile()) ? 0 : 1000 * 30; +const AFK_TIME = debugMode ? 0 : 1000 * 30; const SPRITE_HEIGHT = 32; const MENU_ID = "birb-menu"; const MENU_EXIT_ID = "birb-menu-exit"; @@ -74,6 +74,10 @@ const STYLESHEET = `:root { cursor: pointer; } +.birb-absolute { + position: absolute !important; +} + .birb-decoration { image-rendering: pixelated; position: fixed; @@ -1296,11 +1300,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI window.addEventListener("scroll", () => { lastActionTimestamp = Date.now(); - // Can't keep up with scrolling on mobile devices so fly down instead - if (isMobile()) { - // focusOnGround(); - } - }); onClick(document, (e) => { @@ -2007,22 +2006,30 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return birdX >= focusedBounds.left && birdX <= focusedBounds.right; } - // function getFocusedElementY() { - // if (focusedElement === null) { - // return 0; - // } - // const rect = focusedElement.getBoundingClientRect(); - // return window.innerHeight - rect.top; - // } - function getFocusedY() { - return window.innerHeight - focusedBounds.top; + return getFullWindowHeight() - focusedBounds.top; } + /** + * @returns The render-safe height of the inner browser window + */ + function getSafeWindowHeight() { + // Necessary because iOS 26 Safari is terrible and won't render + // fixed elements behind the address bar + return window.innerHeight; + } + + /** + * @returns The true height of the inner browser window + */ + function getFullWindowHeight() { + return document.documentElement.clientHeight; + } + function focusOnGround() { console.log("Focusing on ground"); focusedElement = null; - focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight }; + focusedBounds = { left: 0, right: window.innerWidth, top: getSafeWindowHeight() }; flyTo(Math.random() * window.innerWidth, 0); } @@ -2052,7 +2059,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI function updateFocusedElementBounds() { if (focusedElement === null) { // Update ground location to bottom of window - focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight }; + focusedBounds = { left: 0, right: window.innerWidth, top: getFullWindowHeight() }; return; } const rect = focusedElement.getBoundingClientRect(); @@ -2110,6 +2117,13 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI setAnimation(Animations.FLYING); } + /** + * @returns {boolean} Whether the bird should be absolutely positioned + */ + function isAbsolute() { + return focusedElement !== null && (currentState === States.IDLE || currentState === States.HOP); + } + /** * Set the current animation and reset the animation timer * @param {Anim} animation @@ -2131,6 +2145,12 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI if (state === States.IDLE) { setAnimation(Animations.BOB); } + if (isAbsolute()) { + canvas.classList.add("birb-absolute"); + } else { + canvas.classList.remove("birb-absolute"); + } + setY(birdY); } /** @@ -2145,7 +2165,15 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI * @param {number} y */ function setY(y) { - canvas.style.bottom = `${y}px`; + let bottom; + if (isAbsolute()) { + // Position is absolute, convert from fixed + bottom = y - window.scrollY; + } else { + // Position is fixed + bottom = y; + } + canvas.style.bottom = `${bottom}px`; } }); diff --git a/dist/birb.user.js b/dist/birb.user.js index bc1c881..0827b5d 100644 --- a/dist/birb.user.js +++ b/dist/birb.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Pocket Bird // @namespace https://idreesinc.com -// @version 2025-10-22-04 +// @version 2025-10-23-01 // @description birb // @author Idrees // @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js @@ -46,7 +46,7 @@ const HOP_SPEED = CONFIG.hopSpeed; const FLY_SPEED = CONFIG.flySpeed; const HOP_DISTANCE = CONFIG.hopDistance; // Time in milliseconds until the user is considered AFK -const AFK_TIME = (debugMode || isMobile()) ? 0 : 1000 * 30; +const AFK_TIME = debugMode ? 0 : 1000 * 30; const SPRITE_HEIGHT = 32; const MENU_ID = "birb-menu"; const MENU_EXIT_ID = "birb-menu-exit"; @@ -88,6 +88,10 @@ const STYLESHEET = `:root { cursor: pointer; } +.birb-absolute { + position: absolute !important; +} + .birb-decoration { image-rendering: pixelated; position: fixed; @@ -1310,11 +1314,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI window.addEventListener("scroll", () => { lastActionTimestamp = Date.now(); - // Can't keep up with scrolling on mobile devices so fly down instead - if (isMobile()) { - // focusOnGround(); - } - }); onClick(document, (e) => { @@ -2021,22 +2020,30 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return birdX >= focusedBounds.left && birdX <= focusedBounds.right; } - // function getFocusedElementY() { - // if (focusedElement === null) { - // return 0; - // } - // const rect = focusedElement.getBoundingClientRect(); - // return window.innerHeight - rect.top; - // } - function getFocusedY() { - return window.innerHeight - focusedBounds.top; + return getFullWindowHeight() - focusedBounds.top; } + /** + * @returns The render-safe height of the inner browser window + */ + function getSafeWindowHeight() { + // Necessary because iOS 26 Safari is terrible and won't render + // fixed elements behind the address bar + return window.innerHeight; + } + + /** + * @returns The true height of the inner browser window + */ + function getFullWindowHeight() { + return document.documentElement.clientHeight; + } + function focusOnGround() { console.log("Focusing on ground"); focusedElement = null; - focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight }; + focusedBounds = { left: 0, right: window.innerWidth, top: getSafeWindowHeight() }; flyTo(Math.random() * window.innerWidth, 0); } @@ -2066,7 +2073,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI function updateFocusedElementBounds() { if (focusedElement === null) { // Update ground location to bottom of window - focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight }; + focusedBounds = { left: 0, right: window.innerWidth, top: getFullWindowHeight() }; return; } const rect = focusedElement.getBoundingClientRect(); @@ -2124,6 +2131,13 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI setAnimation(Animations.FLYING); } + /** + * @returns {boolean} Whether the bird should be absolutely positioned + */ + function isAbsolute() { + return focusedElement !== null && (currentState === States.IDLE || currentState === States.HOP); + } + /** * Set the current animation and reset the animation timer * @param {Anim} animation @@ -2145,6 +2159,12 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI if (state === States.IDLE) { setAnimation(Animations.BOB); } + if (isAbsolute()) { + canvas.classList.add("birb-absolute"); + } else { + canvas.classList.remove("birb-absolute"); + } + setY(birdY); } /** @@ -2159,7 +2179,15 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI * @param {number} y */ function setY(y) { - canvas.style.bottom = `${y}px`; + let bottom; + if (isAbsolute()) { + // Position is absolute, convert from fixed + bottom = y - window.scrollY; + } else { + // Position is fixed + bottom = y; + } + canvas.style.bottom = `${bottom}px`; } }); diff --git a/stylesheet.css b/stylesheet.css index f4de410..504506c 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -21,6 +21,10 @@ cursor: pointer; } +.birb-absolute { + position: absolute !important; +} + .birb-decoration { image-rendering: pixelated; position: fixed;