diff --git a/www/cursor-thing.js b/www/cursor-thing.js new file mode 100644 index 0000000..c1f1b38 --- /dev/null +++ b/www/cursor-thing.js @@ -0,0 +1,183 @@ + +const thing = document.getElementById('cursor-thing'); +let updateTimerId; + +let lastUpdate = 0; + +// targeting +let mouseX = 0; +let mouseY = 0; +let scrollX = document.scrollingElement.scrollLeft; +let scrollY = document.scrollingElement.scrollTop; +const getTargetX = () => mouseX + scrollX; +const getTargetY = () => mouseY + scrollY; + +// physics +let xPos = 0; +let yPos = 0; +let angle = 0; +let velocity = 0; +const maxVel = 600; +let accel = 0; +const maxAccel = 1500; +const accelCurve = (x) => maxAccel*(x/200)**3; // maxAccel when >150px distance + +// more physics +const slideFric = .9; // coefficient of friction +const turnSpeedCoeff = .08; // coefficient of turning + +// other stuff +let brake = false; +const brakeDist = 70; + +// animation +let alt = false; +let lastAlt = 0; + + +function update() { + // timing + thisUpdate = Date.now(); + deltaT = (thisUpdate - lastUpdate)/1000.0; + lastUpdate = thisUpdate; + + // update trajectory + targetX = getTargetX(); + targetY = getTargetY(); + + xDelta = targetX - xPos; + yDelta = targetY - yPos; + + // find target angle + let targetAngle = Math.atan(yDelta/xDelta); + if (isNaN(targetAngle)) targetAngle = 0; + if (xDelta < 0) targetAngle = Math.PI*.5 - (targetAngle*-1); + else targetAngle = -((targetAngle*-1) - Math.PI*1.5); + + // find shortest rotation delta + let aAngle = angle; + let aTargetAngle = targetAngle; + if (aTargetAngle > aAngle && aTargetAngle - aAngle > Math.PI) { + aAngle += Math.PI; + aTargetAngle -= Math.PI; + } else if (aAngle > aTargetAngle && aAngle - aTargetAngle > Math.PI) { + aAngle -= Math.PI; + aTargetAngle += Math.PI; + } + + rDelta = aTargetAngle - aAngle; + + // console.log(`myAngle: ${angle * (180 / Math.PI)} target angle: ${targetAngle * (180 / Math.PI)}`) + // console.log(`deelta: ${(rDelta) * (180 / Math.PI)} `); + + // apply delta + angle += rDelta * turnSpeedCoeff; + + // counter overflow + if (angle > 2*Math.PI) angle -= 2*Math.PI; + else if (angle < 0) angle += 2*Math.PI; + + // stop when close + let dist = Math.sqrt((xPos-targetX)**2 + (yPos-targetY)**2); + brake = dist < brakeDist; + if (!brake) { + accel = accelCurve(dist); + if (accel > maxAccel) accel = maxAccel; + else if (accel < -maxAccel) accel = -maxAccel; + } else accel = 0; + + // physics + velocity += accel * deltaT; + + if (brake) { + // basic friction + velocity *= slideFric; + } + + // vel limit + if (velocity > maxVel) velocity = maxVel; + else if (velocity < -maxVel) velocity = -maxVel + + // translate vector to 2D + let hAngle = angle % Math.PI; + let right = angle > Math.PI; + let xVel = Math.sin(hAngle) * velocity; + let yVel = Math.cos(hAngle) * velocity; + if (right) { + yVel *= -1; + } else { + xVel *= -1; + } + + // apply velocity + xPos += xVel * deltaT; + yPos += yVel * deltaT; + + // console.log(`accel: ${accel}`); + // console.log(`velocity: ${velocity} (${xVel}, ${yVel})`) + + let visualAngle = angle + (rDelta/2); + visualAngle = targetAngle; + + thing.style.left = xPos + 'px'; + thing.style.top = yPos + 'px'; + // thing.style.rotate = visualAngle + 'rad'; + // if (brake) { + // thing.style['background-color'] = '#00ff00'; + // } else { + // thing.style['background-color'] = '#ff0000'; + // } + + let fuck = visualAngle + Math.PI/4; + if (fuck < Math.PI/2 || fuck > Math.PI * 2) { + // front-facing + thing.style['background-position-x'] = '100px'; + } else if (fuck < Math.PI) { + // left-facing + thing.style['background-position-x'] = '0px'; + } else if (fuck < Math.PI*3/2) { + // right-facing + thing.style['background-position-x'] = '150px'; + } else { + // back-facing + thing.style['background-position-x'] = '50px'; + } + + if (thisUpdate - lastAlt > 70) { + lastAlt = thisUpdate; + alt = !alt; + } + + if (velocity > 50) { + thing.style['background-position-y'] = alt ? '50px' : '100px'; + } else { + thing.style['background-position-y'] = '0px'; + } + +} + +document.addEventListener('mousemove', e => { + mouseX = e.clientX; + mouseY = e.clientY; +}); + +document.addEventListener('scroll', e => { + el = e.target.scrollingElement; + scrollX = el.scrollLeft; + scrollY = el.scrollTop; +}); + +let duck_start = false; +function click_duck() { + if (duck_start) return; + duck_start = true; + + xPos = thing.offsetLeft; + yPos = thing.offsetTop; + thing.style.position = 'absolute'; + thing.style.cursor = 'auto'; + lastUpdate = Date.now(); + + updateTimerId = setInterval(update, 10); +} + diff --git a/www/img/duck-sprite.webp b/www/img/duck-sprite.webp new file mode 100644 index 0000000..3a46dd1 Binary files /dev/null and b/www/img/duck-sprite.webp differ diff --git a/www/index.html b/www/index.html index 3ff831d..1fbe2d3 100644 --- a/www/index.html +++ b/www/index.html @@ -316,7 +316,7 @@ 🄯 Copyleft 2024 Benjamin Wiegand
+ +