/** * Wheel of Fortune - JavaScript for wheel functionality * Robust, smooth animation using requestAnimationFrame and easing functions. */ jQuery(document).ready(function($) { // Check if wheelSettings are available if (typeof wheelSettings === 'undefined') { console.error('Wheel of Fortune: Settings (wheelSettings) are not defined. Please check the plugin setup.'); return; } // Elements const wheel = $('.wheel'); const button = $('.wheel-button'); const resultDiv = $('.wheel-result'); const spinsCounter = $('.wheel-spins-counter span'); // Animation state let isSpinning = false; let animationFrameId = null; let accumulatedRotation = 0; // Total rotation is maintained between spins // Animation settings const spinDuration = 8000; // 8 seconds for a more dramatic effect const spinRotations = 5; // Number of full rotations before stopping /** * Easing function (cubic-bezier out) for natural deceleration * @param {number} t - Current time (0 to 1) * @returns {number} - Animation progress (0 to 1) */ function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); } /** * Main animation loop * @param {number} startTime - Animation start time * @param {number} startRotation - Starting rotation angle * @param {number} rotationAmount - Total rotation to perform for this spin */ function animateWheel(startTime, startRotation, rotationAmount) { const currentTime = Date.now(); const elapsedTime = currentTime - startTime; if (elapsedTime >= spinDuration) { wheel.css('transform', `rotate(${startRotation + rotationAmount}deg)`); accumulatedRotation = startRotation + rotationAmount; // Update the final position finishSpin(); return; } // Calculate progress within the animation duration const timeProgress = elapsedTime / spinDuration; // Apply the easing function to determine the rotation progress const rotationProgress = easeOutCubic(timeProgress); // Calculate the current rotation const newRotation = startRotation + (rotationAmount * rotationProgress); wheel.css('transform', `rotate(${newRotation}deg)`); // Continue to the next frame animationFrameId = requestAnimationFrame(() => animateWheel(startTime, startRotation, rotationAmount)); } /** * Function to execute after the spin animation is complete */ function finishSpin() { // Display the result with a slight delay for better effect setTimeout(() => { const prize = window.spinResult.prize; resultDiv.html(`

Congratulations!

You've won: ${prize.name}

`); resultDiv.addClass('win').fadeIn(300); isSpinning = false; // Re-enable the button if there are remaining spins if (window.spinResult.remaining_spins > 0) { button.prop('disabled', false); } else { button.prop('disabled', true); } }, 500); // 500ms pause before showing the result } // Spin button click handler button.on('click', function() { if (isSpinning) return; isSpinning = true; button.prop('disabled', true); resultDiv.hide().removeClass('win error'); $.ajax({ url: wheelSettings.ajaxUrl, method: 'POST', data: { action: 'wheel_spin', nonce: wheelSettings.nonce }, beforeSend: function(xhr) { xhr.setRequestHeader('X-WP-Nonce', wheelSettings.nonce); }, success: function(response) { if (response.success) { window.spinResult = response; // The target angle (0-360) for the wheel to stop at, sent by the server. const targetAngle = response.degree; // Add several full rotations for a nice visual effect. const fullSpins = spinRotations * 360; // Get the current angle of the wheel, normalized to 0-360 degrees. const currentAngle = accumulatedRotation % 360; // Calculate the rotation needed to get from the current angle to the target angle. // We must always rotate forward (clockwise, positive rotation). let rotationToTarget = targetAngle - currentAngle; if (rotationToTarget < 0) { rotationToTarget += 360; } // The total amount of rotation for this spin. const rotationAmount = fullSpins + rotationToTarget; const startRotation = accumulatedRotation; // Start the animation. The function will rotate the wheel by 'rotationAmount' degrees. animateWheel(Date.now(), startRotation, rotationAmount); // Update the remaining spins counter on the screen. spinsCounter.text(response.remaining_spins); } else { // Display error from server resultDiv.html(`

${response.data.message || 'An error occurred.'}

`); resultDiv.addClass('error').fadeIn(300); isSpinning = false; button.prop('disabled', false); } }, error: function(xhr) { const errorMsg = xhr.responseJSON ? xhr.responseJSON.message : 'A communication error occurred.'; resultDiv.html(`

${errorMsg}

`); resultDiv.addClass('error').fadeIn(300); isSpinning = false; button.prop('disabled', false); } }); }); // Initialization if (wheelSettings.remainingSpins <= 0) { button.prop('disabled', true); } });