学习D3.js(五)基础折线图

703 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情

开始绘制

引入D3模块

<!-- 选择器模块 -->
  <script src="https://cdn.jsdelivr.net/npm/d3-selection@3"></script>

  <!-- 比例尺模块 和 依赖 -->
  <script src="https://cdn.jsdelivr.net/npm/d3-array@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-color@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-format@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-interpolate@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-time@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-time-format@4"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-scale@4"></script>

  <!-- 坐标轴 -->
  <script src="https://cdn.jsdelivr.net/npm/d3-axis@3"></script>

  <!-- 形状 -->
  <script src="https://cdn.jsdelivr.net/npm/d3-path@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-shape@3"></script>

  <!-- 动画 -->
  <script src="https://cdn.jsdelivr.net/npm/d3-dispatch@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-ease@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-timer@3"></script>
  <script src="https://cdn.jsdelivr.net/npm/d3-transition@3"></script>

数据

  • 数据格式随意,方便后面使用。
 var dataArr = [
  {
    label: '1月',
    value: 10.5
  },
  {
    label: '2月',
    value: 70.5
  },
  {
    label: '3月',
    value: 60.5
  },
  {
    label: '4月',
    value: 10.5
  },
  {
    label: '5月',
    value: 20.5
  },
  {
    label: '6月',
    value: 30.5
  }
]

添加画布

  • 初始化画布。
var svg = d3
  .select('.d3Chart')
  .append('svg')
  .attr('width', width)
  .attr('height', height)
  .style('background-color', '#1a3055')
// 组
var chart = svg.append('g').attr('transform', `translate(${margin * 2}, ${margin})`)

创建比例尺

  • 根据需求创建比例迟。
// 分段比例尺
// 把X轴长度 分成多段
var xScale = d3
  .scaleBand()
  .range([0, 400])
  .domain(dataArr.map((s) => s.label))

// 线性比例尺
// 把Y轴长度 转换为100
var yScale = d3.scaleLinear().range([400, 0]).domain([0, 100])

绘制坐标轴

  • 根据定义好的比例尺,使用axis模块自动绘制坐标轴。
  • 绘制好坐标轴后,在坐标轴组中绘制文本标签。
    // 坐标轴
    const xAxis = d3.axisBottom(xScale)
    chart.append('g').attr('class', 'xAxis').attr('transform', `translate(0, ${400})`).call(xAxis)
    const yAxis = d3
      .axisLeft()
      .scale(yScale)
      .tickFormat((d) => {
        return d + '%'
      })
    chart.append('g').attr('transform', 'translate(0, 0)').call(yAxis)

    // 标签
    d3.select('.xAxis')
      .append('text')
      .attr('x', 400 / 2 - 12)
      .attr('y', 0)
      .attr('dy', 45)
      .style('font-size', '24px')
      .text('日期')

    d3.selectAll('.d3Chart text').style('fill', '#fff')
    d3.selectAll('.d3Chart line').style('stroke', '#fff')
    d3.selectAll('.d3Chart path').style('stroke', '#fff')

image.png

绘制折线

  • 创建线形状计算方法。
let line = d3
  .line()
  .x(function (d) {
    return xScale(d.label)
  })
  .y(function (d) {
    return yScale(d.value)
  })
  .curve(d3.curveCardinal) // 曲线效果
  1. d3.line() 创建线生成器。
  2. .x()、.y() 设置线生成器中点X、y的坐标。
  3. .curve() 设置生成曲线。
  4. d3.curveCardinal 生成一条三次曲线。
var lineChart = chart.append('g')
  • 创建绘制组(g),线和点都绘制在组中。
// 创建 path 路径 设置 d属性 和 线的位置
lineChart
  .selectAll()
  .data([dataArr])
  .enter()
  .append('path')
  .attr('class', (d, index) => 'path' + index)
  .attr('d', function (d) {
    return line(d)
  })
  .attr('stroke', '#2e6be6')
  .attr('fill', 'none')
  .attr('transform', `translate(${xScale.bandwidth() / 2}, 0)`)
  • 绑定数据。一开始只创建了一条折线的数据,这里创建数组放入折线数据。
  • 添加标识符,用于后面操作。
  • 使用线生成器,生成线的坐标。
// 在点位置 画圆
lineChart
  .selectAll()
  .data([dataArr])
  .enter()
  .append('g')
  .attr('class', (d, index) => 'circleG' + index)
  .selectAll()
  .data((d) => d)
  .enter()
  .append('circle')
  .attr('cx', function (d) {
    return xScale(d.label)
  })
  .attr('cy', function (d) {
    return yScale(d.value)
  })
  .attr('r', 4)
  .attr('transform', `translate(${xScale.bandwidth() / 2}, 0)`)
  .attr('fill', '#fff')
  .attr('stroke', 'rgba(56, 8, 228, .5)')
  • 绑定了两次数据。因为点数据是分开,我们把一条折线的点放在一组。方便后面操作。
  • 给组设置class标识,用于分组操作点数据。
  • 使用比例尺,获取点的坐标。设置点属性。

image.png

添加动画

// stroke-dasharray 虚线长度
// stroke-dashoffset 虚线偏移量
svg
  .selectAll('path.path0')
  .style('stroke-dasharray', function () {
    //
    return d3.select(this).node().getTotalLength()
  })
  .style('stroke-dashoffset', function () {
    return d3.select(this).node().getTotalLength()
  })
  .transition()
  .duration(2000)
  .ease(d3.easeLinear)
  .style('stroke-dashoffset', 0)
  1. d3.select(this).node().getTotalLength() 选中当前元素获取元素节点对象。使用SVG节点方法,获取路径总长度。
  2. .transition() 创建平滑的补间动画。简单理解就是,缓慢的把前面设置的属性,修改为后面设置的属性。
  3. .duration() 设置动画时长。
  4. .ease() 设置动画过度函数。
  5. d3.easeLinear 恒等函数,使用相同的速度播放动画。
  • 通过选择器,获取折线path元素。
  • 设置虚线长度和偏移量为本身长度。
  • 使用.transition()创建补间动画,修改虚线偏为0,形成线段移动动画。
  • 路径描边动画
//圆点
svg
  .selectAll('.circleG0 > circle')
  .attr('r', 0)
  .style('stroke-width', 0)
  .transition()
  .duration(300)
  .delay(function (d, i) {
    return (i * 2000) / 5
  })
  .ease(d3.easeLinear)
  .attr('r', 4)
  .style('stroke-width', 3)
  1. .delay() 动画延迟时间。

4.gif

  • 通过选择器,获取折线的所有点。
  • 计算每个动画的,等待时间完善动画。
  • 一个基础折线图就完成了。
  • 代码地址