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 声明式可视化展示》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

走出电商困局

走出电商困局

黄若 / 东方出版社 / 2013-11-1 / 38.00

高速增长的时代趋于结束,迅猛运转的加速器早已锈蚀 这是一场转型,更是一次新的机遇 《走出电商困局》是本年度最畅销管理书籍《我看电商》作者黄若的最新力作,意在深度剖析电商行业发展 ,破解电商困局。经历过10年超常规的快速增长,电商行业即将进入较为平稳的发展期。多年来这个行业不断融资不断烧钱却大多无法盈利的怪圈怎样突破?在很多企业面临估值下跌,资金吃紧,用户流失的关键节点,怎样从零售经营的......一起来看看 《走出电商困局》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

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

Base64 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具