In this series on D3, we have been constructing a game of Reversi (OthelloTM). Follow the link if you're not familiar with the rules, but basically the game consists of trying to flip as many of your opponent's pieces to your color as possible. In this post, we're going to see how to animate the flipping.
If you have an SVG-compatible browser, I suggest you start by following this link to play a few moves. As you saw in the last post, the background will change when you're near the center of a square on which you may play. If you click, you'll flip one or more of your opponent's pieces.
So how is it done?
You can see the full source using your browser's View Source command, but this post will focus on the flipPiece method:
// Flip the piece at board index i,
// ixOfFlip is the index of the flipping piece in the array of flips.
// It is used to delay this piece's flip so they flip in sequance.
function flipPiece(state, i, ixOfFlip) {
var totalTime = 600; // Total milliseconds for the flip.
var delayToStart = ixOfFlip * totalTime * .6; // Times .6 so there's a pleasing overlap.
d3.select("#piece_" + i)
.transition().delay(delayToStart).duration(totalTime/2).attr("rx", 1)
.transition().delay(delayToStart + totalTime / 2).duration(0).attr("fill", function (d) { return d.color; })
.transition().delay(delayToStart + totalTime / 2).duration(totalTime / 2).attr("rx", circleRadius);
}
At the start of the function, we say that we want a flip to take 600 milliseconds, and we compute a delay before the flip begins. We want the pieces to flip in sequence, but I found through experimentation that if I waited until piece n's flip was completely done before starting flip n+1, it was boring. So I use the factor of .6 to make the flips overlap a little.
Next comes the main busness. We use D3 to select the piece by ID (e.g., #piece_27) and then set up three "transitions". All the transitions are queued simultaneously, but various delays cause them to execute sequentially. Let's look at each transition in turn.
The first transition changes the x-radius (rx) attribute of the ellipse that represents the piece. It shrinks the x-radius down to 1 so the piece appears to be on its edge. We want this to take half the total time of the animation, so a call to .duration(totalTime/2) is part of the transition. The effect is that during the first 300 milliseconds the piece will flip half-way, so we see it edgewise.
The second transition is the color change. We want this to happen when the piece is on its edge, so we set the delay appropriately. D3's default duration for a transition is 250 milliseconds, but we want this transition to happen instantly so we call .duration(0). Changing the color is as simple as changing the fill attribute of the SVG ellipse.
The final translation broadens the re-colored ellipse back to a circle. This completes the flip!
As you see, with a little creativity animations are fun and easy in D3! If you haven't done so already, and you have an SVG-compatible browser, I encourage you to play the game or view the complete source by clicking here.