diff options
| -rw-r--r-- | static/404.html | 15 | ||||
| -rw-r--r-- | static/js/oneko.js | 165 | ||||
| -rw-r--r-- | static/oneko.gif | bin | 0 -> 3316 bytes |
3 files changed, 172 insertions, 8 deletions
diff --git a/static/404.html b/static/404.html index c9cbca36..f3de5a77 100644 --- a/static/404.html +++ b/static/404.html @@ -2,22 +2,20 @@ <html lang="en" prefix="og: https://ogp.me/ns#"> <head> <meta charset="utf-8"/> - <title>Page not found | GrapheneOS</title> + <title>Page not found | Hakurei</title> <meta name="description" content="The requested page does not exist."/> <meta name="theme-color" content="#212121"/> <meta name="color-scheme" content="dark light"/> <meta name="msapplication-TileColor" content="#ffffff"/> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/> - <meta name="twitter:site" content="@GrapheneOS"/> - <meta name="twitter:creator" content="@GrapheneOS"/> <meta property="og:title" content="Page not found"/> <meta property="og:description" content="The requested page does not exist."/> <meta property="og:type" content="website"/> - <meta property="og:image" content="https://grapheneos.org/opengraph.png"/> + <meta property="og:image" content="https://hakurei.app/opengraph.png"/> <meta property="og:image:width" content="512"/> <meta property="og:image:height" content="512"/> - <meta property="og:image:alt" content="GrapheneOS logo"/> - <meta property="og:site_name" content="GrapheneOS"/> + <meta property="og:image:alt" content="Hakurei logo"/> + <meta property="og:site_name" content="Hakurei"/> <link rel="icon" href="/favicon.ico"/> <link rel="icon" sizes="any" type="image/svg+xml" href="/favicon.svg"/> <link rel="mask-icon" href="[[path|/mask-icon.svg]]" color="#1a1a1a"/> @@ -25,7 +23,8 @@ [[css|/main.css]] <link rel="manifest" href="/manifest.webmanifest"/> <link rel="license" href="/LICENSE.txt"/> - <link rel="me" href="https://grapheneos.social/@GrapheneOS"/> + <link rel="me" href="https://port.mk/@hakurei"/> + [[js|/js/oneko.js]] </head> <body> {% include "header.html" %} @@ -33,7 +32,7 @@ <h1><a href="#page-not-found">Page not found</a></h1> <p>The requested page does not exist. If you think this is a mistake, please - <a href="https://github.com/GrapheneOS/grapheneos.org/issues">report an issue</a>.</p> + <a href="https://git.gensokyo.uk/security/hakurei.app/issues">report an issue</a>.</p> </main> {% include "footer.html" %} </body> diff --git a/static/js/oneko.js b/static/js/oneko.js new file mode 100644 index 00000000..2f01bbcf --- /dev/null +++ b/static/js/oneko.js @@ -0,0 +1,165 @@ +(function oneko() { + const nekoEl = document.createElement("div"); + let nekoPosX = 32; + let nekoPosY = 32; + let mousePosX = 0; + let mousePosY = 0; + let frameCount = 0; + let idleTime = 0; + let idleAnimation = null; + let idleAnimationFrame = 0; + const nekoSpeed = 10; + const spriteSets = { + idle: [[-3, -3]], + alert: [[-7, -3]], + scratch: [ + [-5, 0], + [-6, 0], + [-7, 0], + ], + 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 create() { + nekoEl.id = "oneko"; + nekoEl.style.width = "32px"; + nekoEl.style.height = "32px"; + nekoEl.style.position = "fixed"; + nekoEl.style.pointerEvents = "none"; + nekoEl.style.backgroundImage = "url('/oneko.gif')"; + nekoEl.style.imageRendering = "pixelated"; + nekoEl.style.left = "16px"; + nekoEl.style.top = "16px"; + + document.body.appendChild(nekoEl); + + document.onmousemove = (event) => { + mousePosX = event.clientX; + mousePosY = event.clientY; + }; + + window.onekoInterval = setInterval(frame, 100); + } + + 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() { + idleTime += 1; + + // every ~ 20 seconds + if ( + idleTime > 10 && + Math.floor(Math.random() * 200) == 0 && + idleAnimation == null + ) { + idleAnimation = ["sleeping", "scratch"][ + Math.floor(Math.random() * 2) + ]; + } + + switch (idleAnimation) { + case "sleeping": + if (idleAnimationFrame < 8) { + setSprite("tired", 0); + break; + } + setSprite("sleeping", Math.floor(idleAnimationFrame / 4)); + if (idleAnimationFrame > 192) { + resetIdleAnimation(); + } + break; + case "scratch": + setSprite("scratch", 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(); + 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 = 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; + + nekoEl.style.left = `${nekoPosX - 16}px`; + nekoEl.style.top = `${nekoPosY - 16}px`; + } + + create(); +})(); diff --git a/static/oneko.gif b/static/oneko.gif Binary files differnew file mode 100644 index 00000000..a009c2cc --- /dev/null +++ b/static/oneko.gif |
