From 694b86837084a0c0461949c2bb11a8c3df408b1f Mon Sep 17 00:00:00 2001 From: xtzws Date: Sun, 23 Mar 2025 23:17:47 -0400 Subject: [PATCH] Added oneko cat cursor. Copied from @nyancrimew --- maia_oneko.gif | Bin 0 -> 7292 bytes oneko.js | 231 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 maia_oneko.gif create mode 100644 oneko.js diff --git a/maia_oneko.gif b/maia_oneko.gif new file mode 100644 index 0000000000000000000000000000000000000000..794918e80766178f48054ca03e099eba0cd9de6c GIT binary patch literal 7292 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 WGVO`bg))?&$J7%%8M?_30028Qa?s%b literal 0 HcmV?d00001 diff --git a/oneko.js b/oneko.js new file mode 100644 index 0000000..3d115c2 --- /dev/null +++ b/oneko.js @@ -0,0 +1,231 @@ +// based on oneko.js from https://github.com/adryd325/oneko.js, licensed under MIT, with art from https://twitter.com/_Anunnery + +function getRandomInt(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min) + min); +} + +function oneko() { + const nekoEl = document.createElement("div"); + let nekoPosX = getRandomInt(32, window.innerWidth - 63); + let nekoPosY = getRandomInt(32, window.innerHeight - 63); + let mousePosX = nekoPosX - 32; + let mousePosY = nekoPosY - 32; + let mouseButtonDown = false; + let frameCount = 0; + let idleTime = 0; + let idleAnimation = null; + let idleAnimationFrame = 0; + const nekoSpeed = 10; + const spriteSets = { + 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], + ], + heart: [ + [-8, 0], + [-8, -1], + [-8, -2], + [-8, -3], + ], + }; + + function create() { + nekoEl.id = "oneko"; + nekoEl.style.width = "32px"; + nekoEl.style.height = "32px"; + nekoEl.style.position = "fixed"; + nekoEl.style.pointerEvents = "none"; + nekoEl.style.backgroundImage = "url('maia_oneko.gif')"; + nekoEl.style.imageRendering = "pixelated"; + nekoEl.style.left = `${nekoPosX}px`; + nekoEl.style.top = `${nekoPosY}px`; + + document.body.appendChild(nekoEl); + + document.onmousemove = (event) => { + mousePosX = event.clientX; + mousePosY = event.clientY; + }; + document.addEventListener("mousedown", toggleMouseState); + document.addEventListener("mouseup", toggleMouseState); + + window.onekoInterval = setInterval(frame, 100); + } + + function toggleMouseState(e) { + var flags = e.buttons !== undefined ? e.buttons : e.which; + mouseButtonDown = (flags & 1) === 1; + } + + function setSprite(name, frame) { + const sprite = spriteSets[name][frame % spriteSets[name].length]; + nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`; + } + + function resetIdleAnimation() { + idleAnimation = null; + idleAnimationFrame = 0; + } + + function idle(diffX, diffY) { + idleTime += 1; + + // every ~ 20 seconds + if (idleTime > 10 && true && idleAnimation == null) { + let avalibleIdleAnimations = ["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; + } + + // check diffs to ensure pointer is on sprite + if (mouseButtonDown && diffY < 8 && diffY > -18 && diffX < 18 && diffX > -18) { + setSprite("heart", Math.floor(idleAnimationFrame / 4)); + } else { + 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() { + frameCount += 1; + const diffX = nekoPosX - mousePosX; + const diffY = nekoPosY - mousePosY; + const distance = Math.sqrt(diffX ** 2 + diffY ** 2); + + if (distance < nekoSpeed || distance < 48) { + idle(diffX, diffY); + 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; + } + + 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`; + } + + create(); +}; + +const isReduced = window.matchMedia(`(prefers-reduced-motion: reduce)`) === true || window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true; +if (!isReduced) { + oneko(); +}