美文网首页
D3数据可视化-Area

D3数据可视化-Area

作者: 一拾五 | 来源:发表于2019-06-13 14:17 被阅读0次

    案例

    Area 图形可以填充整块的图形,下面是同样的数据(2017年2季度到2018年1季度手机品牌市场占有情况)分别在 area 和在 stack bar 图呈现的结果。

    area-market-share.png stack-bar-market-share.png

    解析

    d3.area 可以生成一个图形生成器,输入一个数组数据,将返回值传入 path 元素的 d 属性就可以渲染出对应的 area 图形。

    首先尝试一个比较简单的 case,只生成单一 area 图形的情况。

    可以构造一个 area 生成器:

    const areaGenerator = d3.area().x([x]).y0([y]).y1([y])
    

    y0 函数可以确定 x 点对应的 y 轴投影下限(base),y1 确定上限(top) 。单一的 area 与折线图非常类似,相当于现充了 y 点到 x 坐标轴的的折线图。

    single-area.png

    要生成 stack 形式的 area,可以拆分为两个步骤:

    1. 先将数据转化为 series 结构,每个数据点用 [baseline, topline] 描述上下限。
    2. 将 series 数据传递 area 生成器,生成对应的 path 图形。

    利用 d3.stack 生成 series 数据:

    const stack = d3.stack().keys([keys]);
    const series = stack(data);
    

    生成 path 图形的步骤如下:

    在 x 轴使用 scalePoint:

    // scalePoint in x axis
    const xAccessor = (d) => `${d.year}-${d.quarter}`;
    const xScale = d3.scalePoint()
      .domain(data.map(xAccessor))
      .range([0, maxWidth]);
    

    在 y 轴使用 scaleLinear:

    // scaleLinear in y axis
    const stackMin = serie => d3.min(serie, (d) => d[0]);
    const stackMax = serie => d3.max(serie, (d) => d[1]);
    const yScale = d3.scaleLinear()
      .domain([d3.min(series, stackMin), d3.max(series, stackMax)])
      .range([maxHeight, 0])
    
    

    构造 area 生成器:

    // area generator
    const area = d3.area()
      .x((d) => xScale(xAccessor(d)))
      .y0((d) => yScale(d[0]))
      .y1((d) => yScale(d[1]))
    
    

    为每个 serie 添加 area 图形:

    // add path 
    svg.selectAll('path.area')
      .data(series)
      .enter()
      .append('path')
      .attr('class', 'area')
      .attr('d', area)
      .attr('fill', (d, i) => colorArray[i % colorArray.length])
    
    

    实现

    Git地址

    完整源码如下:

    <!DOCTYPE html>
    <html>
      <body>
        <style>
          svg {
            border: 1px solid lightgrey;
          }
    
        </style>
        <script src="http://d3js.org/d3.v5.min.js"></script>
        <script type="text/javascript">
          const maxHeight = 400;
          const maxWidth = 600;
          const barWidth = 20;
    
          const colorArray = ['#38CCCB', '#0074D9', '#2FCC40', '#FEDC00', '#FF4036', 'lightgrey'];
    
          function singleArea() {
              const data = [
                {
                  date: '2019-01-01',
                  value: 1,
                },
                {
                  date: '2019-01-02',
                  value: 10,
                },
                {
                  date: '2019-01-03',
                  value: 35,
                },
              ];
    
            const xScale = d3.scaleTime()
              .domain(d3.extent(data, (d) => new Date(d.date)))
              .range([0, maxWidth]);
    
            const yScale = d3.scaleLinear()
              .domain(d3.extent(data, (d) => d.value))
              .range([maxHeight, 0])
    
            const area = d3.area()
              .x((d) => xScale(new Date(d.date)))
              .y1((d) => yScale(d.value))
              .y0((d) => yScale(0))
    
           const svg = d3.select('body')
             .append('svg')
             .attr('width', maxWidth)
             .attr('height', maxHeight)
    
            svg.append('g')
              .selectAll('path.area')
              .data([data])
              .enter()
              .append('path')
              .attr('class', 'area')
              .attr('d', area)
              .attr('fill', colorArray[0])
          }
    
          function stackArea() {
            const stackData = [
              {
                year: 2017,
                quarter: 2,
                samsung: 0.229,
                apple: 0.118,
                huawei: 0.110,
                oppo: 0.08,
                xiaomi: 0.062,
                others: 0.401,
              },
              {
                year: 2017,
                quarter: 3,
                samsung: 0.221,
                apple: 0.124,
                huawei: 0.104,
                oppo: 0.081,
                xiaomi: 0.075,
                others: 0.396,
              },
    
              {
                year: 2017,
                quarter: 4,
                samsung: 0.189,
                apple: 0.196,
                huawei: 0.107,
                oppo: 0.069,
                xiaomi: 0.071,
                others: 0.368,
              },
              {
                year: 2018,
                quarter: 1,
                samsung: 0.235,
                apple: 0.157,
                huawei: 0.118,
                oppo: 0.074,
                xiaomi: 0.084,
                others: 0.332,
              },
            ]
    
            const stack = d3.stack()
              .keys(['apple', 'samsung', 'huawei', 'oppo', 'xiaomi', 'others']);
    
            const series = stack(stackData);
    
            const stackMin = (serie) => d3.min(serie, (d) => d[0]);
            const stackMax = (serie) => d3.max(serie, (d) => d[1]);
            const yScale = d3.scaleLinear()
              .domain([d3.min(series, stackMin), d3.max(series, stackMax)])
              .range([maxHeight, 0])
    
          const xAccessor = (d) => `${d.year}-${d.quarter}`;
    
          const xScale = d3.scalePoint()
            .domain(stackData.map(xAccessor))
            .range([0, maxWidth])
    
          const area = d3.area()
            .x((d) => xScale(xAccessor(d.data)))
            .y0((d) => yScale(d[0]))
            .y1((d) => yScale(d[1]))
    
          const svg = d3.select('body')
            .append('svg')
            .attr('width', maxWidth)
            .attr('height', maxHeight)
    
          svg.append('g')
            .selectAll('path')
            .data(series)
            .enter()
            .append('path')
            .attr('d', area)
            .attr('fill', (d, i) => colorArray[i % colorArray.length])
          }
    
          singleArea()
          stackArea()
    
        </script>
      </body>
    </html>
    

    相关文章

      网友评论

          本文标题:D3数据可视化-Area

          本文链接:https://www.haomeiwen.com/subject/rdqsfctx.html