Js+SVG 柱状图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
svg,
.container {
box-sizing: border-box;
padding: 8px;
border-radius: 3px;
border: 1px solid #ddd;
}
svg {
background-color: #f1f1f1;
}
.container {
background-color: #fff;
}
</style>
</head>
<body>
<div class="container"></div>
<script>
onload = function () {
let width = '100%',
height = 256,
barHeight = 16;
let dataset = new Array(15);
for (let i = 0; i < dataset.length; i++) {
(dataset[i] = Number.parseInt(Math.random() * 1000, 10));
}
let body = document.querySelector('.container');
let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', width);
svg.setAttribute('height', height);
dataset.forEach((v, idx) => {
let r = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
r.setAttribute('y', barHeight * idx);
r.setAttribute('width', v);
r.setAttribute('height', barHeight - 4);
r.setAttribute('fill', '#369');
r.setAttribute('ry', '2px');
r.setAttribute('fill-opacity', 0.85);
svg.appendChild(r);
let t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
t.setAttribute('x', v + 4);
t.setAttribute('y', (idx + 1) * barHeight - 6);
t.setAttribute('font-size', '0.75em');
t.textContent = v;
svg.appendChild(t);
});
body.appendChild(svg);
};
</script>
</body>
</html>
D3+SVG 柱状图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<style>
svg,
.container {
box-sizing: border-box;
padding: 8px;
border-radius: 3px;
border: 1px solid #ddd;
}
svg {
background-color: #f1f1f1;
}
.container {
background-color: #fff;
}
</style>
<div class="container"></div>
<script src="../../source/d3.js"></script>
<script>
let width = '100%',
height = 256,
barHeight = 16;
let dataset = new Array(15);
for (let i = 0; i < dataset.length; i++) {
(dataset[i] = Number.parseInt(Math.random() * 1000, 10));
}
let svg = d3.select('.container').append('svg');
svg.attr('width', width)
.attr('height', height);
let rectes = svg.selectAll('rect')
.data(dataset)
.enter()
.append('rect');
rectes
.attr('y', (item, idx) => idx * barHeight)
.attr('width', item => item)
.attr('height', barHeight - 4)
.attr('ry', '2px')
.attr('fill-opacity', 0.85)
.attr('fill', '#369')
let title = svg.selectAll('text')
.data(dataset)
.enter()
.append('text');
title.attr('x', item => item + 4)
.attr('y', (_, idx) => (idx + 1) * barHeight - 6)
.attr('font-size', '0.75em')
.text(item => item)
</script>
</body>
</html>
Js vs D3
两个段代码实现的效果是相同的.
总的来说, 通过比较上述代码, 可以看的出来:
- 创建 SVG 元素
Js 创建一个 SVG 元素相对复杂, 需要指定 namespaceURI
, 通常这个值等于 http://www.w3.org/2000/svg(也可以点击网址查看更多信息), 写作 document.createElementNS('http://www.w3.org/2000/svg', 'svg');
虽说看起来挺长的, 不过总的来说也并不是那么复杂就是了. 另外, 关于 namespaceURI, vscode 的提示信息内可以复制到, 其他编辑器并未测试.
D3 创建一个 SVG 元素就很简单了, Selection.append('svg');
.
- 编程方式
Js 是普通的逐行编写.
D3 看起来更加倾向于链式.
- 批量生成元素
Js 需要书写遍历函数, 在循环体内编写创建 SVG 元素的代码.
D3 不需要我们自己来写遍历函数, 采用了 .data(dataset).enter().append(元素名)
的方式自动补全元素.
- 补齐元素(本例并不明显)
这个所谓的补齐元素的概念, 其出发点可能是为了性能优化吧, 具体来讲就是:
当文档中已经存在了 N 个 rect 元素(就拿 rect 元素举例), 然后由于某种原因, 原本对应这 N 个 rect 元素的数组发生了改变, 变成了 M 个.
此时如果不考虑那么多, 直接移除所有的 rect 在重新遍历一遍显然也不是不行! 不过总感觉如此大量的删/插元素并不是什么好的选择.
可能是因此, D3 才提出了补齐/删齐元素的说法, 以求尽量少的删/插元素吧.
总之此时:
Js 可能需要计算了, 比如现有 N 个元素, M 个数据; 需要划分 3 中情况来分别进行编写.
D3 提供了一段通用的代码
// Update 情形2
var p = d3.select("body")
.selectAll("p")
.data([4, 8, 15, 16, 23, 42])
.text(function(d) { return d; });
// Enter 情形1
p.enter().append("p")
.text(function(d) { return d; });
// Exit 情形3
p.exit().remove();
结语
就制作一个简单的条形图而言, 还没有特别强烈的感到使用 D3 能够带来莫大的好处.