为四子棋(Connect Four)棋盘添加棋子下落动画的JS/CSS方案咨询
Awesome job getting your Connect Four game fully functional! Adding that satisfying drop animation will take it to the next level, and I’ve got a simple, jQuery-free solution that fits right into your existing code. We’ll use CSS transitions for the smooth fall and a tiny bit of JavaScript to handle the animation lifecycle.
Step 1: Add CSS for Animation & Positioning
First, drop these styles into your CSS file. They’ll handle the falling piece’s appearance and movement:
/* Make the table a relative container so we can position the falling piece inside it */ table { position: relative; /* Keep your existing table styles here */ } /* Style the falling piece to match your game's hole size/shape */ .falling-piece { position: absolute; width: 50px; /* Match your .hole button width */ height: 50px; /* Match your .hole button height */ border-radius: 50%; /* Creates the classic Connect Four circle */ pointer-events: none; /* Prevents the piece from blocking clicks mid-fall */ transition: top 0.5s ease-in; /* Smooth, natural falling motion */ } /* Optional: Tweak your hole styles if needed (ensure they're consistent) */ .hole { width: 50px; height: 50px; border: 1px solid #333; border-radius: 50%; background: #fff; position: relative; /* Helps with positioning context */ } .droplocation { width: 50px; height: 50px; font-size: 24px; }
Step 2: Create a Reusable Animation Helper Function
Add this JavaScript function to your script. It handles creating the falling piece, animating it from the top button to the target hole, and cleaning up once the animation finishes:
let isAnimating = false; // Prevent multiple clicks during animation function animatePieceDrop(columnIndex, targetHoleId, pieceColor) { isAnimating = true; // Get elements we need for positioning const dropButton = document.getElementById(`Z${columnIndex}`); const targetHole = document.getElementById(targetHoleId); const table = document.querySelector('table'); // Create the temporary falling piece const fallingPiece = document.createElement('div'); fallingPiece.classList.add('falling-piece'); fallingPiece.style.backgroundColor = pieceColor; // Calculate positions relative to the table const buttonPos = dropButton.getBoundingClientRect(); const tablePos = table.getBoundingClientRect(); const targetPos = targetHole.getBoundingClientRect(); // Set the piece's starting position (right over the drop button) fallingPiece.style.left = `${buttonPos.left - tablePos.left}px`; fallingPiece.style.top = `${buttonPos.top - tablePos.top}px`; // Add the piece to the table table.appendChild(fallingPiece); // Force a reflow to trigger the transition (trick to make CSS transitions work) void fallingPiece.offsetWidth; // Set the final position (on top of the target hole) fallingPiece.style.top = `${targetPos.top - tablePos.top}px`; // When the animation ends: update the hole, clean up, and check for a win fallingPiece.addEventListener('transitionend', () => { targetHole.style.backgroundColor = pieceColor; table.removeChild(fallingPiece); // Check for win after the piece is in place if (pieceColor === '#f44256') { checkForRedWin(); } else { checkForBlueWin(); } isAnimating = false; // Don't forget to switch turns here if your code handles that! }); }
Step 3: Update Your Existing drop Functions
Now modify each of your 7 drop functions to use the animation instead of directly setting the background color. Here’s how your drop1() function will look (the others will follow the same pattern, just adjusting the IDs):
function drop1() { if (isAnimating) return; // Block clicks during animation if(turn == "red" && gameisover == false) { if(f1isempty == true){ animatePieceDrop(1, "F1", "#f44256"); f1isempty = false; f1isred = true; } else if(e1isempty == true){ animatePieceDrop(1, "E1", "#f44256"); e1isempty = false; e1isred = true; } else if(d1isempty == true){ animatePieceDrop(1, "D1", "#f44256"); d1isempty = false; d1isred = true; } else if(c1isempty == true){ animatePieceDrop(1, "C1", "#f44256"); c1isempty = false; c1isred = true; } else if(b1isempty == true){ animatePieceDrop(1, "B1", "#f44256"); b1isempty = false; b1isred = true; } else if(a1isempty == true){ animatePieceDrop(1, "A1", "#f44256"); a1isempty = false; a1isred = true; } else { alert("Looks like that column is filled up! Let's try another one, shall we?"); } // Switch turn here if your code doesn't handle it elsewhere } else if(turn == "blue" && gameisover == false) { if(f1isempty == true){ animatePieceDrop(1, "F1", "#4183f4"); f1isempty = false; f1isblue = true; } else if(e1isempty == true){ animatePieceDrop(1, "E1", "#4183f4"); e1isempty = false; e1isblue = true; } else if(d1isempty == true){ animatePieceDrop(1, "D1", "#4183f4"); d1isempty = false; d1isblue = true; } else if(c1isempty == true){ animatePieceDrop(1, "C1", "#4183f4"); c1isempty = false; c1isblue = true; } else if(b1isempty == true){ animatePieceDrop(1, "B1", "#4183f4"); b1isempty = false; b1isblue = true; } else if(a1isempty == true){ animatePieceDrop(1, "A1", "#4183f4"); a1isempty = false; a1isblue = true; } else { alert("Looks like that column is filled up! Let's try another one, shall we?"); } // Switch turn here if your code doesn't handle it elsewhere } }
Quick Tweaks to Perfect It
- Adjust Fall Speed: Change the
0.5svalue in the.falling-piecetransition to make the fall faster (e.g.,0.3s) or slower (e.g.,0.7s). - Match Sizes: Ensure the
.falling-piecewidth/height exactly matches your.holebuttons so it lines up perfectly. - Turn Switch: Don’t forget to handle switching turns after the animation finishes (either in the
transitionendhandler or after callinganimatePieceDrop).
This solution keeps your existing code structure intact while adding that satisfying natural fall effect—no jQuery required!
内容的提问来源于stack exchange,提问作者Reilly Ford

