Search code examples
javascriptanimationd3.jssvgchess

Chess moves in D3


Drawing a chess board with D3 is discussed in this question:

How to draw a chess board in D3?

enter image description here

Also, there is an incredible D3 chess board plugin by @jbkunst:

d3-chessboard plugin

enter image description here

However, I would like to animate chess moves, like this:

enter image description here

(but a lot smoother; with configurable duration etc.)

Do you have any advice how to do it, D3 style?

I would be happy with animation of just one move for now. I will build up the more general solution later.


Solution

  • Here's a quick implementation using chained transitions to make the pieces step across the board. I've tried to account for two different types of movement, "line" where the pieces move in a straight line (ie bishop, castle) and "step" where they move step-wise (ie knight). I based it off your work in the previous question.

    // piece is the text element to move
    // position is an object like { x: 4, y: 6 } of the board position to move to
    // type is "step" or "line"
    function movePiece(piece, position, type) {
    
      var p = d3.select(piece),
          d = p.datum();
    
      (function repeat() {
    
        if (type === "step"){
          if (position.y === d.y) {
            if (position.x === d.x) {
              return;
            } else if (position.x > d.x) {
              d.x += 1;
            } else {
              d.x -= 1;
            }
          } else {
            if (position.y > d.y) {
              d.y += 1;
            } else {
              d.y -= 1;
            }
          }
        } else {
          if (position.x === d.x &&
              position.y === d.y) {
              return;
          }
          else {
            if (position.x != d.x){
              if (position.x > d.x) {
                d.x += 1;
              } else {
                d.x -= 1;
              }
            }
            if (position.y != d.y){
              if (position.y > d.y) {
                d.y += 1;
              } else {
                d.y -= 1;
              }
            }
          }
        }
    
        p = p.transition()
          .transition()
          .attr("x", d.x * fieldSize)
          .attr("y", d.y * fieldSize)
          .each("end", repeat);
      })();
    }
    

    Note, I didn't attempt to code whether it's a legal move.


    Full example:

    <!DOCTYPE html>
    <html>
    
    <head>
      <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
      <link rel="stylesheet" href="style.css" />
      <script src="script.js"></script>
    </head>
    
    <body>
      <script>
        var pieces = {
          NONE: {
            name: "None",
            code: " "
          },
          WHITE_KING: {
            name: "White King",
            code: "\u2654"
          },
          WHITE_QUEEN: {
            name: "White Queen",
            code: "\u2655"
          },
          WHITE_ROOK: {
            name: "White Rook",
            code: "\u2656"
          },
          WHITE_BISHOP: {
            name: "White Bishop",
            code: "\u2657"
          },
          WHITE_KNIGHT: {
            name: "White Knight",
            code: "\u2658"
          },
          WHITE_POWN: {
            name: "White Pown",
            code: "\u2659"
          },
          BLACK_KING: {
            name: "Black King",
            code: "\u265A"
          },
          BLACK_QUEEN: {
            name: "Black Queen",
            code: "\u265B"
          },
          BLACK_ROOK: {
            name: "Black Rook",
            code: "\u265C"
          },
          BLACK_BISHOP: {
            name: "Black Bishop",
            code: "\u265D"
          },
          BLACK_KNIGHT: {
            name: "Black Knight",
            code: "\u265E"
          },
          BLACK_POWN: {
            name: "Black Pown",
            code: "\u265F"
          },
        };
    
        var board = [],
          boardDimension = 8,
          fieldSize = 40;
    
        for (var i = 0; i < boardDimension * boardDimension; i++) {
          board.push({
            x: i % boardDimension,
            y: Math.floor(i / boardDimension),
            piece: pieces.NONE
          });
        };
    
        board[0].piece = pieces.BLACK_ROOK
        board[1].piece = pieces.BLACK_KNIGHT
        board[2].piece = pieces.BLACK_BISHOP
        board[3].piece = pieces.BLACK_QUEEN
        board[4].piece = pieces.BLACK_KING
        board[5].piece = pieces.BLACK_BISHOP
        board[6].piece = pieces.BLACK_KNIGHT
        board[7].piece = pieces.BLACK_ROOK
    
        board[8].piece = pieces.BLACK_POWN
        board[9].piece = pieces.BLACK_POWN
        board[10].piece = pieces.BLACK_POWN
        board[11].piece = pieces.BLACK_POWN
        board[12].piece = pieces.BLACK_POWN
        board[13].piece = pieces.BLACK_POWN
        board[14].piece = pieces.BLACK_POWN
        board[15].piece = pieces.BLACK_POWN
    
        board[6 * 8 + 0].piece = pieces.WHITE_POWN
        board[6 * 8 + 1].piece = pieces.WHITE_POWN
        board[6 * 8 + 2].piece = pieces.WHITE_POWN
        board[6 * 8 + 3].piece = pieces.WHITE_POWN
        board[6 * 8 + 4].piece = pieces.WHITE_POWN
        board[6 * 8 + 5].piece = pieces.WHITE_POWN
        board[6 * 8 + 6].piece = pieces.WHITE_POWN
        board[6 * 8 + 7].piece = pieces.WHITE_POWN
    
        board[7 * 8 + 0].piece = pieces.WHITE_ROOK
        board[7 * 8 + 1].piece = pieces.WHITE_KNIGHT
        board[7 * 8 + 2].piece = pieces.WHITE_BISHOP
        board[7 * 8 + 3].piece = pieces.WHITE_QUEEN
        board[7 * 8 + 4].piece = pieces.WHITE_KING
        board[7 * 8 + 5].piece = pieces.WHITE_BISHOP
        board[7 * 8 + 6].piece = pieces.WHITE_KNIGHT
        board[7 * 8 + 7].piece = pieces.WHITE_ROOK
    
        var svg = d3.select('body')
          .append('svg')
          .attr('width', 500)
          .attr('height', 500);
    
        svg.selectAll("rect")
          .data(board)
          .enter()
          .append("rect")
          .style("class", "fields")
          .style("class", "rects")
          .attr("x", function(d) {
            return d.x * fieldSize;
          })
          .attr("y", function(d) {
            return d.y * fieldSize;
          })
          .attr("width", fieldSize + "px")
          .attr("height", fieldSize + "px")
          .style("fill", function(d) {
            if (((d.x % 2 == 0) && (d.y % 2 == 0)) ||
              ((d.x % 2 == 1) && (d.y % 2 == 1)))
              return "beige";
            else
              return "tan";
          });
    
        var pieces = svg.selectAll("text")
          .data(board)
          .enter().append("text")
          .attr("x", function(d) {
            d.piece.x = d.x;
            return d.x * fieldSize;
          })
          .attr("y", function(d) {
            d.piece.y = d.y;
            return d.y * fieldSize;
          })
          .style("font-size", "40")
          .attr("text-anchor", "middle")
          .attr("dy", "35px")
          .attr("dx", "20px")
          .text(function(d) {
            return d.piece.code;
          })
    
        pieces
          .append("title")
          .text(function(d) {
            return d.piece.name;
          });
    
        movePiece(pieces[0][6], {
          x: 5,
          y: 2
        }, "step");
        
        movePiece(pieces[0][58], {
          x: 5,
          y: 4
        }, "line");
        
        function movePiece(piece, position, type) {
          
          var p = d3.select(piece),
              d = p.datum();
              
          (function repeat() {
    
            if (type === "step"){
              if (position.y === d.y) {
                if (position.x === d.x) {
                  return;
                } else if (position.x > d.x) {
                  d.x += 1;
                } else {
                  d.x -= 1;
                }
              } else {
                if (position.y > d.y) {
                  d.y += 1;
                } else {
                  d.y -= 1;
                }
              }
            } else {
              if (position.x === d.x &&
                  position.y === d.y) {
                  return;
              }
              else {
                if (position.x != d.x){
                  if (position.x > d.x) {
                    d.x += 1;
                  } else {
                    d.x -= 1;
                  }
                }
                if (position.y != d.y){
                  if (position.y > d.y) {
                    d.y += 1;
                  } else {
                    d.y -= 1;
                  }
                }
              }
            }
    
            p = p.transition()
              .transition()
              .attr("x", d.x * fieldSize)
              .attr("y", d.y * fieldSize)
              .each("end", repeat);
          })();
        }
      </script>
    </body>
    
    </html>