React+D3 声明式可视化展示

栏目: IOS · Android · 发布时间: 5年前

内容简介:在进行前端可视化图定制开发的时候,我们往往会使用d3.js来进行开发,其自包含了针对数据集合的处理以及操作节点集合的方式。下面我们将介绍在配合React使用时,借用React的思路,充分发挥React的组件化,声明式特性,来优化D3的开发过程。d3提供了d3-selection,来使用数据对节点进行驱动。我们可以根据数据拿到需要对节点进行的变更。往往我们会在componentDidMount中来进行节点操作,大概分为几个步骤:这种方式虽然完成了任务,但是我们依旧会感觉有不舒服的地方:

在进行前端可视化图定制开发的时候,我们往往会使用d3.js来进行开发,其自包含了针对数据集合的处理以及操作节点集合的方式。下面我们将介绍在配合React使用时,借用React的思路,充分发挥React的组件化,声明式特性,来优化D3的开发过程。

旧的方式

d3提供了d3-selection,来使用数据对节点进行驱动。我们可以根据数据拿到需要对节点进行的变更。往往我们会在componentDidMount中来进行节点操作,大概分为几个步骤:

  1. 根据数据和容器宽度,获取比例尺
  2. 根据data join的enter和exit选择集,来添加,删除或更新元素
export default class Graphextends React.Component{
  componentDidMount () {
    const { data = [] } = this.props
    
    this.renderBar(data)
  }
  renderBar (data) {
    // 拿到比例尺
    const scale = d3.scaleLinear()
      .domain(d3.extent(data))
      .range([0, 180])

    const wrap = d3.select('#qps-graph')

    wrap.selectAll(`g.bar`)
      .data(data)
      .enter()
      .append('g')
      .attr('class', 'bar')
      .append('rect')
      .attr('class', 'bar-rect')

    wrap.selectAll(`g.bar`)
      .data(data)
      .attr("transform", (d, i) => `translate(${i * 20},${180 - scale(d)})`)
      .select('rect')
      .style('height', d => scale(d))

    wrap.selectAll(`g.bar`)
      .data(data)
      .exit()
      .remove()
  }
  render() {
    return <svgheight="180"width="1000"id="qps-graph"></svg>
  }
}

这种方式虽然完成了任务,但是我们依旧会感觉有不舒服的地方:

  1. 我们很难对图形进行复用,图形稍微改动一点,就需要改动代码
  2. 在声明式代码中掺杂了很多命令式的操作过程,不直观,不利于维护
  3. 针对事件处理,会很难进行,我们往往需要使用d3创建新的元素,来控制它的内容、显示和隐藏

新的方式

我们会发现,我们根据data join的enter和exit选择集,来添加,删除或更新元素,这个步骤,其实React也可以进行,并且可以使用更加直观的声明式来书写,类似这样:

render(){
  const scale = d3.scaleLinear()
    .domain(d3.extent(data))
    .range([0, 180])
  const h = d => scale(d)
  const y = (d, i) => `translate(${i * 20},${180 - scale(d)})`

  return <svgheight="180"width="1000">
    {data.map((d, i) => {
      return (
        <gkey={`bar-${i}`}transform={y(d,i)}>
          <rect
            height={h(d)}
            width={10}
            fill='#fc2e1c'
          />
        </g>
      );
    })}
  </svg>
}

是不是很简单!同时,我们可以利用React的组件化特性,来对我们的每个单元模块进行封装,独立功能,方便各个组件的复用,来让我们的可视化代码更加直观,类似这样:

render(){
  const scale = d3.scaleLinear()
    .domain(d3.extent(data))
    .range([0, 180])
  const h = d => scale(d)
  const y = (d, i) => `translate(${i * 20},${180 - scale(d)})`

  return <svgheight="180"width="1000">
    {data.map((d, i) => {
      return (
        <Groupkey={`bar-${i}`}left={i*20}top={180-scale(d)}>
          <Bar
            height={h(d)}
            width={10}
            fill='#fc2e1c'
          />
        </Group>
      );
    })}
  </svg>
}

我们有幸看到已经有人做了这个工作: vx ,作者已经封装了很多的常用图形,我们的任务就是,对这些图形加上数据,进行拼接就可以了。同时,我们可以对其进行二次封装,以适应我们的项目。

经过一系列封装,我们写一个图形,这样操作即可:

// 定义getter
const x = d => new Date(d.date);
const y = d => +d.close;

// 定义容器配置
const padding = {
  top: 20,
  left: 40,
  bottom: 20,
  right: 20
};

export default class LineGraph{
  render() {
    return (
      <Container width={1000} height={200} padding={padding} x={x} y={y}>
        {({ width, height }) => {
          // 根据数据定义x,y轴的scale
          const xScale = scaleTime({
            rangeRound: [0, width],
            domain: d3.extent(data, x)
          });
          const yScale = scaleLinear({
            rangeRound: [height, 0],
            domain: d3.extent(data, y)
          });

          return (
            <Group>
              <Grid
                width={width}
                height={height}
                xScale={xScale}
                yScale={yScale}
              />
              <AxisBottom top={height} scale={xScale} />
              <AxisLeft scale={yScale} />
              <AreaClosed
                data={data}
                xScale={xScale}
                yScale={yScale}
                x={x}
                y={y}
              />
            </Group>
          );
        }}
      </Container>
    );
  }
}

More

其实不止这些基本的图形可以用这种方式,当我们进行更加复杂图形的书写时,也可以使用这种方式。

例如我们最近做了一个复杂的树形结构,其每个节点都包含了很复杂的内容和交互,我们可以将其进行简化成以下方式:

render () {
  return <Container>
    <Defs/>
    <RootNode/>
    <TopTree/>
    <BottomTree/>
  </Container>
}

其中Tree可以进行继续的封装,拼接:

render () {
  const nodeElements = nodes.map((node, index) => (
    <Node direction={direction} node={node} methods={methods} />
  ));
  const linkElements = links.map((link, index) => {
    return (
      <Path
        active={active}
        item={item}
        direction={direction}
        s={link.source}
        d={link.target}
        activeItem={activeItem}
      />
    );
  });
  return <Group>
    {nodeElements}
    {linkElements}
  </Group>
}

然后我们对Node,和Path组件,进行详细的书写,这样非常的简洁明了,不是吗?

同时,当我们其他可视化组件需要这样的Path活Node或者Tree时,我们直接拿来用即可!


以上所述就是小编给大家介绍的《React+D3 声明式可视化展示》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Making Things See

Making Things See

Greg Borenstein / Make / 2012-2-3 / USD 39.99

Welcome to the Vision Revolution. With Microsoft's Kinect leading the way, you can now use 3D computer vision technology to build digital 3D models of people and objects that you can manipulate with g......一起来看看 《Making Things See》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试