NumberSelector.svelte (2882B)
1 <script lang="ts"> 2 import { clamp } from '$lib/types'; 3 export let value: number; 4 export let updateValue: (n: number) => void; 5 6 let pos: 7 | { 8 start: { x: number; y: number }; 9 delta: { x: number; y: number }; 10 mouse: { x: number; y: number }; 11 } 12 | undefined; 13 let el: HTMLDivElement | undefined; 14 15 const handleMove = (evt: MouseEvent) => { 16 if (!pos) return; 17 pos.delta.x += evt.movementX; 18 //pos.delta.y += evt.movementY; 19 let mouseX = (pos.start.x + pos.delta.x / 2) % window.innerWidth; 20 21 if (mouseX < 0) { 22 mouseX += window.innerWidth; 23 } 24 pos.mouse.x = mouseX; 25 26 //let mouseY = (pos.start.y + pos.delta.y) % window.innerHeight; 27 //if (mouseY < 0) { 28 // mouseY += window.innerHeight; 29 //} 30 //pos.mouse.y = mouseY; 31 }; 32 33 type Ticker = { start: () => void; stop: () => void }; 34 const Ticker = (): Ticker => { 35 let c = 0; 36 let tDelta: number | undefined; 37 let cont = true; 38 const run = (ts: number) => { 39 if (!cont || !pos || !pos.delta) { 40 return; 41 } 42 if (tDelta === undefined) { 43 tDelta = ts; 44 } else { 45 tDelta = ts - tDelta; 46 const rate = Math.pow(pos.delta.x / 200, 3); 47 c = c + rate * tDelta; 48 if (Math.abs(c) > 10000) { 49 let n = Math.floor(c / 10000); 50 c -= n * 10000; 51 const truncValue = clamp(value + n, { min: -500_000, max: 500_000 }); 52 updateValue(truncValue); 53 } 54 } 55 requestAnimationFrame(run); 56 }; 57 const stop = () => { 58 cont = false; 59 }; 60 const start = () => { 61 cont = true; 62 requestAnimationFrame(run); 63 }; 64 return { start, stop }; 65 }; 66 67 let ticker: Ticker | undefined; 68 69 const handleMouseDown = async (evt: MouseEvent) => { 70 //@ts-ignore TODO figure out if this is true cuz mdn says to do this. 71 await el?.requestPointerLock(); 72 pos = { 73 start: { 74 x: evt.pageX, 75 y: evt.pageY 76 }, 77 delta: { 78 x: 0, 79 y: 0 80 }, 81 mouse: { 82 x: evt.pageX, 83 y: evt.pageY 84 } 85 }; 86 ticker = Ticker(); 87 ticker.start(); 88 document.addEventListener('mousemove', handleMove); 89 document.addEventListener('mouseup', handleMouseUp); 90 }; 91 92 const handleMouseUp = () => { 93 ticker?.stop(); 94 ticker = undefined; 95 pos = undefined; 96 document.exitPointerLock(); 97 document.removeEventListener('mousemove', handleMove); 98 document.removeEventListener('mouseup', handleMouseUp); 99 }; 100 101 const handleDbl = () => { 102 updateValue(0); 103 }; 104 105 $: if (el) { 106 el.removeEventListener('dblclick', handleDbl); 107 el.addEventListener('dblclick', handleDbl); 108 el.removeEventListener('mousedown', handleMouseDown); 109 //el.addEventListener('mousedown', handleMouseDown); 110 } 111 </script> 112 113 <div bind:this={el} class="sel">{value} {pos?.delta.x}</div> 114 {#if pos} 115 <div class="tmp-pointer" style={`top:${pos.mouse.y}px; left:${pos.mouse.x}px`} /> 116 {/if} 117 118 <style> 119 .sel { 120 font-family: monospace; 121 } 122 .tmp-pointer { 123 position: fixed; 124 height: 10px; 125 width: 10px; 126 background: red; 127 } 128 </style>