Skip to content

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

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

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

引用 d3.js 框架

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

dom

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

编写

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

javascript
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;
};
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;
};

演示代码

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

最后编辑时间:

Version 4.0 (framework-1.0.0-rc.20)