使用 d3.js 构建酷图表和可视化数据
一段时间以来,您可能已经看到了很多“covidtracker”或加密货币图表应用程序,其中一些提供了非常棒的图表,您很难使用“准备制作”图表的公共库来制作这些图表。
我必须承认......我喜欢玩数据,看到这些漂亮的 UI 和所有这些数字都呈现得如此出色让我嫉妒,我想:哦,我真的需要能够构建它!
所以我最近开始研究著名的 d3.js 库。
不要指望我在这里想出一些令人印象深刻的东西,不,但我希望有一个很好的基础来向您介绍使用 d3.js 进行数据可视化。
我们要构建的是按国家/地区划分的前 10 名人口的条形图:
演示
d3.js 是什么?
事实上d3.js不是一个图表库,它是一种操作 DOM 的大 API,并提供了很多实用功能。您可以操作 svg、canvas、html 等构建图表、图形、地图,以及数据可视化所需的一切。
使用 d3 的最大优势是,您在网络上看到的每一个很酷的数据可视化都可能使用 d3.js 是可行的。
主要缺点是 d3.js 一开始可能会让人不知所措,而且在我看来很难学习。
构建应用程序
准备项目:
我们不会在这里使用任何框架或特定配置,只是为了简单起见一些纯 javascript。
创建一个 index.html 文件并包含以下内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>d3.js Demo</title>
</head>
<body>
<h1>Top 10 population by country</h1>
<div id="chart"></div>
<div id="tooltip">
<h3 id="country_name"></h3>
<p id="country_population"></p>
</div>
<script src="https://d3js.org/d3.v6.js"></script>
<script src="index.js"></script>
</body>
</html>
进入全屏模式 退出全屏模式
我们正在导入 d3.js 库和 index.js 文件,该文件将包含我们的代码。
使用这些样式创建一个 style.css 文件:
* {
margin: 0;
box-sizing: border-box;
}
body {
box-sizing: border-box;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: Avenir, Helvetica, Arial, sans-serif;
}
svg {
background-color: #2a2a2e;
color: white;
border-radius: 5px;
}
h1 {
padding-bottom: 2rem;
}
#tooltip {
position: absolute;
visibility: hidden;
background-color: white;
font-size: .7rem;
border-radius: 5px;
padding: .3rem;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
#country_name {
padding: .3rem;
}
#country_population {
padding: .3rem;
}
.bar {
transition: .2s all;
}
进入全屏模式 退出全屏模式
最后,添加一个 index.js 文件。我们现在准备开始编码。
准备数据
我们将使用restcountryAPI。
我们想要在图表上显示的是人口最多的 10 个国家的列表,因此我们只需要每个国家的 x/y 轴的名称和人口。
让我们首先收集这些数据并将它们格式化为所需的形状:
const API_URL = 'https://restcountries.eu/rest/v2/all';
const getData = async () => {
const reponse = await fetch(API_URL);
const result = await reponse.json();
// Sort by population
const dataSort = result.sort((a, b) => b.population - a.population);
// only keep the top 10 population
dataSort.length = 10;
// We only need name + population
const dataReady = dataSort.map((country) => ({
name: country.name,
population: Math.floor(country.population / 1000000),
}));
return dataReady;
};
进入全屏模式 退出全屏模式
现在我们有了我们需要的东西:
https://i.gyazo.com/384c61698aae5dc6f39c86d8b5447150.png
构建图表:
创建主元素
创建一个新函数,它将在 d3 的帮助下负责生成条形图:
const generateChart = (popData) => {
const margin = {
top: 20,
right: 40,
bottom: 60,
left: 80,
};
const width = 1000 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;
// Create svg
const svgElement = d3
.select('#chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
}
进入全屏模式 退出全屏模式
注意我们如何使用“d3.select”来选择一个dom元素并链接其他方法,例如“append”和“attr”来构造我们的元素。这些确实是d3的基础。
边距、宽度和高度变量用于设置 svg 元素的位置,也将用于定位我们的轴。
创建轴
接下来让我们使用 d3-scale 方法创建我们的轴:
// Add X axis
const xScale = d3.scaleBand()
.range([0, width])
.domain(popData.map((s) => s.name))
.padding(0.2)
svgElement.append('g')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale));
// Add Y axis
const yScale = d3.scaleLinear()
.domain([popData[0].population, popData[9].population])
.range([0, height]);
svgElement.append('g')
.call(d3.axisLeft(yScale));
// Add grid
svgElement
.append('g')
.call(d3.axisLeft(yScale).ticks().tickSize(-width).tickFormat(''));
进入全屏模式 退出全屏模式
我们使用了比例模块的 d3 部分的几种实用方法来正确映射我们的轴与数据(scaleLinear、scaleBand)。
如果您打开导航器,您现在会看到一个带有两个轴但还没有数据的 svg 元素。
用条形表示数据
为了用 bar 表示我们的数据,我们只需创建矩形并将它们添加到我们的主 svg 元素中,再次使用正确的宽度和比例,这要归功于某些 d3-scale 方法(带宽、xScale、yScale):
// Draw the bars
svgElement
.append('g')
.selectAll('.bar')
.data(popData)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', (d) => xScale(d.name))
.attr('width', xScale.bandwidth())
.attr('y', (d) => yScale(d.population))
.attr('height', 0)
.style('fill', '#00FA9A')
.transition()
.duration(750)
.attr('height', (d) => height - yScale(d.population));
进入全屏模式 退出全屏模式
我们的图表现在正在运行,但让我们让它更“生动”一点。
添加工具提示和悬停效果:
我们希望在悬停一个栏时显示确切的人口,因此我们需要创建一个工具提示并在每个栏上添加鼠标事件。
请记住:在我们的 index.html 页面中有一个 div 元素,它带有一个工具提示 id,以及 css 中的一些样式。事实上一切都准备好了,工具提示在那里但被隐藏了,我们现在只需要添加鼠标事件:
// create a tooltip
const tooltip = d3.select('#tooltip');
const tooltip_name = d3.select('#country_name');
const tooltip_pop = d3.select('#country_population');
// Add mouse event to show the tooltip when hovering bars
d3.selectAll('.bar')
.on('mouseover', function () {
d3.select(this).style('fill', '#59ffb2');
tooltip.style('visibility', 'visible');
})
.on('mousemove', function (e, d) {
tooltip
.style('top', event.pageY - 10 + 'px')
.style('left', event.pageX + 10 + 'px');
tooltip_name.text(d.name);
tooltip_pop.text(`Population: ${d.population} Millions`);
})
.on('mouseout', function () {
d3.select(this).style('fill', '#00FA9A');
tooltip.style('visibility', 'hidden');
});
进入全屏模式 退出全屏模式
为轴添加文本标签:
// text label for the y axis
svgElement
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 0 - margin.left)
.attr('x', 0 - height / 2)
.attr('dy', '1em')
.style('text-anchor', 'middle')
.style('fill', 'white')
.text('Population (in millions)');
// text label for the y axis
svgElement
.append('text')
.attr('y', height + 30)
.attr('x', 0 + width / 2)
.attr('dy', '1em')
.style('text-anchor', 'middle')
.style('fill', 'white')
.text('Country name');
进入全屏模式 退出全屏模式
执行代码:
在主范围内简单地执行我们的功能
getData().then(generateChart);
进入全屏模式 退出全屏模式
你去了,现在你应该有这个结果。
如果你想检查整个代码:这里
数据可视化领域真的有很多可能性和东西可以构建,只是好奇和探索!
我希望我已经足够清楚,它可以帮助您理解图表构造以创建更好的 UI。
祝你有美好的一天!
更多推荐
所有评论(0)