用 d3.js 做一个简单的柱状

公司需要做一个和设计稿相同的图表,但是现有的 echarts 等图标库没有类似的东西,所以自己做了一个。

由于上次用 oCanvas 做的有性能问题,这次用 d3.js(svg)又重做了一遍。上次的文章可以看自定义 oCanvas 图形 - 自制柱状图

# 引用 d3.js 框架

<script src="https://cdn.bootcss.com/d3/4.12.2/d3.js"></script>
1

# dom

<div id="svg"></div>
1

# 编写

里面除了用到 SVG 的一些知识外,还用了 d3 中插值和过渡动画。

const initChart = (el, width, height, value, frontColor) => {
  // const width = 50;
  // const height = 500;
  // const value = 60;
  const backgroundColor = "rgba(34,151,255,0.5)";
  // const frontColor = 'rgb(34,151,255)';
  const lineColor = "rgb(255,255,255,0.5)";
  const lineWidth = 1;

  const points = [[width / 2, height], [width, height - width / 2], [width, 0], [0, 0], [0, height - width / 2]];
  const points2string = points => points.map(arr => arr.join(",")).join(" ");

  const svg = d3
    .select(el)
    .append("svg")
    .attr("width", width)
    .attr("height", height);
  const clipPath = svg
    .append("defs")
    .append("clipPath")
    .attr("id", "clipPath");
  clipPath.append("polygon").attr("points", points2string(points));

  const chart = svg.append("g").attr("clip-path", "url(#clipPath)");

  const background = chart
    .append("rect")
    .attr("fill", backgroundColor)
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", width)
    .attr("height", height);
  const frontHeight = (value / 100) * height;
  const front = chart
    .append("rect")
    .attr("fill", frontColor)
    .attr("x", 0)
    .attr("y", height - frontHeight)
    .attr("width", width)
    .attr("height", frontHeight);

  const text = chart
    .append("text")
    .attr("text-anchor", "middle")
    .attr("fill", "#fff")
    .attr("x", width / 2)
    .attr("dy", 5)
    .attr("y", height - frontHeight)
    .text(value + "%");

  for (let i = 1; i <= 10; i++) {
    const p = [[0, (height / 10) * i], [width, (height / 10) * i]];
    chart
      .append("polyline")
      .attr("points", points2string(p))
      .attr("stroke", lineColor)
      .attr("stroke-width", lineWidth);
  }
  front.setValue = value => {
    const frontHeight = (value / 100) * height;
    const oldValue = parseInt(text.text());
    front
      .transition()
      .duration(1000)
      .attr("y", height - frontHeight)
      .attr("height", frontHeight);
    text
      .transition()
      .duration(1000)
      .attr("y", height - frontHeight)
      .tween("y", () => {
        const i = d3.interpolate(oldValue, value);
        return t => text.text(i(t).toFixed(0) + "%");
      });
  };

  return front;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

# 演示代码

const chart = initChart("#svg", 50, 500, 60, "rgb(34,151,255)");
setInterval(function() {
  chart.setValue(Math.random() * 100);
}, 1000);
1
2
3
4