Created
July 11, 2025 15:35
-
-
Save trohit/95ddfe84d91f6bc3744a777334bbc617 to your computer and use it in GitHub Desktop.
simple javascript spinwheel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Custom Spinning Wheel with URLs</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| canvas { | |
| border-radius: 9999px; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen flex flex-col items-center py-10 px-4"> | |
| <h1 class="text-3xl font-bold mb-6">🎯 Spinning Wheel with Links</h1> | |
| <div class="w-full max-w-xl"> | |
| <label class="block font-medium text-lg mb-1">Enter Items (comma-separated)</label> | |
| <textarea id="entries" class="w-full p-3 rounded border border-gray-300 mb-4" rows="2">Apple,Banana,Cherry,Date,Egg,Fig</textarea> | |
| <label class="block font-medium text-lg mb-1">Enter URLs (comma-separated)</label> | |
| <textarea id="urls" class="w-full p-3 rounded border border-gray-300 mb-4" rows="2">https://apple.com,https://banana.com,https://cherry.com,https://date.com,https://egg.com,https://fig.com</textarea> | |
| <button onclick="setupWheel()" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">Set Entries</button> | |
| </div> | |
| <canvas id="wheel" width="400" height="400" class="my-6 shadow-xl"></canvas> | |
| <button id="spin-btn" onclick="spin()" class="bg-green-500 text-white px-6 py-3 rounded-full text-lg hover:bg-green-600">🎲 Spin</button> | |
| <div id="result" class="text-xl font-semibold mt-4"></div> | |
| <script> | |
| const canvas = document.getElementById("wheel"); | |
| const ctx = canvas.getContext("2d"); | |
| const resultEl = document.getElementById("result"); | |
| let entries = []; | |
| let urls = []; | |
| let startAngle = 0; | |
| let spinning = false; | |
| let spinAngle = 0; | |
| function setupWheel() { | |
| const entryInput = document.getElementById("entries").value; | |
| const urlInput = document.getElementById("urls").value; | |
| entries = entryInput.split(",").map(e => e.trim()).filter(Boolean); | |
| urls = urlInput.split(",").map(u => u.trim()).filter(Boolean); | |
| if (entries.length < 2 || urls.length !== entries.length) { | |
| alert("Please ensure both entries and URLs are provided and match in count."); | |
| return; | |
| } | |
| drawWheel(); | |
| } | |
| function drawWheel(highlightIndex = null) { | |
| const radius = canvas.width / 2; | |
| const arc = 2 * Math.PI / entries.length; | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| entries.forEach((item, i) => { | |
| const angle = startAngle + i * arc; | |
| ctx.beginPath(); | |
| ctx.fillStyle = (i === highlightIndex) ? "#FFD700" : `hsl(${i * 360 / entries.length}, 70%, 60%)`; | |
| ctx.moveTo(radius, radius); | |
| ctx.arc(radius, radius, radius, angle, angle + arc); | |
| ctx.fill(); | |
| // Add labels | |
| ctx.save(); | |
| ctx.translate(radius, radius); | |
| ctx.rotate(angle + arc / 2); | |
| ctx.textAlign = "right"; | |
| ctx.fillStyle = "#000"; | |
| ctx.font = "16px sans-serif"; | |
| ctx.fillText(item, radius - 10, 5); | |
| ctx.restore(); | |
| }); | |
| // Draw center circle | |
| ctx.beginPath(); | |
| ctx.arc(radius, radius, 20, 0, 2 * Math.PI); | |
| ctx.fillStyle = "#fff"; | |
| ctx.fill(); | |
| // Draw pointer | |
| ctx.beginPath(); | |
| ctx.moveTo(radius, 0); | |
| ctx.lineTo(radius - 10, 20); | |
| ctx.lineTo(radius + 10, 20); | |
| ctx.closePath(); | |
| ctx.fillStyle = "#111"; | |
| ctx.fill(); | |
| } | |
| function spin() { | |
| if (spinning || entries.length < 2) return; | |
| spinning = true; | |
| resultEl.textContent = ""; | |
| const duration = 4000; | |
| const extraSpins = 5 * 2 * Math.PI; | |
| const finalAngle = Math.random() * 2 * Math.PI; | |
| const target = extraSpins + finalAngle; | |
| const start = performance.now(); | |
| function animate(time) { | |
| const elapsed = time - start; | |
| const progress = Math.min(elapsed / duration, 1); | |
| spinAngle = easeOutCubic(progress) * target; | |
| startAngle = spinAngle % (2 * Math.PI); | |
| drawWheel(); | |
| if (progress < 1) { | |
| requestAnimationFrame(animate); | |
| } else { | |
| const index = getSelectedIndex(); | |
| const selected = entries[index]; | |
| const url = urls[index]; | |
| drawWheel(index); | |
| resultEl.innerHTML = ` | |
| 🎉 You got: <b>${selected}</b><br> | |
| <button id="go-btn" class="mt-2 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">Take me there</button> | |
| `; | |
| spinning = false; | |
| document.getElementById("go-btn").onclick = () => { | |
| window.open(url, "_blank"); | |
| }; | |
| } | |
| } | |
| requestAnimationFrame(animate); | |
| } | |
| // FIXED getSelectedIndex | |
| /* | |
| function getSelectedIndex() { | |
| const numberOfSegments = entries.length; | |
| const arc = 2 * Math.PI / numberOfSegments; | |
| // Adjust angle by subtracting Pi (180 degrees) to fix pointer offset | |
| let angle = (2 * Math.PI - startAngle + Math.PI / 2 - Math.PI) % (2 * Math.PI); | |
| let index = Math.floor(angle / arc) % numberOfSegments; | |
| return index; | |
| } | |
| */ | |
| function getSelectedIndex() { | |
| const numberOfSegments = entries.length; | |
| const arc = 2 * Math.PI / numberOfSegments; | |
| // Adjust angle for pointer offset, then subtract a tiny epsilon | |
| let angle = (2 * Math.PI - startAngle + Math.PI / 2 - Math.PI - 1e-10) % (2 * Math.PI); | |
| let index = Math.floor(angle / arc) % numberOfSegments; | |
| // Ensure index is always valid | |
| if (index < 0) index += numberOfSegments; | |
| return index; | |
| } | |
| function easeOutCubic(t) { | |
| return 1 - Math.pow(1 - t, 3); | |
| } | |
| setupWheel(); // Init | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment