Skip to content
Snippets Groups Projects
Select Git revision
  • 7b14f0ea0c05b4d0370bc64b570d97c3b9a8cc5a
  • master default protected
  • 1.3.1
  • 1.3.0
  • 1.2.0
  • 1.1.5
  • 1.1.4
  • 1.1.3
  • 1.1.2
  • 1.1.1
  • 1.1.0
  • 1.0.19
  • 1.0.18
  • 1.0.17
  • 1.0.16
  • 1.0.15
  • 1.0.14
  • 1.0.13
  • 1.0.12
  • 1.0.10
  • 1.0.9
  • 1.0.8
22 results

NoteReducer.js

Blame
  • Forked from KSZK / DevTeam / kszkepzes / old / kszkepzes-frontend
    Source project has a limited visibility.
    neko.js 5.93 KiB
    // Based on oneko.js: https://github.com/adryd325/oneko.js
    
    (function oneko() {
      const isReducedMotion =
        window.matchMedia(`(prefers-reduced-motion: reduce)`) === true ||
        window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
    
      let movementEnabled = false;
    
      const nekoEl = document.createElement("div");
    
      let nekoPosX = window.innerWidth - 32;
      let nekoPosY = window.innerHeight - 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]],
        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 onClickNeko() {
        if (isReducedMotion) {
          // Maybe show a message?
          alert("Your browser is set to avoid unneeded motion. So I will behave and not chase your mouse.");
        } else {
          movementEnabled = true;
          nekoEl.style.cursor = "default";
          nekoEl.title = "";
        }
      }
    
      function init() {
        nekoEl.id = "oneko";
        nekoEl.ariaHidden = true;
        nekoEl.style.width = "32px";
        nekoEl.style.height = "32px";
        nekoEl.style.position = "fixed";
        nekoEl.style.imageRendering = "pixelated";
        nekoEl.style.left = `${nekoPosX - 16}px`;
        nekoEl.style.top = `${nekoPosY - 16}px`;
        nekoEl.style.cursor = "pointer";
        nekoEl.style.zIndex = 2147483647;
        nekoEl.title = "Play with me!";
    
        let nekoFile = "/static/neko/oneko.gif"
        const curScript = document.currentScript
        if (curScript && curScript.dataset.cat) {
          nekoFile = curScript.dataset.cat
        }
        nekoEl.style.backgroundImage = `url(${nekoFile})`;
    
        document.body.appendChild(nekoEl);
        nekoEl.addEventListener("click", onClickNeko);
    
        document.addEventListener("mousemove", function (event) {
          mousePosX = event.clientX;
          mousePosY = event.clientY;
        });
        window.addEventListener("resize", function () {
          if (nekoPosX > window.innerWidth) {
            nekoPosX = window.innerWidth - 32;
          }
          if (nekoPosY > window.innerHeight) {
            nekoPosY = window.innerHeight - 32;
          }
          nekoEl.style.left = `${nekoPosX - 16}px`;
          nekoEl.style.top = `${nekoPosY - 16}px`;
        });
    
        window.requestAnimationFrame(onAnimationFrame);
      }
    
      let lastFrameTimestamp;
    
      function onAnimationFrame(timestamp) {
        // Stops execution if the neko element is removed from DOM
        if (!nekoEl.isConnected) {
          return;
        }
        if (!lastFrameTimestamp) {
          lastFrameTimestamp = timestamp;
        }
        if (timestamp - lastFrameTimestamp > 100) {
          lastFrameTimestamp = timestamp
          frame()
        }
        window.requestAnimationFrame(onAnimationFrame);
      }
    
      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 ~ 10 seconds
        if (
          idleTime > 10 &&
          Math.floor(Math.random() * 100) == 0 &&
          idleAnimation == null
        ) {
          let avalibleIdleAnimations = ["sleeping", "scratchSelf", "scratchWallW", "scratchWallN", "scratchWallE", "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;
        }
        if (idleAnimation != null) idleAnimationFrame += 1;
      }
    
      function frame() {
        frameCount += 1;
        const diffX = nekoPosX - mousePosX;
        const diffY = nekoPosY - mousePosY;
        const distance = Math.sqrt(diffX ** 2 + diffY ** 2);
    
        if (isReducedMotion || !movementEnabled || 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();
    })();