Шахматные ходы в D3

В этом вопросе обсуждается рисование шахматной доски с помощью D3:

Как нарисовать шахматную доску в D3?

введите здесь описание изображения

Кроме того, есть невероятный плагин для шахматной доски D3 от @jbkunst:

плагин d3-chessboard

введите здесь описание изображения

Однако я хотел бы анимировать шахматные ходы, например:

введите здесь описание изображения

(но гораздо более плавный; с настраиваемой продолжительностью и т. д.)

Есть ли у вас какие-либо советы, как это сделать в стиле D3?

Пока я был бы доволен анимацией всего одного движения. Позже я создам более общее решение.


person VividD    schedule 19.03.2016    source источник
comment
Я знаю, что для вопроса требуется d3, но есть много шахматных плагинов с функциональностью, которые могут работать лучше. При этом я пытаюсь понять, как вы двигаете фигуры. Ты пользуешься шахматным плагином или сам все рисуешь как в первой ссылке?   -  person Siderite Zackwehdex    schedule 19.03.2016
comment
@SideriteZackwehdex Я бы предпочел иметь автономное решение тому, которое зависит от плагина, но оба варианта могут быть интересны.   -  person VividD    schedule 19.03.2016


Ответы (3)


Вот краткая реализация с использованием цепочек переходов, чтобы части шагали по доске. Я пытался объяснить два различных типа движения: «линия», когда фигуры двигаются по прямой линии (т. е. слон, ладья), и «шаг», когда они двигаются ступенчато (т. е. конь). Я основывался на вашей работе в предыдущем вопросе.

// 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);
  })();
}

Примечание. Я не пытался кодировать, является ли это законным ходом.


Полный пример:

<!DOCTYPE html>
<html>

<head>
  <script data-require="[email protected]" 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>

person Mark    schedule 19.03.2016

Если вы знаете начальную позицию и конечную позицию, вы можете установить переход () для любого процесса, перемещающего части. Это сделает его анимированным между штатами. Тем не менее, это будет линейно, поэтому это будет хорошо выглядеть для любого, кто движется по сетке по прямой линии, и менее хорошо, если вы этого не сделаете (например, рыцарь). Для рыцаря я бы сначала переходил по одной оси, а потом по другой.

person nucleon    schedule 19.03.2016

Что ж, после некоторых исследований я нашел это: http://blog.visual.ly/creating-animations-and-transitions-with-d3-js/, который в значительной степени дает тот же ответ, что и nucleon: когда вы хотите изменить атрибуты элемента (например, позицию) в d3 и вы делаете что-то вроде d3.select(selector).attr(attribute,value) и вам нужно использовать d3.select(selector).transition().attr(attribute,value)

Однако то, как плагин рисует шахматную доску, например, у вас есть g элементов, представляющих квадраты, содержащие прямоугольник и текст. прямоугольник — цвет квадрата, а текст — часть. Если вы измените преобразование текста за пределами диапазона элемента g, он исчезнет. В любом случае это кажется неправильной моделью для движущихся частей.

Предположим, вы рисуете свою доску, а затем независимо рисуете свои фигуры, вы можете использовать пример по ссылке выше, чтобы переместить фигуры туда, куда вы хотите. Осторожнее с рыцарем, двигать его надо двумя переходами, и, наверное, стоит подумать над анимацией захвата.

Это все, что у меня есть.

person Siderite Zackwehdex    schedule 19.03.2016