本文基于 D3.js 做者
Mike Bostock
的 例子javascript
原文分为三部分, 在这里笔者将其整合为了一篇方便阅读.html
该效果基于 D3.js, 主要使用到了 d3-selection. 若是对d3-selection的基本使用逻辑不太清楚能够参见 这篇文章.java
Step1 首先代码会随机生成一个字符串, 该字符以绿色进入画面.git
Step2 接下来, 代码随机生成一个新字符串, 新生成的字符串会和原始字符串进行对比:github
2.1 新字符串和原始字符串中相同的字母,会变成黑色保留在屏幕上app
2.2 原始字符串中有, 而新字符串中没有的字母, 会变成红色,被移除屏幕dom
2.3 新字符串中有, 而原始字符串中没有的字母, 会变成绿色,被添加到屏幕svg
<script> var alphabet = "abcdefghijklmnopqrstuvwxyz".split(""); var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"), g = svg.append("g").attr("transform", "translate(32," + (height / 2) + ")"); function update(data) { // DATA JOIN // Join new data with old elements, if any. var text = g.selectAll("text") .data(data); // UPDATE // Update old elements as needed. text.attr("class", "update"); // ENTER // Create new elements as needed. // // ENTER + UPDATE // After merging the entered elements with the update selection, // apply operations to both. text.enter().append("text") .attr("class", "enter") .attr("x", function(d, i) { return i * 32; }) .attr("dy", ".35em") .merge(text) .text(function(d) { return d; }); // EXIT // Remove old elements as needed. text.exit().remove(); } // The initial display. update(alphabet); // Grab a random sample of letters from the alphabet, in alphabetical order. d3.interval(function() { update(d3.shuffle(alphabet) .slice(0, Math.floor(Math.random() * 26)) .sort()); }, 1500); </script>
复制代码
代码不长, 接下来一步步分析代码逻辑:post
首先, 获取svg的宽高信息. 并建立一个
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(32," + (height / 2) + ")");
复制代码
在 update()
方法中, 咱们传进来一个字符串 data ** (data由26个字母中随机选出一些组成). 首先咱们选中全部的 text 元素并将其和data** 进行绑定:
var text = g.selectAll("text")
.data(data);
复制代码
而后咱们处理enter集合 (也就是新加入的字符), 为每个新字符添加一个 text 元素, 并为其添加class属性, 使用index为其计算出横纵坐标:
text.enter().append("text") // 添加svg text元素
.attr("class", "enter") // 添加class属性 (绿色)
.attr("x", function(d, i) { return i * 32; }) // 按每一个字符32像素, 为其计算x坐标
.attr("dy", ".35em") // 设置y轴偏移量
复制代码
接下来同时处理 enter 和 update 集合, 将字符数据填入 text 元素中:
.merge(text)
.text(function(d) { return d; });
复制代码
最后处理 exit集合, 咱们暂时直接将其从屏幕中移除:
text.exit().remove();
复制代码
咱们用 d3.interval(callback, timeInterval)
定时调用update()
来实现字符刷新. 如今咱们就获得了下面的效果:
若是你观察仔细的话, 你可能已经发现第一步实现的效果中: 新加的字符老是出如今最后. 这显然不是咱们想要的效果, 新加的字符出现的位置应该是随机的. 那么出现如今这个效果的缘由是什么呢?
答案在于咱们在绑定字符数据时: data(data)
并无指定data的key值, 那么d3会默认使用index做为key, 这样就是为何新增长的字符老是出如今最后面.
因此咱们为字符加入key值的accessor:
var text = g.selectAll("text")
.data(data, function(d) { return d; });
复制代码
如今 key 值绑定好后, 已经存在的字符在update时text已经不会再改变, 可是坐标须要从新计算, 因此咱们作如下改动:
text.enter().append("text")
.attr("class", "enter")
.attr("dy", ".35em")
.text(function(d) { return d; }) // 将text赋值移动到 enter中
.merge(text)
.attr("x", function(d, i) { return i * 32; }); // 将坐标计算移动到 merge后 (enter & update)
复制代码
如今咱们的代码长这样, 点我在线运行:
<script> var alphabet = "abcdefghijklmnopqrstuvwxyz".split(""); var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"), g = svg.append("g").attr("transform", "translate(32," + (height / 2) + ")"); function update(data) { // DATA JOIN // Join new data with old elements, if any. var text = g.selectAll("text") .data(data, function(d) { return d; }); // UPDATE // Update old elements as needed. text.attr("class", "update"); // ENTER // Create new elements as needed. // // ENTER + UPDATE // After merging the entered elements with the update selection, // apply operations to both. text.enter().append("text") .attr("class", "enter") .attr("dy", ".35em") .text(function(d) { return d; }) .merge(text) .attr("x", function(d, i) { return i * 32; }); // EXIT // Remove old elements as needed. text.exit().remove(); } // The initial display. update(alphabet); // Grab a random sample of letters from the alphabet, in alphabetical order. d3.interval(function() { update(d3.shuffle(alphabet) .slice(0, Math.floor(Math.random() * 26)) .sort()); }, 1500); </script>
复制代码
如今咱们获得的效果:
动画能让咱们更好的观察元素的变化过程和状态, 给不一样状态的元素赋予不一样的动画能够更直观的展现咱们的数据.
如今咱们给字符的变化增长动画效果, 并把字符移除时的颜色变化补上.
首先咱们定义一个 transition变量, 并设置其动画间隔为 750
var t = d3.transition()
.duration(750);
复制代码
在D3中使用动画很是简单, 在动画前指定元素的一些属性, 调用动画, 再指定动画后的一些属性. D3会自动根据插值器生成动画.
下面的代码对于离开界面的字符(exit selection)进行了处理:
text.exit()
.attr("class", "exit") // 动画前, 设置class属性, 字体变红
.transition(t) // 设置动画
.attr("y", 60) // 设置y坐标, 使元素向下离开界面 (y: 0 => 60)
.style("fill-opacity", 1e-6) // 设置透明度, 使元素渐变消失 (opacity: 1 => 0)
.remove(); // 最后将其移出界面
复制代码
一样的, 咱们对 enter 和 update selection进行处理:
// UPDATE old elements present in new data.
text.attr("class", "update")
.attr("y", 0)
.style("fill-opacity", 1)
.transition(t)
.attr("x", function(d, i) { return i * 32; });
// ENTER new elements present in new data.
text.enter().append("text")
.attr("class", "enter")
.attr("dy", ".35em")
.attr("y", -60)
.attr("x", function(d, i) { return i * 32; })
.style("fill-opacity", 1e-6)
.text(function(d) { return d; })
.transition(t)
.attr("y", 0)
.style("fill-opacity", 1);
复制代码
最终咱们的代码长这样, 点我运行
<script>
var alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(32," + (height / 2) + ")");
function update(data) {
var t = d3.transition()
.duration(750);
// JOIN new data with old elements.
var text = g.selectAll("text")
.data(data, function(d) { return d; });
// EXIT old elements not present in new data.
text.exit()
.attr("class", "exit")
.transition(t)
.attr("y", 60)
.style("fill-opacity", 1e-6)
.remove();
// UPDATE old elements present in new data.
text.attr("class", "update")
.attr("y", 0)
.style("fill-opacity", 1)
.transition(t)
.attr("x", function(d, i) { return i * 32; });
// ENTER new elements present in new data.
text.enter().append("text")
.attr("class", "enter")
.attr("dy", ".35em")
.attr("y", -60)
.attr("x", function(d, i) { return i * 32; })
.style("fill-opacity", 1e-6)
.text(function(d) { return d; })
.transition(t)
.attr("y", 0)
.style("fill-opacity", 1);
}
// The initial display.
update(alphabet);
// Grab a random sample of letters from the alphabet, in alphabetical order.
d3.interval(function() {
update(d3.shuffle(alphabet)
.slice(0, Math.floor(Math.random() * 26))
.sort());
}, 1500);
</script>
复制代码
最终效果:
这里是个人 D3.js 、 数据可视化 的github 地址, 欢迎 start & fork :tada: