Konva中的动画线位置

时间:2019-07-31 15:14:43

标签: javascript konvajs react-konva

我有一个要与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;

1 个答案:

答案 0 :(得分:1)

您可能需要为points属性设置初始值才能获得更好的补间效果。 另外,您没有在source组件中使用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