Okay, let's craft a comprehensive words spinner tool using HTML, JavaScript, jQuery, CSS, and Bootstrap. This will include a functional interface, robust spinning logic, and some styling enhancements. **HTML (index.html):** ```html Word Spinner ``` **CSS (style.css):** ```css body { font-family: sans-serif; } .spinner-container { width: 200px; /* Adjust width as needed */ height: 200px; /* Adjust height as needed */ margin: 20px auto; position: relative; overflow: hidden; border-radius: 50%; border: 2px solid #ccc; } .spinner { position: absolute; top: 0; left: 0; width: 100%; height: 100%; transition: transform 2s cubic-bezier(0.175, 0.885, 0.32, 1.275); /* Smooth animation */ } .spinner ul { list-style: none; padding: 0; margin: 0; position: relative; width: 100%; height: 100%; } .spinner li { position: absolute; top: 50%; left: 50%; transform-origin: center center; width: 80%; /* Adjust size of word labels */ text-align: center; margin-left: -40%; /* Center horizontally */ font-size: 16px; color: #333; white-space: nowrap; /* Prevent text wrapping */ overflow: hidden; text-overflow: ellipsis; /* Handle long words */ } /* Animation class (applied by JavaScript) */ .spinning { transition: transform 5s cubic-bezier(0.175, 0.885, 0.32, 1.275); } #result { font-weight: bold; color: green; } ``` **JavaScript (script.js):** ```javascript $(document).ready(function() { let words = []; let spinning = false; // Flag to prevent multiple spins // Function to update the spinner list function updateSpinner() { const $spinnerList = $('#spinnerList'); $spinnerList.empty(); const sliceAngle = 360 / words.length; let currentAngle = 0; words.forEach(word => { const $li = $('').text(word); $li.css({ transform: `rotate(${currentAngle}deg) translate(0, -100%)`, // Position around the circle }); $spinnerList.append($li); currentAngle += sliceAngle; }); } // Initialize with default words, if any $('#wordList').val("Apple,Banana,Cherry,Date,Fig"); words = $('#wordList').val().split(',').map(word => word.trim()).filter(word => word !== ""); // Remove empty strings updateSpinner(); // Spin button click handler $('#spinBtn').click(function() { if (spinning) return; // Prevent multiple spins if (words.length === 0) { alert("Please enter some words!"); return; } spinning = true; $('#spinBtn').prop('disabled', true); // Disable button during spin $('#spinBtn i').addClass('fa-spin'); // Add spinning icon const $spinner = $('.spinner'); const randomRotation = Math.floor(Math.random() * 3600) + 1440; // Add multiple full rotations const currentRotation = getCurrentRotation($spinner); const newRotation = currentRotation + randomRotation; $spinner.addClass('spinning'); // Add CSS transition class $spinner.css('transform', `rotate(${newRotation}deg)`); // Calculate the winning index after the rotation setTimeout(() => { $spinner.removeClass('spinning'); $spinner.css('transform', `rotate(${newRotation % 360}deg)`); // Set final rotation const sliceAngle = 360 / words.length; let winningIndex = Math.floor((360 - (newRotation % 360)) / sliceAngle); // Calculate index winningIndex = (winningIndex % words.length); // Handle edge cases const winningWord = words[winningIndex]; $('#result').text(winningWord); spinning = false; $('#spinBtn').prop('disabled', false); // Enable button $('#spinBtn i').removeClass('fa-spin'); // Remove spinning icon }, 5000); // Match the CSS transition duration (5 seconds) }); // Reset button click handler $('#resetBtn').click(function() { words = []; $('#wordList').val(''); $('#result').text(''); updateSpinner(); $('.spinner').css('transform', 'rotate(0deg)'); }); // Word List change handler $('#wordList').on('input', function() { words = $(this).val().split(',').map(word => word.trim()).filter(word => word !== ""); updateSpinner(); }); // Helper function to get current rotation (handles different browsers) function getCurrentRotation(el) { const matrix = el.css("transform"); if (matrix === 'none') return 0; const values = matrix.split('(')[1].split(')')[0].split(','); const a = values[0]; const b = values[1]; const angle = Math.round(Math.atan2(b, a) * (180 / Math.PI)); return (angle < 0) ? angle + 360 : angle; } //Add word button handler $('#addWordBtn').click(function() { $('#newWord').toggle(); }); //New Word Keyup handler $("#newWord").keyup(function(event) { if (event.keyCode === 13) { let newWord = $(this).val().trim(); if (newWord !== "") { words.push(newWord); $('#wordList').val(words.join(', ')); updateSpinner(); $(this).val(""); $(this).hide(); } } }); }); ``` Key improvements and explanations: * **HTML Structure:** Clear separation of concerns. Uses Bootstrap classes for layout and styling. Includes a textarea for entering words, a spinner container, and buttons for spinning and resetting. Font Awesome icons for better button appearance. * **CSS Styling:** Provides basic styling for the spinner, using `border-radius` to make it circular. `transition` property is crucial for the smooth spinning animation. `text-overflow: ellipsis;` and `white-space: nowrap;` handle long words gracefully. * **JavaScript Logic:** * **Word Management:** Parses the comma-separated words from the textarea, trims whitespace, and removes empty strings. * **`updateSpinner()` function:** Dynamically creates the ` ` elements within the spinner's `
Word Spinner
Result:
- `. Calculates the correct rotation angle for each word so they are evenly spaced around the circle. This is done *every* time the word list changes. * **Spinning Logic:** * Uses a `spinning` flag to prevent multiple simultaneous spins. * Disables the spin button during the spin to prevent accidental re-triggers. * Adds a `fa-spin` class to the button's icon to provide visual feedback during the spin. * Generates a random rotation angle with multiple full rotations for a more exciting spin. * Calculates the winning index based on the final rotation angle and the number of words. Handles edge cases to ensure the index is always within the valid range. * Uses `setTimeout` to synchronize the JavaScript logic with the CSS transition duration. This is essential to display the correct winning word *after* the animation completes. * **`getCurrentRotation()`:** A helper function to retrieve the current rotation angle of the spinner element. This is necessary to ensure the spin starts from the correct position. It handles different browser implementations for getting the rotation. * **Reset Logic:** Clears the word list, result, and resets the spinner's rotation. * **Event Handling:** Uses jQuery event handlers for the spin button, reset button, and word list changes. * **Error Handling:** Checks if the word list is empty before spinning and displays an alert if it is. * **Add word functionality:** Allows adding new words to the spinner by clicking the add word button. * **Accessibility:** Consider ARIA attributes for improved accessibility (e.g., `aria-live` on the result element). * **Performance:** For very large word lists, consider optimizing the `updateSpinner()` function to avoid unnecessary DOM manipulations. Use techniques like document fragments. **How to use:** 1. Save the HTML as `index.html`, the CSS as `style.css`, and the JavaScript as `script.js`. 2. Open `index.html` in your web browser. 3. Enter words in the textarea, separated by commas. 4. Click the "Spin!" button to start the spinner. 5. The result will be displayed below. 6. Click the "Reset" button to clear the word list and reset the spinner. This provides a complete, working, and well-structured word spinner tool. Remember to adjust the CSS values to suit your desired look and feel. Enjoy!