1
头图

前言

上一篇文章 我们讲了 D3.js 中一些常见的 API,并且通过一个 demo 了解了 D3.js 是如何工作的。铺垫完之后,这篇文章我们正儿八经的来实现一个柱状图。

正文

比例尺

比例尺是 D3.js 中一个重要的概念,它用于在值和坐标之间的相互转换。本文中出现的相关 API 如下👇

API说明
scaleBand一般用于将一组离散值映射到连续范围的值
scaleLinear线性转换,将连续的输入域映射到连续的输出范围

想象一下,我们需要在一个 800 x 600 宽高的容器内绘制一个坐标系。通常情况下,坐标的数值不会与实际的宽高完全对应。因此,我们需要一种工具来将实际的宽高与坐标系中的数值进行映射。

对于 scaleLinear 这个线性转换函数,可以用下面的公式来描述:

$y = mx + b(b 为任意常量)$

通过输入 $x$(通常是一个数值),我们可以获得元素在页面上的高度或宽度。

绘制坐标系

首先我们先添加初始代码:引入 D3.js & 设置基础样式

<html>
<head>
    <title>Bar</title>
    <style>
        #bar {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
<div id="bar"></div>
</body>
<script src="<https://d3js.org/d3.v7.min.js>"></script>
</html>

紧接着我们来创建一个 svg,宽高为:800 x 600

const width = 800;
  const height = 600;
  const data = Array.from({length: 26}).map((_, i) => ({
    name: String.fromCharCode(65 + i),
    value: Math.random() * 100
  }))

  const svg = d3.select('#bar')
    .append('svg')
    .attr('width', width)
    .attr('height', height)

此时页面上依旧是光秃秃的一片,没关系,我们先把 x 轴加上:

const xScale = d3.scaleBand().domain(data.map(item => item.name)).range([0, 500]).padding(0.1)
const xAxis = d3.axisBottom(xScale)
svg.append('g').call(xAxis)

此时,页面已经出现了我们需要的 x 轴

image.png
随后我们把 y 轴也加到页面上:

const yScale = d3.scaleLinear().domain([0, 100]).range([500, 0])
const yAxis = d3.axisLeft(yScale)
svg.append('g').call(yAxis)

此时页面就会出现一个奇怪的坐标系😱😱😱

当然如果你看完了上一篇文章,你就知道这是因为坐标原点在左上角导致的🤔

image.png
因此我们在绘制 x 轴的时候还需要把它的位置调整一下

svg.append('g').attr('transform', `translate(0, 500)`).call(xAxis

那么写到这里,我们就得到了一个长得挺好看的坐标系

image.png

添加柱状图

添加坐标系之后,我们就可以开始添加柱状图了。

在此之前有一些细节需要说明:我们可以利用 xScaleyScale 来根据网页上实际需要展示的值,来决定柱状图的展示位置。比如,下面这段代码:

const xScale = d3.scaleBand().domain(data.map(item => item.name)).range([0, 500]).padding(0.1)

执行之后,我们可以通过 name → value 的映射,来获取 x 轴上每一个离散点的具体位置,同理 y 轴也是如此。

根据这点,我们就不需要再花太多时间计算每个矩形的的 x、y 坐标了,而是能直接通过 xScaleyScale 这些函数来进行简单的换算来达到我们想要的效果。代码如下:

svg.selectAll('rect')
    .data(data)
    .join('rect')
    .attr('x', item => xScale(item.name))
    .attr('y', item => yScale(item.value))
    .attr('width', xScale.bandwidth())
    .attr('height', item => 500 - yScale(item.value))
    .attr('fill', 'steelblue')

效果如下👇

image.png

补充动画

D3.js 中的动画逻辑是这样的:

  1. 设置初始值,比如 x、y
  2. 添加动画配置,比如动画函数、持续时间等等
  3. 设置最终值,比如 widthheight
svg.selectAll('rect')
    .data(data)
    .join('rect')
    // 设置 x 坐标,防止动画时出现抖动
    .attr('x', item => xScale(item.name))
    .attr('y', 500)
    // 设置宽度,柱状图的宽度在这里一直保持不变即可
    .attr('width', xScale.bandwidth())
    // 为了有动画效果,这里先设置高度为 0
    .attr('height', 0)
    .transition()
    // 设置延迟
    .delay((item, i) => i * 20)
    // 添加动画函数
    .ease(d3.easeCubic)
    // 设置动画时长
    .duration(1000)
    // 最终的效果
    .attr('y', item => yScale(item.value))
    .attr('height', item => 500 - yScale(item.value))
    .attr('fill', 'steelblue')

2023-02-09 231642.gif

总结

本文中我们了解了比例尺并且熟悉了 D3.js 中一些与之相关的 API ,并且通过一系列步骤了解了怎么通过 D3.js 来实现一个柱状图。

想要了解更多前端知识,欢迎关注我的公众号:tony老师的前端补习班

系列文章:


tonychen
1.2k 声望272 粉丝