通过带有动画的d3-层次结构在树根周围分布节点

时间:2018-11-22 22:21:35

标签: reactjs d3.js

我随附了a code sandbox,显示了我的解决方案。

我觉得我的解决方案不合适,因为我已经在NodesFanout组件中完成了所有计算。

import * as React from "react";
import NodeGroup from "react-move/NodeGroup";
import { StructureItem } from "../../types";
import { Node } from "../Node";
import { HierarchyPointNode } from "d3-hierarchy";
import { HierarchyLabelProps } from "../HierarchyLabel";
import { findIndex } from "lodash";
import { NodeHeight } from "../Tree";
import { findCollapsedParent } from "../../util/node";

const { Group } = require("@vx/group");

export interface NodesProps {
  nodes: HierarchyPointNode<StructureItem>[];
  clickHandler: any;
  shapeLength: number;
  collapse: boolean;
  Label: React.ComponentType<HierarchyLabelProps>;
  LabelCollapsed: React.ComponentType<HierarchyLabelProps>;
}

const positionNodes = (
  node: HierarchyPointNode<StructureItem>,
  nodes: HierarchyPointNode<StructureItem>[]
) => {
  let left: number;
  let top: number;

  if (!node.parent) {
    left = node.y / 2 - NodeHeight;
    top = node.x - NodeHeight;
  } else if (node.data && node.data.isRight) {
    const index = findIndex(nodes, node);
    const lastLeft = nodes[index - 1];

    top = lastLeft.x;
    left = NodeHeight;
  } else {
    top = node.x;
    left = node.y;
  }

  return {
    top: [top],
    left: [left],
    opacity: [1]
  };
};

export const NodesFanout: React.SFC<NodesProps> = ({
  Label,
  LabelCollapsed,
  nodes,
  shapeLength,
  clickHandler,
  collapse
}) => {
  return (
    <NodeGroup
      data={nodes}
      keyAccessor={(d: HierarchyPointNode<StructureItem>) => d.data.id}
      start={(node: HierarchyPointNode<StructureItem>) => {
        let left: number;
        let top: number;

        if (!node.parent) {
          left = node.y / 2 - NodeHeight;
          top = node.x - NodeHeight;
        } else {
          left = node.parent.y / 2;
          top = node.parent.x;
        }

        return {
          top,
          left,
          opacity: 0
        };
      }}
      enter={node => positionNodes(node, nodes)}
      update={node => positionNodes(node, nodes)}
      leave={node => {
        let left: number;
        let top: number;

        const collapsedParent = findCollapsedParent(
          node.parent
        ) as HierarchyPointNode<StructureItem>;

        if (!collapsedParent.parent) {
          left = collapsedParent.y / 2;
          top = collapsedParent.x - NodeHeight;
        } else {
          left = collapsedParent.parent.y / 2;
          top = collapsedParent.parent.x - NodeHeight;
        }

        return {
          top: [top],
          left: [left],
          opacity: [0]
        };
      }}
    >
      {nodes => (
        <Group>
          {nodes.map(({ key, data: node, state }, index) => {
            return (
              <Group
                top={state.top}
                left={state.left}
                key={key}
                opacity={state.opacity}
                className={`node__${index}`}
              >
                <Node
                  Label={Label}
                  LabelCollapsed={LabelCollapsed}
                  node={node}
                  shapeLength={shapeLength}
                  clickHandler={(e: any) => {
                    clickHandler({ e, node });
                  }}
                  key={key}
                  collapse={collapse}
                />
              </Group>
            );
          })}
        </Group>
      )}
    </NodeGroup>
  );
};

动画也没有正确地从中心散开。

我正在尝试实现this之类的东西,其中所有节点都从中心散开。

有更好的方法吗?

1 个答案:

答案 0 :(得分:6)

我相信这可以达到目标:https://codesandbox.io/s/w4n1v3xvw

我认为代码没有问题,您所基于的示例只是一个例子,因此leave动画需要根据每个节点的侧面进行更新。

这是更改前带有注释的代码:

leave={node => {
        let left: number;
        let top: number;
        let nodeIndex: number;

        const collapsedParent = findCollapsedParent(
          node.parent
        ) as HierarchyPointNode<StructureItem>;

        if (!collapsedParent.parent) {
          // Get this nodes index from its' position in the parent
          nodeIndex = findIndex(node.parent.children, node);

          // Even indices go one side and odd go to the other
          // So we add a modifier which changes sign based on NodeHeight
          let modifier: number = NodeHeight;
          if (nodeIndex % 2 == 1) {
            modifier *= -1;
          }

          // We add the modifier in the left calculation
          left = collapsedParent.y / 2 + modifier;
          top = collapsedParent.x - NodeHeight;
        } else {
          left = collapsedParent.parent.y / 2;
          top = collapsedParent.parent.x - NodeHeight;
        }

        return {
          top: [top],
          left: [left],
          opacity: [0]
        };
      }}