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