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
 | ||||
| 
 | ||||
| (function oneko() { | ||||
|     const nekoEl = document.createElement("div"); | ||||
|     let nekoPosX = 32; | ||||
|     let nekoPosY = 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 oneko() { | ||||
|   const nekoEl = document.createElement("div"); | ||||
|   let nekoPosX = 32; | ||||
|   let nekoPosY = 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 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() { | ||||
|       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"; | ||||
|     window.onekoInterval = setInterval(frame, 100); | ||||
|   } | ||||
| 
 | ||||
|       document.body.appendChild(nekoEl); | ||||
|   function setSprite(name, frame) { | ||||
|     const sprite = spriteSets[name][frame % spriteSets[name].length]; | ||||
|     nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`; | ||||
|   } | ||||
| 
 | ||||
|       document.onmousemove = (event) => { | ||||
|         mousePosX = event.clientX; | ||||
|         mousePosY = event.clientY; | ||||
|       }; | ||||
|   function resetIdleAnimation() { | ||||
|     idleAnimation = null; | ||||
|     idleAnimationFrame = 0; | ||||
|   } | ||||
| 
 | ||||
|       window.onekoInterval = setInterval(frame, 100); | ||||
|     } | ||||
|   function idle() { | ||||
|     idleTime += 1; | ||||
| 
 | ||||
|     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) | ||||
|           ]; | ||||
|     // 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) | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|       switch (idleAnimation) { | ||||
|         case "sleeping": | ||||
|           if (idleAnimationFrame < 8) { | ||||
|             setSprite("tired", 0); | ||||
|             break; | ||||
|           } | ||||
|           setSprite("sleeping", Math.floor(idleAnimationFrame / 4)); | ||||
|           if (idleAnimationFrame > 192) { | ||||
|             resetIdleAnimation(); | ||||
|           } | ||||
|     switch (idleAnimation) { | ||||
|       case "sleeping": | ||||
|         if (idleAnimationFrame < 8) { | ||||
|           setSprite("tired", 0); | ||||
|           break; | ||||
|         case "scratchWallN": | ||||
|         case "scratchWallS": | ||||
|         case "scratchWallE": | ||||
|         case "scratchWallW": | ||||
|         case "scratchSelf": | ||||
|           setSprite(idleAnimation, idleAnimationFrame); | ||||
|           if (idleAnimationFrame > 9) { | ||||
|             resetIdleAnimation(); | ||||
|           } | ||||
|           break; | ||||
|         default: | ||||
|           setSprite("idle", 0); | ||||
|           return; | ||||
|       } | ||||
|       idleAnimationFrame += 1; | ||||
|         } | ||||
|         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; | ||||
|     } | ||||
|     idleAnimationFrame += 1; | ||||
|   } | ||||
| 
 | ||||
|   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; | ||||
|     } | ||||
| 
 | ||||
|     function frame() { | ||||
|       frameCount += 1; | ||||
|       const diffX = nekoPosX - mousePosX; | ||||
|       const diffY = nekoPosY - mousePosY; | ||||
|       const distance = Math.sqrt(diffX ** 2 + diffY ** 2); | ||||
|     idleAnimation = null; | ||||
|     idleAnimationFrame = 0; | ||||
| 
 | ||||
|       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`; | ||||
|     if (idleTime > 1) { | ||||
|       setSprite("alert", 0); | ||||
|       // count down after being alerted before moving
 | ||||
|       idleTime = Math.min(idleTime, 7); | ||||
|       idleTime -= 1; | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     create(); | ||||
|   })(); | ||||
|     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