6-oneko.js - bitreich-www - the bitreich www website generator
 (HTM) git clone git://bitreich.org/bitreich-www/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/bitreich-www/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
       ---
       6-oneko.js (5243B)
       ---
            1 // Stolen from https://outerproduct.net/oneko.js
            2 // based on maia's oneko.js https://maia.crimew.gay/
            3 // based on oneko.js from https://github.com/adryd325/oneko.js, licensed under MIT, with art from https://twitter.com/_Anunnery
            4 
            5 function getRandomInt(min, max) {
            6   min = Math.ceil(min);
            7   max = Math.floor(max);
            8   return Math.floor(Math.random() * (max - min) + min);
            9 }
           10 
           11 function oneko() {
           12   const nekoEl = document.createElement("div");
           13   let nekoPosX = getRandomInt(32, window.innerWidth - 63);
           14   let nekoPosY = getRandomInt(32, window.innerHeight - 63);
           15   let mousePosX = nekoPosX - 32;
           16   let mousePosY = nekoPosY - 32;
           17   let frameCount = 0;
           18   let idleTime = 0;
           19   let idleAnimation = null;
           20   let idleAnimationFrame = 0;
           21   const nekoSpeed = 10;
           22   const spriteSets = {
           23     idle: [[-3, -3]],
           24     alert: [[-7, -3]],
           25     scratchSelf: [
           26       [-5, 0],
           27       [-6, 0],
           28       [-7, 0],
           29     ],
           30     scratchWallN: [
           31       [0, 0],
           32       [0, -1],
           33     ],
           34     scratchWallS: [
           35       [-7, -1],
           36       [-6, -2],
           37     ],
           38     scratchWallE: [
           39       [-2, -2],
           40       [-2, -3],
           41     ],
           42     scratchWallW: [
           43       [-4, 0],
           44       [-4, -1],
           45     ],
           46     tired: [[-3, -2]],
           47     sleeping: [
           48       [-2, 0],
           49       [-2, -1],
           50     ],
           51     N: [
           52       [-1, -2],
           53       [-1, -3],
           54     ],
           55     NE: [
           56       [0, -2],
           57       [0, -3],
           58     ],
           59     E: [
           60       [-3, 0],
           61       [-3, -1],
           62     ],
           63     SE: [
           64       [-5, -1],
           65       [-5, -2],
           66     ],
           67     S: [
           68       [-6, -3],
           69       [-7, -2],
           70     ],
           71     SW: [
           72       [-5, -3],
           73       [-6, -1],
           74     ],
           75     W: [
           76       [-4, -2],
           77       [-4, -3],
           78     ],
           79     NW: [
           80       [-1, 0],
           81       [-1, -1],
           82     ],
           83   };
           84 
           85   function create() {
           86     nekoEl.id = "oneko";
           87     nekoEl.style.width = "32px";
           88     nekoEl.style.height = "32px";
           89     nekoEl.style.position = "fixed";
           90     nekoEl.style.pointerEvents = "none";
           91     nekoEl.style.backgroundImage = "url('/s/neko.png')";
           92     nekoEl.style.imageRendering = "pixelated";
           93     nekoEl.style.left = `${nekoPosX}px`;
           94     nekoEl.style.top = `${nekoPosY}px`;
           95     nekoEl.style.zIndex = 9999999999; //Number.MAX_SAFE_INTEGER
           96 
           97     document.body.appendChild(nekoEl);
           98 
           99     document.onmousemove = (event) => {
          100       mousePosX = event.clientX;
          101       mousePosY = event.clientY;
          102     };
          103 
          104     window.onekoInterval = setInterval(frame, 100);
          105   }
          106 
          107   function setSprite(name, frame) {
          108     const sprite = spriteSets[name][frame % spriteSets[name].length];
          109     nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`;
          110   }
          111 
          112   function resetIdleAnimation() {
          113     idleAnimation = null;
          114     idleAnimationFrame = 0;
          115   }
          116 
          117   function idle() {
          118     idleTime += 1;
          119 
          120     // every ~ 20 seconds
          121     if (idleTime > 10 && true && idleAnimation == null) {
          122       let avalibleIdleAnimations = ["sleeping", "scratchSelf"];
          123       if (nekoPosX < 32) {
          124         avalibleIdleAnimations.push("scratchWallW");
          125       }
          126       if (nekoPosY < 32) {
          127         avalibleIdleAnimations.push("scratchWallN");
          128       }
          129       if (nekoPosX > window.innerWidth - 32) {
          130         avalibleIdleAnimations.push("scratchWallE");
          131       }
          132       if (nekoPosY > window.innerHeight - 32) {
          133         avalibleIdleAnimations.push("scratchWallS");
          134       }
          135       idleAnimation =
          136         avalibleIdleAnimations[
          137         Math.floor(Math.random() * avalibleIdleAnimations.length)
          138         ];
          139     }
          140 
          141     switch (idleAnimation) {
          142       case "sleeping":
          143         if (idleAnimationFrame < 8) {
          144           setSprite("tired", 0);
          145           break;
          146         }
          147         setSprite("sleeping", Math.floor(idleAnimationFrame / 4));
          148         if (idleAnimationFrame > 192) {
          149           resetIdleAnimation();
          150         }
          151         break;
          152       case "scratchWallN":
          153       case "scratchWallS":
          154       case "scratchWallE":
          155       case "scratchWallW":
          156       case "scratchSelf":
          157         setSprite(idleAnimation, idleAnimationFrame);
          158         if (idleAnimationFrame > 9) {
          159           resetIdleAnimation();
          160         }
          161         break;
          162       default:
          163         setSprite("idle", 0);
          164         return;
          165     }
          166     idleAnimationFrame += 1;
          167   }
          168 
          169   function frame() {
          170     frameCount += 1;
          171     const diffX = nekoPosX - mousePosX;
          172     const diffY = nekoPosY - mousePosY;
          173     const distance = Math.sqrt(diffX ** 2 + diffY ** 2);
          174 
          175     if (distance < nekoSpeed || distance < 48) {
          176       idle();
          177       return;
          178     }
          179 
          180     idleAnimation = null;
          181     idleAnimationFrame = 0;
          182 
          183     if (idleTime > 1) {
          184       setSprite("alert", 0);
          185       // count down after being alerted before moving
          186       idleTime = Math.min(idleTime, 7);
          187       idleTime -= 1;
          188       return;
          189     }
          190 
          191     direction = diffY / distance > 0.5 ? "N" : "";
          192     direction += diffY / distance < -0.5 ? "S" : "";
          193     direction += diffX / distance > 0.5 ? "W" : "";
          194     direction += diffX / distance < -0.5 ? "E" : "";
          195     setSprite(direction, frameCount);
          196 
          197     nekoPosX -= (diffX / distance) * nekoSpeed;
          198     nekoPosY -= (diffY / distance) * nekoSpeed;
          199 
          200     nekoPosX = Math.min(Math.max(16, nekoPosX), window.innerWidth - 16);
          201     nekoPosY = Math.min(Math.max(16, nekoPosY), window.innerHeight - 16);
          202 
          203     nekoEl.style.left = `${nekoPosX - 16}px`;
          204     nekoEl.style.top = `${nekoPosY - 16}px`;
          205   }
          206 
          207   create();
          208 };
          209 
          210 const isReduced = window.matchMedia(`(prefers-reduced-motion: reduce)`) === true || window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
          211 if (!isReduced) {
          212   oneko();
          213 }
          214