oneko: honor prefers reduced motion
This commit is contained in:
		
							parent
							
								
									584aa15038
								
							
						
					
					
						commit
						454b6eaaf7
					
				
					 1 changed files with 197 additions and 192 deletions
				
			
		|  | @ -1,199 +1,204 @@ | ||||||
| // based on oneko.js from https://github.com/adryd325/oneko.js, licensed under MIT, with art from https://twitter.com/_Anunnery
 | // based on oneko.js from https://github.com/adryd325/oneko.js, licensed under MIT, with art from https://twitter.com/_Anunnery
 | ||||||
| 
 | 
 | ||||||
| (function oneko() { | function oneko() { | ||||||
|     const nekoEl = document.createElement("div"); |   const nekoEl = document.createElement("div"); | ||||||
|     let nekoPosX = 32; |   let nekoPosX = 32; | ||||||
|     let nekoPosY = 32; |   let nekoPosY = 32; | ||||||
|     let mousePosX = 0; |   let mousePosX = 0; | ||||||
|     let mousePosY = 0; |   let mousePosY = 0; | ||||||
|     let frameCount = 0; |   let frameCount = 0; | ||||||
|     let idleTime = 0; |   let idleTime = 0; | ||||||
|     let idleAnimation = null; |   let idleAnimation = null; | ||||||
|     let idleAnimationFrame = 0; |   let idleAnimationFrame = 0; | ||||||
|     const nekoSpeed = 10; |   const nekoSpeed = 10; | ||||||
|     const spriteSets = { |   const spriteSets = { | ||||||
|       idle: [[-3, -3]], |     idle: [[-3, -3]], | ||||||
|       alert: [[-7, -3]], |     alert: [[-7, -3]], | ||||||
|       scratchSelf: [ |     scratchSelf: [ | ||||||
|         [-5, 0], |       [-5, 0], | ||||||
|         [-6, 0], |       [-6, 0], | ||||||
|         [-7, 0], |       [-7, 0], | ||||||
|       ], |     ], | ||||||
|       scratchWallN: [ |     scratchWallN: [ | ||||||
|         [0, 0], |       [0, 0], | ||||||
|         [0, -1], |       [0, -1], | ||||||
|       ], |     ], | ||||||
|       scratchWallS: [ |     scratchWallS: [ | ||||||
|         [-7, -1], |       [-7, -1], | ||||||
|         [-6, -2], |       [-6, -2], | ||||||
|       ], |     ], | ||||||
|       scratchWallE: [ |     scratchWallE: [ | ||||||
|         [-2, -2], |       [-2, -2], | ||||||
|         [-2, -3], |       [-2, -3], | ||||||
|       ], |     ], | ||||||
|       scratchWallW: [ |     scratchWallW: [ | ||||||
|         [-4, 0], |       [-4, 0], | ||||||
|         [-4, -1], |       [-4, -1], | ||||||
|       ], |     ], | ||||||
|       tired: [[-3, -2]], |     tired: [[-3, -2]], | ||||||
|       sleeping: [ |     sleeping: [ | ||||||
|         [-2, 0], |       [-2, 0], | ||||||
|         [-2, -1], |       [-2, -1], | ||||||
|       ], |     ], | ||||||
|       N: [ |     N: [ | ||||||
|         [-1, -2], |       [-1, -2], | ||||||
|         [-1, -3], |       [-1, -3], | ||||||
|       ], |     ], | ||||||
|       NE: [ |     NE: [ | ||||||
|         [0, -2], |       [0, -2], | ||||||
|         [0, -3], |       [0, -3], | ||||||
|       ], |     ], | ||||||
|       E: [ |     E: [ | ||||||
|         [-3, 0], |       [-3, 0], | ||||||
|         [-3, -1], |       [-3, -1], | ||||||
|       ], |     ], | ||||||
|       SE: [ |     SE: [ | ||||||
|         [-5, -1], |       [-5, -1], | ||||||
|         [-5, -2], |       [-5, -2], | ||||||
|       ], |     ], | ||||||
|       S: [ |     S: [ | ||||||
|         [-6, -3], |       [-6, -3], | ||||||
|         [-7, -2], |       [-7, -2], | ||||||
|       ], |     ], | ||||||
|       SW: [ |     SW: [ | ||||||
|         [-5, -3], |       [-5, -3], | ||||||
|         [-6, -1], |       [-6, -1], | ||||||
|       ], |     ], | ||||||
|       W: [ |     W: [ | ||||||
|         [-4, -2], |       [-4, -2], | ||||||
|         [-4, -3], |       [-4, -3], | ||||||
|       ], |     ], | ||||||
|       NW: [ |     NW: [ | ||||||
|         [-1, 0], |       [-1, 0], | ||||||
|         [-1, -1], |       [-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('/img/maia_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; | ||||||
|     }; |     }; | ||||||
|    | 
 | ||||||
|     function create() { |     window.onekoInterval = setInterval(frame, 100); | ||||||
|       nekoEl.id = "oneko"; |   } | ||||||
|       nekoEl.style.width = "32px"; | 
 | ||||||
|       nekoEl.style.height = "32px"; |   function setSprite(name, frame) { | ||||||
|       nekoEl.style.position = "fixed"; |     const sprite = spriteSets[name][frame % spriteSets[name].length]; | ||||||
|       nekoEl.style.pointerEvents = "none"; |     nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`; | ||||||
|       nekoEl.style.backgroundImage = "url('/img/maia_oneko.gif')"; |   } | ||||||
|       nekoEl.style.imageRendering = "pixelated"; | 
 | ||||||
|       nekoEl.style.left = "16px"; |   function resetIdleAnimation() { | ||||||
|       nekoEl.style.top = "16px"; |     idleAnimation = null; | ||||||
|    |     idleAnimationFrame = 0; | ||||||
|       document.body.appendChild(nekoEl); |   } | ||||||
|    | 
 | ||||||
|       document.onmousemove = (event) => { |   function idle() { | ||||||
|         mousePosX = event.clientX; |     idleTime += 1; | ||||||
|         mousePosY = event.clientY; | 
 | ||||||
|       }; |     // every ~ 20 seconds
 | ||||||
|    |     if (idleTime > 10 && true && idleAnimation == null) { | ||||||
|       window.onekoInterval = setInterval(frame, 100); |       let avalibleIdleAnimations = ["sleeping", "scratchSelf"]; | ||||||
|     } |       if (nekoPosX < 32) { | ||||||
|    |         avalibleIdleAnimations.push("scratchWallW"); | ||||||
|     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 && true && idleAnimation == null) { |  | ||||||
|         let avalibleIdleAnimations = ["sleeping", "scratchSelf"]; |  | ||||||
|         if (nekoPosX < 32) { |  | ||||||
|           avalibleIdleAnimations.push("scratchWallW"); |  | ||||||
|         } |  | ||||||
|         if (nekoPosY < 32) { |  | ||||||
|           avalibleIdleAnimations.push("scratchWallN"); |  | ||||||
|         } |  | ||||||
|         if (nekoPosX > window.innerWidth - 32) { |  | ||||||
|           avalibleIdleAnimations.push("scratchWallE"); |  | ||||||
|         } |  | ||||||
|         if (nekoPosY > window.innerHeight - 32) { |  | ||||||
|           avalibleIdleAnimations.push("scratchWallS"); |  | ||||||
|         } |  | ||||||
|         idleAnimation = |  | ||||||
|           avalibleIdleAnimations[ |  | ||||||
|             Math.floor(Math.random() * avalibleIdleAnimations.length) |  | ||||||
|           ]; |  | ||||||
|       } |       } | ||||||
|    |       if (nekoPosY < 32) { | ||||||
|       switch (idleAnimation) { |         avalibleIdleAnimations.push("scratchWallN"); | ||||||
|         case "sleeping": |       } | ||||||
|           if (idleAnimationFrame < 8) { |       if (nekoPosX > window.innerWidth - 32) { | ||||||
|             setSprite("tired", 0); |         avalibleIdleAnimations.push("scratchWallE"); | ||||||
|             break; |       } | ||||||
|           } |       if (nekoPosY > window.innerHeight - 32) { | ||||||
|           setSprite("sleeping", Math.floor(idleAnimationFrame / 4)); |         avalibleIdleAnimations.push("scratchWallS"); | ||||||
|           if (idleAnimationFrame > 192) { |       } | ||||||
|             resetIdleAnimation(); |       idleAnimation = | ||||||
|           } |         avalibleIdleAnimations[ | ||||||
|  |         Math.floor(Math.random() * avalibleIdleAnimations.length) | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch (idleAnimation) { | ||||||
|  |       case "sleeping": | ||||||
|  |         if (idleAnimationFrame < 8) { | ||||||
|  |           setSprite("tired", 0); | ||||||
|           break; |           break; | ||||||
|         case "scratchWallN": |         } | ||||||
|         case "scratchWallS": |         setSprite("sleeping", Math.floor(idleAnimationFrame / 4)); | ||||||
|         case "scratchWallE": |         if (idleAnimationFrame > 192) { | ||||||
|         case "scratchWallW": |           resetIdleAnimation(); | ||||||
|         case "scratchSelf": |         } | ||||||
|           setSprite(idleAnimation, idleAnimationFrame); |         break; | ||||||
|           if (idleAnimationFrame > 9) { |       case "scratchWallN": | ||||||
|             resetIdleAnimation(); |       case "scratchWallS": | ||||||
|           } |       case "scratchWallE": | ||||||
|           break; |       case "scratchWallW": | ||||||
|         default: |       case "scratchSelf": | ||||||
|           setSprite("idle", 0); |         setSprite(idleAnimation, idleAnimationFrame); | ||||||
|           return; |         if (idleAnimationFrame > 9) { | ||||||
|       } |           resetIdleAnimation(); | ||||||
|       idleAnimationFrame += 1; |         } | ||||||
|     } |         break; | ||||||
|    |       default: | ||||||
|     function frame() { |         setSprite("idle", 0); | ||||||
|       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; |         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; |  | ||||||
|       } |  | ||||||
|    |  | ||||||
|       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`; |  | ||||||
|     } |     } | ||||||
|    |     idleAnimationFrame += 1; | ||||||
|     create(); |   } | ||||||
|   })(); | 
 | ||||||
|  |   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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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`; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   create(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const isReduced = window.matchMedia(`(prefers-reduced-motion: reduce)`) === true || window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true; | ||||||
|  | if (!isReduced) { | ||||||
|  |   oneko(); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue