diff options
| author | Elizabeth Hunt <me@liz.coffee> | 2025-12-14 16:39:17 -0800 |
|---|---|---|
| committer | Elizabeth Hunt <me@liz.coffee> | 2025-12-14 16:39:17 -0800 |
| commit | 613632f3a8fccb998147e46d0e751ca4afc66544 (patch) | |
| tree | 609c21fb12b95d54c3cdce4c20d02c152dbd81e2 /src/js/oneko.ts | |
| parent | e033a8fd8ce3fbe92ef87ca662ac6785d4ec9b7b (diff) | |
| download | adelie-613632f3a8fccb998147e46d0e751ca4afc66544.tar.gz adelie-613632f3a8fccb998147e46d0e751ca4afc66544.zip | |
Fixes
Diffstat (limited to 'src/js/oneko.ts')
| -rw-r--r-- | src/js/oneko.ts | 277 |
1 files changed, 0 insertions, 277 deletions
diff --git a/src/js/oneko.ts b/src/js/oneko.ts deleted file mode 100644 index 236c7cb..0000000 --- a/src/js/oneko.ts +++ /dev/null @@ -1,277 +0,0 @@ -// oneko.js: https://github.com/adryd325/oneko.js - -export function initOneko(): void { - const isReducedMotion = - window.matchMedia('(prefers-reduced-motion: reduce)') === true || - window.matchMedia('(prefers-reduced-motion: reduce)').matches === true; - - if (isReducedMotion) return; - - const nekoEl = document.createElement('div'); - let persistPosition = true; - - let nekoPosX = 32; - let nekoPosY = 32; - - let mousePosX = 0; - let mousePosY = 0; - - let frameCount = 0; - let idleTime = 0; - let idleAnimation: string | null = null; - let idleAnimationFrame = 0; - - const nekoSpeed = 10; - const spriteSets: Record<string, [number, number][]> = { - idle: [[-3, -3]], - alert: [[-7, -3]], - scratchSelf: [ - [-5, 0], - [-6, 0], - [-7, 0], - ], - scratchWallN: [ - [0, 0], - [0, -1], - ], - scratchWallS: [ - [-7, -1], - [-6, -2], - ], - scratchWallE: [ - [-2, -2], - [-2, -3], - ], - scratchWallW: [ - [-4, 0], - [-4, -1], - ], - tired: [[-3, -2]], - sleeping: [ - [-2, 0], - [-2, -1], - ], - N: [ - [-1, -2], - [-1, -3], - ], - NE: [ - [0, -2], - [0, -3], - ], - E: [ - [-3, 0], - [-3, -1], - ], - SE: [ - [-5, -1], - [-5, -2], - ], - S: [ - [-6, -3], - [-7, -2], - ], - SW: [ - [-5, -3], - [-6, -1], - ], - W: [ - [-4, -2], - [-4, -3], - ], - NW: [ - [-1, 0], - [-1, -1], - ], - }; - - function init(): void { - const assetBase = window.ASSET_BASE || ''; - let nekoFile = `${assetBase}/oneko/oneko.gif`; - const curScript = document.currentScript as HTMLScriptElement; - if (curScript?.dataset.cat) { - nekoFile = curScript.dataset.cat; - } - if (curScript?.dataset.persistPosition) { - if (curScript.dataset.persistPosition === '') { - persistPosition = true; - } else { - persistPosition = JSON.parse(curScript.dataset.persistPosition.toLowerCase()); - } - } - - if (persistPosition) { - const storedNekoStr = window.localStorage.getItem('oneko'); - const storedNeko = storedNekoStr ? JSON.parse(storedNekoStr) : null; - if (storedNeko !== null) { - nekoPosX = storedNeko.nekoPosX; - nekoPosY = storedNeko.nekoPosY; - mousePosX = storedNeko.mousePosX; - mousePosY = storedNeko.mousePosY; - frameCount = storedNeko.frameCount; - idleTime = storedNeko.idleTime; - idleAnimation = storedNeko.idleAnimation; - idleAnimationFrame = storedNeko.idleAnimationFrame; - nekoEl.style.backgroundPosition = storedNeko.bgPos; - } - } - - nekoEl.id = 'oneko'; - nekoEl.ariaHidden = 'true'; - nekoEl.style.width = '32px'; - nekoEl.style.height = '32px'; - nekoEl.style.position = 'fixed'; - nekoEl.style.pointerEvents = 'none'; - nekoEl.style.imageRendering = 'pixelated'; - nekoEl.style.left = `${nekoPosX - 16}px`; - nekoEl.style.top = `${nekoPosY - 16}px`; - nekoEl.style.zIndex = '2147483647'; - - nekoEl.style.backgroundImage = `url(${nekoFile})`; - - document.body.appendChild(nekoEl); - - document.addEventListener('mousemove', (event: MouseEvent) => { - mousePosX = event.clientX; - mousePosY = event.clientY; - }); - - if (persistPosition) { - window.addEventListener('beforeunload', () => { - window.localStorage.setItem( - 'oneko', - JSON.stringify({ - nekoPosX: nekoPosX, - nekoPosY: nekoPosY, - mousePosX: mousePosX, - mousePosY: mousePosY, - frameCount: frameCount, - idleTime: idleTime, - idleAnimation: idleAnimation, - idleAnimationFrame: idleAnimationFrame, - bgPos: nekoEl.style.backgroundPosition, - }) - ); - }); - } - - window.requestAnimationFrame(onAnimationFrame); - } - - let lastFrameTimestamp: number | undefined; - - function onAnimationFrame(timestamp: number): void { - if (!nekoEl.isConnected) { - return; - } - if (!lastFrameTimestamp) { - lastFrameTimestamp = timestamp; - } - if (lastFrameTimestamp && timestamp - lastFrameTimestamp > 100) { - lastFrameTimestamp = timestamp; - frame(); - } - window.requestAnimationFrame(onAnimationFrame); - } - - function setSprite(name: string, frame: number): void { - const sprite = spriteSets[name][frame % spriteSets[name].length]; - nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`; - } - - function resetIdleAnimation(): void { - idleAnimation = null; - idleAnimationFrame = 0; - } - - function idle(): void { - idleTime += 1; - - // every ~ 20 seconds - if (idleTime > 10 && Math.floor(Math.random() * 200) === 0 && idleAnimation == null) { - const avalibleIdleAnimations: string[] = ['sleeping', 'scratchSelf']; - if (nekoPosX < 32) { - avalibleIdleAnimations.push('scratchWallW'); - } - if (nekoPosY < 32) { - avalibleIdleAnimations.push('scratchWallN'); - } - if (nekoPosX > window.innerWidth - 32) { - avalibleIdleAnimations.push('scratchWallE'); - } - if (nekoPosY > window.innerHeight - 32) { - avalibleIdleAnimations.push('scratchWallS'); - } - idleAnimation = - avalibleIdleAnimations[Math.floor(Math.random() * avalibleIdleAnimations.length)]; - } - - switch (idleAnimation) { - case 'sleeping': - if (idleAnimationFrame < 8) { - setSprite('tired', 0); - break; - } - setSprite('sleeping', Math.floor(idleAnimationFrame / 4)); - if (idleAnimationFrame > 192) { - resetIdleAnimation(); - } - break; - case 'scratchWallN': - case 'scratchWallS': - case 'scratchWallE': - case 'scratchWallW': - case 'scratchSelf': - setSprite(idleAnimation, idleAnimationFrame); - if (idleAnimationFrame > 9) { - resetIdleAnimation(); - } - break; - default: - setSprite('idle', 0); - return; - } - idleAnimationFrame += 1; - } - - function frame(): void { - frameCount += 1; - const diffX = nekoPosX - mousePosX; - const diffY = nekoPosY - mousePosY; - const distance = Math.sqrt(diffX ** 2 + diffY ** 2); - - if (distance < nekoSpeed || distance < 48) { - idle(); - return; - } - - idleAnimation = null; - idleAnimationFrame = 0; - - if (idleTime > 1) { - setSprite('alert', 0); - // count down after being alerted before moving - idleTime = Math.min(idleTime, 7); - idleTime -= 1; - return; - } - - let direction = ''; - direction += diffY / distance > 0.5 ? 'N' : ''; - direction += diffY / distance < -0.5 ? 'S' : ''; - direction += diffX / distance > 0.5 ? 'W' : ''; - direction += diffX / distance < -0.5 ? 'E' : ''; - setSprite(direction, frameCount); - - nekoPosX -= (diffX / distance) * nekoSpeed; - nekoPosY -= (diffY / distance) * nekoSpeed; - - nekoPosX = Math.min(Math.max(16, nekoPosX), window.innerWidth - 16); - nekoPosY = Math.min(Math.max(16, nekoPosY), window.innerHeight - 16); - - nekoEl.style.left = `${nekoPosX - 16}px`; - nekoEl.style.top = `${nekoPosY - 16}px`; - } - - init(); -} |
