0

我有一个我想用 Konva(和 react-konva 绑定)绘制的网络。当位置更新时,我想将网络中的节点动画到它们的新位置,同时动画连接它们的链接的开始和结束位置。

我从以下简单示例开始,但似乎无法以与节点相同的方式让 Line 进行动画处理。

有没有办法解决这个问题,还是我以错误的方式接近它?

import React from "react";
import { Stage, Layer, Rect, Line } from "react-konva";

class Node extends React.Component {
  componentDidUpdate() {
    this.rect.to({
      x: this.props.x,
      y: this.props.y,
    });
  }

  render() {
    const { id } = this.props;
    const color = id === "a" ? "blue" : "red";

    return (
      <Rect
        ref={node => {
          this.rect = node;
        }}
        width={5}
        height={5}
        fill={color}
      />
    );
  }
}

class Link extends React.Component {
  componentDidUpdate() {
    const x0 = 0;
    const y0 = 0;
    const x1 = 100;
    const y1 = 100;

    this.line.to({
      x: x0,
      y: y0,
      points: [x1, y1, x0, y0],
    });
  }

  render() {
    const color = "#ccc";

    return (
      <Line
        ref={node => {
          this.line = node;
        }}
        stroke={color}
      />
    );
  }
}

class Graph extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      nodes: [{ id: "a", x: 0, y: 0 }, { id: "b", x: 200, y: 200 }],
      links: [
        {
          source: "a",
          target: "b",
        },
      ],
    };
  }

  handleClick = () => {
    const nodes = this.state.nodes.map(node => {
      const position = node.x === 0 ? { x: 200, y: 200 } : { x: 0, y: 0 };

      return Object.assign({}, node, position);
    });

    this.setState({
      nodes,
    });
  };

  render() {
    const { links, nodes } = this.state;

    return (
      <React.Fragment>
        <Stage width={800} height={800}>
          <Layer>
            {nodes.map((node, index) => {
              return (
                <Node
                  key={`node-${index}`}
                  x={node.x}
                  y={node.y}
                  id={node.id}
                />
              );
            })}
          </Layer>
          <Layer>
            {links.map(link => {
              return (
                <Link
                  source={nodes.find(node => node.id === link.source)}
                  target={nodes.find(node => node.id === link.target)}
                />
              );
            })}
          </Layer>
        </Stage>
        <button onClick={this.handleClick}>Click me</button>
      </React.Fragment>
    );
  }
}

export default Graph;
4

1 回答 1

2

您可能需要为points属性设置初始值以获得更好的补间。此外,您没有在组件中使用sourceand 。您应该使用该道具来计算动画。targetLink

import React from "react";
import { render } from "react-dom";
import { Stage, Layer, Rect, Line } from "react-konva";

class Node extends React.Component {
  componentDidMount() {
    this.rect.setAttrs({
      x: this.props.x,
      y: this.props.y
    });
  }
  componentDidUpdate() {
    this.rect.to({
      x: this.props.x,
      y: this.props.y
    });
  }

  render() {
    const { id } = this.props;
    const color = id === "a" ? "blue" : "red";

    return (
      <Rect
        ref={node => {
          this.rect = node;
        }}
        width={5}
        height={5}
        fill={color}
      />
    );
  }
}

class Link extends React.Component {
  componentDidMount() {
    // set initial value:
    const { source, target } = this.props;

    console.log(source, target);
    this.line.setAttrs({
      points: [source.x, source.y, target.x, target.y]
    });
  }
  componentDidUpdate() {
    this.animate();
  }

  animate() {
    const { source, target } = this.props;

    this.line.to({
      points: [source.x, source.y, target.x, target.y]
    });
  }

  render() {
    const color = "#ccc";

    return (
      <Line
        ref={node => {
          this.line = node;
        }}
        stroke={color}
      />
    );
  }
}

class Graph extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      nodes: [{ id: "a", x: 0, y: 0 }, { id: "b", x: 200, y: 200 }],
      links: [
        {
          source: "a",
          target: "b"
        }
      ]
    };
  }

  handleClick = () => {
    const nodes = this.state.nodes.map(node => {
      const position = node.x === 0 ? { x: 200, y: 200 } : { x: 0, y: 0 };

      return Object.assign({}, node, position);
    });

    this.setState({
      nodes
    });
  };

  render() {
    const { links, nodes } = this.state;

    return (
      <React.Fragment>
        <Stage width={800} height={300}>
          <Layer>
            {nodes.map((node, index) => {
              return (
                <Node
                  key={`node-${index}`}
                  x={node.x}
                  y={node.y}
                  id={node.id}
                />
              );
            })}
          </Layer>
          <Layer>
            {links.map(link => {
              return (
                <Link
                  source={nodes.find(node => node.id === link.source)}
                  target={nodes.find(node => node.id === link.target)}
                />
              );
            })}
          </Layer>
        </Stage>
        <button onClick={this.handleClick}>Click me</button>
      </React.Fragment>
    );
  }
}

render(<Graph />, document.getElementById("root"));

演示:https ://codesandbox.io/s/react-konva-animating-line-demo-erufn

于 2019-08-01T03:37:06.973 回答