HTML/CSS
JavaScript
Spin the Wheel
It is commonly used in promotions, giveaways, decision-making tools, and gamified experiences.
<!DOCTYPE HTML> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta charset="utf-8"> <title>Wheel of Fortune Bingo</title> <style type="text/css"> body { font-family: Helvetica, Arial, sans-serif; } text { font-family: Helvetica, Arial, sans-serif; font-size: 10px; pointer-events: none; } #chart { background-image: url("https://experiencenetcorecloud.com/assets/images/spinbg.png"); background-repeat: no-repeat; background-size: 130px; background-position: center 185px; } .close { position: absolute; color: red; right: 2px; top: 2px; font-family: Arial; font-size: 18px; line-height: 14px; cursor: pointer; border: 1px solid red; border-radius: 14px; padding: 3px; } .spinner-wappers { width: 100%; height: 100%; display: block; background: #f2f2f2; } .spinner { margin: 0 auto; display: block; position: relative; width: 400px; height: 365px; } #formContainer { display: none; position: fixed; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); z-index: 1000; } .form-wrapper { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: white; padding: 15px; border-radius: 5px; width: 300px; max-width: 90%; } .form-title { text-align: left; margin-bottom: 15px; font-size: 12px; font-weight: bold; } .form-group { margin-bottom: 10px; } .form-group label { display: block; margin-bottom: 5px; font-weight: bold; text-align: left; font-size: 10px; } .success-message h2 { font-size: 14px; font-weight: bold; margin-bottom: 10px; text-align: center; } .form-group input { width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px; text-align: left; font-size: 10px; } .form-group .error { color: red; font-size: 8px; margin-top: 5px; display: none; } .spin-submit-btn { background-color: #BF2526!Important; color: white!Important; border: none!Important; padding: 10px 20px!Important; width: 100%!Important; border-radius: 4px!Important; cursor: pointer!Important; font-size: 14px!Important; font-weight: bold!Important; } .spin-submit-btn:hover { background-color: #9E1E1F!Important; } .success-message { display: none; text-align: center; } .prize-title { font-size: 18px; color: #BF2526; margin-bottom: 10px; } .prize-info p { font-size: 10px; color: #000; } .coupon-code { background-color: #f5f5f5; border: 2px dashed #BF2526; padding: 10px 15px; font-size: 20px; font-weight: bold; letter-spacing: 1px; margin: 15px auto; max-width: 200px; border-radius: 4px; } .spinner { animation: none ! Important; } .spinner *:focus-visible { outline: none !important; box-shadow: none !important; } .coupon-instructions { font-size: 14px; color: #555; margin-top: 10px; } .disabled { opacity: 0.5; cursor: not-allowed !important; } #spinButton { cursor: pointer; } </style> </head> <body> <div class="spinner-wappers"> <div class="spinner"> <div id="chart"></div> </div> </div> <div id="formContainer"> <div class="form-wrapper"> <div class="form-content"> <div class="close" id="close-form">X</div> <div class="form-title">Congratulations! You've won: <span id="prizeText"></span></div> <div class="form-group"> <label for="name">Name:</label> <input type="text" id="name" placeholder="Enter your name"> <div class="error" id="nameError">Please enter your name</div> </div> <div class="form-group"> <label for="email">Email: <span style="color: red;">*</span></label> <input type="email" id="email" placeholder="Enter your email"> <div class="error" id="emailError">Please enter a valid email address</div> </div> <div class="form-group"> <label for="phone">Phone: <span style="color: red;">*</span></label> <input type="tel" id="phone" placeholder="Enter your phone number"> <div class="error" id="phoneError">Please enter a valid phone number</div> </div> <button type="button" class="spin-submit-btn" id="claimBtn">Claim Offer Now</button> </div> <div class="success-message" id="successMessage"> <div class="close" id="close-success">X</div> <h2>Thank You!</h2> <div class="prize-info"> <p>Your prize: <span class="prize-title" id="finalPrize"></span></p> <div class="coupon-code" id="couponCode"></div> <p class="coupon-instructions">Use this code during checkout to redeem your offer.</p> </div> </div> </div> </div> </body> </html>
Note: In the console, type 'allow pasting,' and then you can paste the code below.
var script = document.createElement('script'); script.src = "https://d3js.org/d3.v3.min.js"; script.type = "text/javascript"; document.head.appendChild(script); script.onload = function() { console.log("D3.js v" + d3.version + " loaded successfully!"); }; var padding = {top: 20, right:10, bottom: 100, left: 10}, w = 400 - padding.left - padding.right, h = 365 - padding.top - padding.bottom, r = Math.min(w, h)/2, rotation = 0, oldrotation = 0, picked = 100000, oldpick = [], isSpinning = false, color = d3.scale.ordinal() .range([ "#8BC53E", "#0A9343", "#24A9E0", "#652C90", "#9E1E62", "#BE1D2D", ]); var data = [ {"label":"100 points", "value": 1, "coupon": "SORRY2025"}, {"label":"Buy 1 get 1 free", "value": 2, "coupon": "BOGO2025"}, {"label":"Buy 3 get 1 free", "value": 3, "coupon": "B3G1FREE"}, {"label":"Flat 50% Off", "value": 4, "coupon": "HALF2025"}, {"label":"Extra 10% Off", "value": 5, "coupon": "EXTRA10"}, {"label":"Extra 5% Off", "value": 6, "coupon": "EXTRA05"}, ]; var currentPrize = ""; var currentCoupon = ""; var svg = d3.select('#chart') .append("svg") .data([data]) .attr("width", w + padding.left + padding.right) .attr("height", h + padding.top + padding.bottom); var container = svg.append("g") .attr("class", "chartholder") .attr("transform", "translate(" + (w/2 + padding.left) + "," + (h/2 + padding.top) + ")"); var vis = container .append("g"); var finalVal = 'test'; var pie = d3.layout.pie().sort(null).value(function(d){return 1;}); // declare an arc generator function var arc = d3.svg.arc().outerRadius(r); // select paths, use arc generator to draw var arcs = vis.selectAll("g.slice") .data(pie) .enter() .append("g") .attr("class", "slice"); arcs.append("path") .attr("fill", function(d, i){ return color(i); }) .attr("d", function (d) { return arc(d); }); // add the text arcs.append("text").attr("transform", function(d){ d.innerRadius = 0; d.outerRadius = r; d.angle = (d.startAngle + d.endAngle)/2; return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")translate(" + (d.outerRadius -10) +")"; }) .attr("fill", "#fff") .style({"font-weight":"bold"}) .attr("text-anchor", "end") .text(function(d, i) { return data[i].label; }); // Define the spin button group var spinButtonGroup = container.append("g") .attr("id", "spinButton"); // Draw spin rectangle button spinButtonGroup.append("rect") .attr("x", -50) .attr("y", 165) .attr("rx", 5) .attr("width", 100) .attr("height", 40) .style({"fill":"#000"}); // Spin text spinButtonGroup.append("text") .attr("x", 0) .attr("y", 193) .attr("text-anchor", "middle") .text("SPIN") .style({"font-weight":"bold","font-size":"20px","fill":"#fff"}); // Add click event to spin button spinButtonGroup.style("cursor", "pointer") .on("click", function() { if (!isSpinning) { spin(); } }); function spin(){ // Set spinning state to true isSpinning = true; // Disable the button spinButtonGroup.classed("disabled", true) .style("pointer-events", "none"); // Check if all slices have been seen console.log("OldPick: " + oldpick.length, "Data length: " + data.length); if(oldpick.length == data.length){ console.log("done"); return; } var ps = 360/data.length, pieslice = Math.round(1440/data.length), rng = Math.floor((Math.random() * 1440) + 360); rotation = (Math.round(rng / ps) * ps); picked = Math.round(data.length - (rotation % 360)/ps); picked = picked >= data.length ? (picked % data.length) : picked; if(oldpick.indexOf(picked) !== -1){ d3.select(this).call(spin); return; } else { oldpick.push(picked); } rotation += 90 - Math.round(ps/2); vis.transition() .duration(3000) .attrTween("transform", rotTween) .each("end", function(){ // Mark question as seen d3.select(".slice:nth-child(" + (picked + 1) + ") path") .attr("stroke-width", "3px") .attr("stroke", "#fff"); oldrotation = rotation; // Get the value of the selected segment var prizeWon = data[picked]; currentPrize = prizeWon.label; currentCoupon = prizeWon.coupon; finalVal = JSON.stringify(prizeWon); console.log("You won: " + currentPrize + " with coupon: " + currentCoupon); // Show the form after 3 seconds setTimeout(function() { document.getElementById('prizeText').textContent = currentPrize; document.getElementById('formContainer').style.display = 'block'; }, 1000); // Reset the spinning state isSpinning = false; // Don't re-enable the button after spinning to prevent multiple spins // We'll keep it disabled }); } // Make arrow svg.append("g") .attr("transform", "translate(" + (w - padding.left) + "," + ((h/2)+padding.top) + ")") .append("path") .attr("d", "M-" + (r*.15) + ",0L0," + (r*.08) + "L0,-" + (r*.08) + "Z") .style({"fill":"#000"}); // Draw spin circle container.append("circle") .attr("cx", 0) .attr("cy", 0) .attr("r", 10) .attr("stroke", "#fff") .attr("stroke-width", 2) .style({"fill":"#BF2526"}); function rotTween(to) { var i = d3.interpolate(oldrotation % 360, rotation); return function(t) { return "rotate(" + i(t) + ")"; }; } function getRandomNumbers(){ var array = new Uint16Array(1000); var scale = d3.scale.linear().range([360, 1440]).domain([0, 100000]); if(window.hasOwnProperty("crypto") && typeof window.crypto.getRandomValues === "function"){ window.crypto.getRandomValues(array); console.log("works"); } else { // No support for crypto, get crappy random numbers for(var i=0; i < 1000; i++){ array[i] = Math.floor(Math.random() * 100000) + 1; } } return array; } // close validation and submission document.getElementById('close-form').addEventListener('click', function() { let elements = document.querySelectorAll('#formContainer'); elements.forEach(element => { element.style.display = 'none'; }); // Reset spinning state isSpinning = false; // Re-enable the spin button d3.select("#spinButton").classed("disabled", false).style("pointer-events", "auto"); }); document.getElementById('close-success').addEventListener('click', function() { let elements = document.querySelectorAll('#formContainer'); elements.forEach(element => { element.style.display = 'none'; }); // Reset spinning state isSpinning = false; // Re-enable the spin button d3.select("#spinButton").classed("disabled", false).style("pointer-events", "auto"); }); // Form validation and submission document.getElementById('claimBtn').addEventListener('click', function() { var name = document.getElementById('name').value; var email = document.getElementById('email').value; var phone = document.getElementById('phone').value; var isValid = true; // Validate name (optional) if (name.trim() === '') { document.getElementById('nameError').style.display = 'none'; } // Validate email (required) if (email.trim() === '' || !isValidEmail(email)) { document.getElementById('emailError').style.display = 'block'; isValid = false; } else { document.getElementById('emailError').style.display = 'none'; } // Validate phone (required) if (phone.trim() === '' || !isValidPhone(phone)) { document.getElementById('phoneError').style.display = 'block'; isValid = false; } else { document.getElementById('phoneError').style.display = 'none'; } if (isValid) { // Collect and process form data var formData = { name: name, email: email, phone: phone, prize: currentPrize, coupon: currentCoupon }; console.log("Form submitted with data:", formData); // Show success message document.querySelector('.form-content').style.display = 'none'; document.getElementById('finalPrize').textContent = currentPrize; document.getElementById('couponCode').textContent = currentCoupon; document.getElementById('successMessage').style.display = 'block'; } }); function isValidEmail(email) { var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } function isValidPhone(phone) { var phoneRegex = /^\+?[0-9]{10,15}$/; return phoneRegex.test(phone); }