6-oneko.js - bitreich-www - the bitreich www website generator | |
git clone git://bitreich.org/bitreich-www/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hh… | |
Log | |
Files | |
Refs | |
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… | |
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] *… | |
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)`) … | |
211 if (!isReduced) { | |
212 oneko(); | |
213 } | |
214 |