From bb810584eef2423874c8710eb2cd73b21696598a Mon Sep 17 00:00:00 2001 From: Frank Tovar Date: Tue, 3 Feb 2026 01:15:33 +0100 Subject: [PATCH] Add smooth parallax background pan --- wwwroot/app.js | 39 ++++++++++++++++++++++++++++++++++++++- wwwroot/styles.css | 12 +++++++++--- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/wwwroot/app.js b/wwwroot/app.js index 290da77..c675209 100644 --- a/wwwroot/app.js +++ b/wwwroot/app.js @@ -8,6 +8,7 @@ import { } from "./js/i18n.js"; initI18n(); +setupBackgroundPan(); const state = { isAuthenticated: false, @@ -836,6 +837,42 @@ function setupCardVisualHover(el, url) { ["mouseleave", "blur"].forEach((evt) => el.addEventListener(evt, reset)); } +function setupBackgroundPan(config = {}) { + const root = document.documentElement; + const maxOffset = config.maxOffsetPx ?? 30; + const ease = config.ease ?? 0.08; + if (config.scalePercent) { + root.style.setProperty("--bg-scale", config.scalePercent); + } + let targetX = 0; + let targetY = 0; + let currX = 0; + let currY = 0; + + const setTarget = (x, y) => { + targetX = x; + targetY = y; + }; + + const step = () => { + currX += (targetX - currX) * ease; + currY += (targetY - currY) * ease; + root.style.setProperty("--bg-x", `${currX}px`); + root.style.setProperty("--bg-y", `${currY}px`); + requestAnimationFrame(step); + }; + + window.addEventListener("mousemove", (e) => { + const nx = (e.clientX / window.innerWidth - 0.5) * 2; + const ny = (e.clientY / window.innerHeight - 0.5) * 2; + setTarget(nx * maxOffset, ny * maxOffset); + }); + + window.addEventListener("mouseleave", () => setTarget(0, 0)); + window.addEventListener("blur", () => setTarget(0, 0)); + step(); +} + async function main() { setupHandlers(); try { @@ -897,7 +934,7 @@ function scoreToEmoji(score) { } function neutralEmoji() { - return "⬅️"; + return "😐"; } function signatureSuggestions(list) { diff --git a/wwwroot/styles.css b/wwwroot/styles.css index 9a14df8..22d7899 100644 --- a/wwwroot/styles.css +++ b/wwwroot/styles.css @@ -8,6 +8,9 @@ sans-serif; background: #f6e9d6; color: #2c1c0d; + --bg-scale: 115%; + --bg-x: 0px; + --bg-y: 0px; } *, @@ -24,9 +27,12 @@ align-items: center; gap: 16px; min-height: 100vh; - background: - url("background.png") center/cover no-repeat fixed, - #f6e9d6; + background-color: #f6e9d6; + background-image: url("background.png"); + background-repeat: no-repeat; + background-attachment: fixed; + background-size: var(--bg-scale) var(--bg-scale); + background-position: calc(50% + var(--bg-x)) calc(50% + var(--bg-y)); } .lang-field {