变换
单元素动画
<script type="text/javascript">var body = d3.select("body"),duration = 5000;body.append("div").classed("box", true).style("background-color", "#e9967a").transition().duration(duration).style("background-color", "#add8e6").style("margin-left", "600px").style("width", "100px").style("height", "100px");</script>

如果没有起始值或者结束值,D3 会自动计算一个值作为过渡的有起始值或者结束值。
多元素动画
<script type="text/javascript">var id= 0,data = [],duration = 500,chartHeight = 100,chartWidth = 680;for(var i = 0; i < 20; i++) push(data);function render(data) {var selection = d3.select("body").selectAll("div.v-bar").data(data, function(d){return d.id;});// enterselection.enter().append("div").attr("class", "v-bar").style("z-index", "0").style("position", "fixed").style("top", chartHeight + "px").style("left", function(d, i){return barLeft(i+1) + "px";}).style("height", "0px").append("span");// updateselection.transition().duration(duration).style("top", function (d) {return chartHeight - barHeight(d) + "px";}).style("left", function(d, i){return barLeft(i) + "px";}).style("height", function (d) {return barHeight(d) + "px";}).select("span").text(function (d) {return d.value;});// exitselection.exit().transition().duration(duration).style("left", function(d, i){return barLeft(-1) + "px";}).remove();}function push(data) {data.push({id: ++id,value: Math.round(Math.random() * chartHeight)});}function barLeft(i) {return i * (30 + 2);}function barHeight(d) {return d.value;}setInterval(function () {data.shift();push(data);render(data);}, 2000);render(data);d3.select("body").append("div").attr("class", "baseline").style("position", "fixed").style("z-index", "1").style("top", chartHeight + "px").style("left", "0px").style("width", chartWidth + "px");</script>

缓动动画
<script type="text/javascript">var data = [{name: 'Linear', fn: d3.easeLinear},{name: 'Cubic', fn: d3.easeCubic},{name: 'CubicIn', fn: d3.easeCubicIn},{name: 'Sin', fn: d3.easeSin},{name: 'SinIn', fn: d3.easeSinIn},{name: 'Exp', fn: d3.easeExp},{name: 'Circle', fn: d3.easeCircle},{name: 'Back', fn: d3.easeBack},{name: 'Bounce', fn: d3.easeBounce},{name: 'Elastic', fn: d3.easeElastic},{name: 'Custom', fn: function(t){ return t * t; }}],colors = d3.scaleOrdinal(d3.schemeCategory20);d3.select("body").selectAll("div").data(data).enter().append("div").attr("class", "fixed-cell").style("top", function (d, i) {return i * 40 + "px";}).style("background-color", function (d, i) {return colors(i);}).style("color", "white").style("left", "500px").text(function (d) {return d.name;});d3.selectAll("div").each(function(d){d3.select(this).transition().ease(d.fn) // <-D.duration(1500).style("left", "10px");});</script>

使用中间帧计算
<script type="text/javascript">var body = d3.select("body"), duration = 5000;body.append("div").append("input").attr("type", "button").attr("class", "countdown").attr("value", "0").style("width", "150px").transition().duration(duration).ease(d3.easeLinear).style("width", "400px").attr("value", "9");body.append("div").append("input").attr("type", "button").attr("class", "countdown").attr("value", "0").transition().duration(duration).ease(d3.easeLinear).styleTween("width", widthTween).attrTween("value", valueTween);function widthTween(a){var interpolate = d3.scaleQuantize().domain([0, 1]).range([150, 200, 250, 350, 400]);return function(t){return interpolate(t) + "px";};}function valueTween(){var interpolate = d3.scaleQuantize().domain([0, 1]).range([1, 2, 3, 4, 5, 6, 7, 8, 9]);return function(t){return interpolate(t);};}</script>

在 D3 中,tween 函数是一个工厂函数,用来构造执行中间帧的最终函数。
使用级联过渡
<script type="text/javascript">var body = d3.select("body");function teleport(s){s.transition().duration(1000).style("width", "200px").style("height", "1px").transition().duration(500).style("left", "600px").transition().duration(1000).style("left", "800px").style("height", "80px").style("width", "80px");}body.append("div").style("position", "fixed").style("background-color", "steelblue").style("left", "10px").style("width", "80px").style("height", "80px").call(teleport);</script>

使用选择性过渡
<script type="text/javascript">var data = ["Cat", "Dog", "Cat", "Dog", "Cat", "Dog", "Cat", "Dog"],duration = 1500;d3.select("body").selectAll("div").data(data).enter().append("div").attr("class", "fixed-cell").style("top", function (d, i) {return i * 40 + "px";}).style("background-color", "steelblue").style("color", "white").style("left", "500px").text(function (d) {return d;}).transition().duration(duration).style("left", "10px").filter(function(d){return d == "Cat";}).transition().duration(duration).style("left", "500px");</script>

监听过渡事件
<script type="text/javascript">var body = d3.select("body"), duration = 3000;var div = body.append("div").classed("box", true).style("background-color", "steelblue").style("color", "white").text("waiting").transition().duration(duration).delay(1000).on("start", function(){d3.select(this).text(function (d, i) {return "transitioning";});}).on("end", function(){d3.select(this).text(function (d, i) {return "done";});}).style("margin-left", "600px");</script>

使用定时器
<script type="text/javascript">var body = d3.select("body");var countdown = body.append("div").append("input");countdown.attr("type", "button").attr("class", "countdown").attr("value", "0");function countUp(target){var t = d3.timer(function(){var value = countdown.attr("value");if( value == target ) {t.stop();return true;}countdown.attr("value", ++value);});}function reset(){countdown.attr("value", 0);}</script><div class="control-group"><button onclick="countUp(100)">Start</button><button onclick="reset()">Clear</button></div>

形状
什么是 SVG
- 矢量:矢量的图形更适合可视化
- 可伸缩性:任意尺寸和放大程度下都不会失真
- 可读性
- 开放标准
- 应用广泛
- 互用性
- 轻量
创建简单图形
<style>svg line{stroke: grey;stroke-width: 2;}svg circle{stroke: red;fill: none;stroke-width: 2;}svg rect{stroke: steelblue;fill: none;stroke-width: 2;}svg polygon{stroke: green;fill: none;stroke-width: 2;}</style><script type="text/javascript">var width = 600,height = 500;var svg = d3.select("body").append("svg");svg.attr("height", height).attr("width", width);svg.append("line").attr("x1", 0).attr("y1", 200).attr("x2", 100).attr("y2", 100);svg.append("circle").attr("cx", 200).attr("cy", 150).attr("r", 50);svg.append("rect").attr("x", 300).attr("y", 100).attr("width", 100).attr("height", 100).attr("rx", 5);svg.append("polygon").attr("points", "450,200 500,100 550,200");</script>

D3 还支持椭圆(ellipse)和折线(ployline)。
使用线条生成器
<script type="text/javascript">var width = 500,height = 500,margin = 50,x = d3.scaleLinear().domain([0, 10]).range([margin, width - margin]),y = d3.scaleLinear().domain([0, 10]).range([height - margin, margin]);var data = [[{x: 0, y: 5},{x: 1, y: 9},{x: 2, y: 7},{x: 3, y: 5},{x: 4, y: 3},{x: 6, y: 4},{x: 7, y: 2},{x: 8, y: 3},{x: 9, y: 2}],d3.range(10).map(function(i){return {x: i, y: Math.sin(i) + 5};})];var line = d3.line().x(function(d){return x(d.x);}).y(function(d){return y(d.y);});var svg = d3.select("body").append("svg");svg.attr("height", height).attr("width", width);svg.selectAll("path").data(data).enter().append("path").attr("class", "line").attr("d", function(d){return line(d);});renderAxes(svg);function renderAxes(svg){var xAxis = d3.axisBottom().scale(x.range([0, quadrantWidth()]));var yAxis = d3.axisLeft().scale(y.range([quadrantHeight(), 0]));svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart()+ "," + yStart() + ")";}).call(xAxis);svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart()+ "," + yEnd() + ")";}).call(yAxis);}function xStart(){return margin;}function yStart(){return height - margin;}function xEnd(){return width - margin;}function yEnd(){return margin;}function quadrantWidth(){return width - 2 * margin;}function quadrantHeight(){return height - 2 * margin;}</script>

使用曲线
<script type="text/javascript">var width = 500,height = 500,margin = 30,x = d3.scaleLinear().domain([0, 10]).range([margin, width - margin]),y = d3.scaleLinear().domain([0, 10]).range([height - margin, margin]);var data = [[{x: 0, y: 5},{x: 1, y: 9},{x: 2, y: 7},{x: 3, y: 5},{x: 4, y: 3},{x: 6, y: 4},{x: 7, y: 2},{x: 8, y: 3},{x: 9, y: 2}],d3.range(10).map(function(i){return {x: i, y: Math.sin(i) + 5};})];var svg = d3.select("body").append("svg");svg.attr("height", height).attr("width", width);renderAxes(svg);render(d3.curveLinear);renderDots(svg);function render(mode){var line = d3.line().x(function(d){return x(d.x);}).y(function(d){return y(d.y);}).curve(mode);svg.selectAll("path.line").data(data).enter().append("path").attr("class", "line");svg.selectAll("path.line").data(data).attr("d", function(d){return line(d);});}function renderDots(svg){data.forEach(function(list){svg.append("g").selectAll("circle").data(list).enter().append("circle").attr("class", "dot").attr("cx", function(d) { return x(d.x); }).attr("cy", function(d) { return y(d.y); }).attr("r", 4.5);});}function renderAxes(svg){var xAxis = d3.axisBottom().scale(d3.scaleLinear().range([0, quadrantWidth()]));var yAxis = d3.axisLeft().scale(d3.scaleLinear().range([quadrantHeight(), 0]));svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart() + "," + yStart() + ")";}).call(xAxis);svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart() + "," + yEnd() + ")";}).call(yAxis);}function xStart(){return margin;}function yStart(){return height - margin;}function xEnd(){return width - margin;}function yEnd(){return margin;}function quadrantWidth(){return width - 2 * margin;}function quadrantHeight(){return height - 2 * margin;}</script><h4>Curve Mode:</h4><div class="control-group"><button onclick="render(d3.curveLinear)">linear</button><button onclick="render(d3.curveLinearClosed)">linear closed</button><button onclick="render(d3.curveStepBefore)">step before</button><button onclick="render(d3.curveStepAfter)">step after</button><button onclick="render(d3.curveBasis)">basis</button><button onclick="render(d3.curveBasisOpen)">basis open</button><button onclick="render(d3.curveBasisClosed)">basis closed</button></div><div class="control-group"><button onclick="render(d3.curveBundle)">bundle</button><button onclick="render(d3.curveCardinal)">cardinal</button><button onclick="render(d3.curveCardinalOpen)">cardinal open</button><button onclick="render(d3.curveCardinalClosed)">cardinal closed</button><button onclick="render(d3.curveMonotoneY)">monotone</button><button onclick="render(d3.curveCatmullRom)">CatmullRom</button></div>

更改线条的张力
<script type="text/javascript">var width = 500,height = 500,margin = 30,duration = 500,x = d3.scaleLinear().domain([0, 10]).range([margin, width - margin]),y = d3.scaleLinear().domain([0, 1]).range([height - margin, margin]);var data = d3.range(10).map(function(i){return {x: i, y: (Math.sin(i * 3) + 1) / 2};});var svg = d3.select("body").append("svg");svg.attr("height", height).attr("width", width);renderAxes(svg);render(1);function render(tension){var line = d3.line().curve(d3.curveCardinal.tension(tension)).x(function(d){return x(d.x);}).y(function(d){return y(d.y);});svg.selectAll("path.line").data([tension]).enter().append("path").attr("class", "line");svg.selectAll("path.line").data([tension]).transition().duration(duration).ease(d3.easeLinear).attr("d", function(d){return line(data);});svg.selectAll("circle").data(data).enter().append("circle").attr("class", "dot").attr("cx", function(d) { return x(d.x); }).attr("cy", function(d) { return y(d.y); }).attr("r", 4.5);}function renderAxes(svg){var xAxis = d3.axisBottom().scale(d3.scaleLinear().range([0, quadrantWidth()]));var yAxis = d3.axisLeft().scale(d3.scaleLinear().range([quadrantHeight(), 0]));svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart() + "," + yStart() + ")";}).call(xAxis);svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart() + "," + yEnd() + ")";}).call(yAxis);}function xStart(){return margin;}function yStart(){return height - margin;}function xEnd(){return width - margin;}function yEnd(){return margin;}function quadrantWidth(){return width - 2 * margin;}function quadrantHeight(){return height - 2 * margin;}</script><h4>Line Tension:</h4><div class="control-group"><button onclick="render(0)">0</button><button onclick="render(0.2)">0.2</button><button onclick="render(0.4)">0.4</button><button onclick="render(0.6)">0.6</button><button onclick="render(0.8)">0.8</button><button onclick="render(1)">1</button></div>

使用区域生成器
<script type="text/javascript">var width = 500,height = 500,margin = 30,duration = 500,x = d3.scaleLinear().domain([0, 10]).range([margin, width - margin]),y = d3.scaleLinear().domain([0, 10]).range([height - margin, margin]);var data = d3.range(11).map(function(i){return {x: i, y: Math.sin(i)*3 + 5};});var svg = d3.select("body").append("svg");svg.attr("height", height).attr("width", width);renderAxes(svg);render();renderDots(svg);function render(){var line = d3.line().x(function(d){return x(d.x);}).y(function(d){return y(d.y);});svg.selectAll("path.line").data([data]).enter().append("path").attr("class", "line");svg.selectAll("path.line").data([data]).attr("d", function(d){return line(d);});var area = d3.area().x(function(d) { return x(d.x); }).y0(y(0)).y1(function(d) { return y(d.y); });svg.selectAll("path.area").data([data]).enter().append("path").attr("class", "area").attr("d", function(d){return area(d);});}function renderDots(svg){svg.append("g").selectAll("circle").data(data).enter().append("circle").attr("class", "dot").attr("cx", function(d) { return x(d.x); }).attr("cy", function(d) { return y(d.y); }).attr("r", 4.5);}function renderAxes(svg){var xAxis = d3.axisBottom().scale(d3.scaleLinear().range([0, quadrantWidth()]));var yAxis = d3.axisLeft().scale(d3.scaleLinear().range([quadrantHeight(), 0]));svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart() + "," + yStart() + ")";}).call(xAxis);svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart() + "," + yEnd() + ")";}).call(yAxis);}function xStart(){return margin;}function yStart(){return height - margin;}function xEnd(){return width - margin;}function yEnd(){return margin;}function quadrantWidth(){return width - 2 * margin;}function quadrantHeight(){return height - 2 * margin;}</script>

使用断面曲线
<script type="text/javascript">var width = 500,height = 500,margin = 30,x = d3.scaleLinear().domain([0, 10]).range([margin, width - margin]),y = d3.scaleLinear().domain([0, 10]).range([height - margin, margin]);var data = d3.range(11).map(function(i){return {x: i, y: Math.sin(i)*3 + 5};});var svg = d3.select("body").append("svg");svg.attr("height", height).attr("width", width);renderAxes(svg);render(d3.curveLinear);renderDots(svg);function render(mode){var line = d3.line().x(function(d){return x(d.x);}).y(function(d){return y(d.y);}).curve(mode);svg.selectAll("path.line").data([data]).enter().append("path").attr("class", "line");svg.selectAll("path.line").data([data]).attr("d", function(d){return line(d);});var area = d3.area().x(function(d) { return x(d.x); }).y0(y(0)).y1(function(d) { return y(d.y); }).curve(mode);svg.selectAll("path.area").data([data]).enter().append("path").attr("class", "area")svg.selectAll("path.area").data([data]).attr("d", function(d){return area(d);});}function renderDots(svg){svg.append("g").selectAll("circle").data(data).enter().append("circle").attr("class", "dot").attr("cx", function(d) { return x(d.x); }).attr("cy", function(d) { return y(d.y); }).attr("r", 4.5);}function renderAxes(svg){var xAxis = d3.axisBottom().scale(d3.scaleLinear().range([0, quadrantWidth()]));var yAxis = d3.axisLeft().scale(d3.scaleLinear().range([quadrantHeight(), 0]));svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart() + "," + yStart() + ")";}).call(xAxis);svg.append("g").attr("class", "axis").attr("transform", function(){return "translate(" + xStart() + "," + yEnd() + ")";}).call(yAxis);}function xStart(){return margin;}function yStart(){return height - margin;}function xEnd(){return width - margin;}function yEnd(){return margin;}function quadrantWidth(){return width - 2 * margin;}function quadrantHeight(){return height - 2 * margin;}</script><h4>Curve Mode:</h4><div class="control-group"><button onclick="render(d3.curveLinear)">linear</button><button onclick="render(d3.curveLinearClosed)">linear closed</button><button onclick="render(d3.curveStepBefore)">step before</button><button onclick="render(d3.curveStepAfter)">step after</button><button onclick="render(d3.curveBasis)">basis</button><button onclick="render(d3.curveBasisOpen)">basis open</button><button onclick="render(d3.curveBasisClosed)">basis closed</button></div><div class="control-group"><button onclick="render(d3.curveBundle)">bundle</button><button onclick="render(d3.curveCardinal)">cardinal</button><button onclick="render(d3.curveCardinalOpen)">cardinal open</button><button onclick="render(d3.curveCardinalClosed)">cardinal closed</button><button onclick="render(d3.curveMonotoneY)">monotone</button><button onclick="render(d3.curveCatmullRom)">CatmullRom</button></div>

使用圆弧生成器
<script type="text/javascript">var width = 400,height = 400,fullAngle = 2 * Math.PI,colors = d3.scaleOrdinal(d3.schemeCategory20c);var svg = d3.select("body").append("svg").attr("class", "pie").attr("height", height).attr("width", width);function render(innerRadius, endAngle){if(!endAngle) endAngle = fullAngle;var data = [{startAngle: 0, endAngle: 0.1 * endAngle},{startAngle: 0.1 * endAngle, endAngle: 0.2 * endAngle},{startAngle: 0.2 * endAngle, endAngle: 0.4 * endAngle},{startAngle: 0.4 * endAngle, endAngle: 0.6 * endAngle},{startAngle: 0.6 * endAngle, endAngle: 0.7 * endAngle},{startAngle: 0.7 * endAngle, endAngle: 0.9 * endAngle},{startAngle: 0.9 * endAngle, endAngle: endAngle}];var arc = d3.arc().outerRadius(200).innerRadius(innerRadius);svg.select("g").remove();svg.append("g").attr("transform", "translate(200,200)").selectAll("path.arc").data(data).enter().append("path").attr("class", "arc").attr("fill", function(d, i){return colors(i);}).attr("d", function(d, i){return arc(d, i);});}render(0);</script><div class="control-group"><button onclick="render(0)">Circle</button><button onclick="render(100)">Annulus(Donut)</button><button onclick="render(0, Math.PI)">Circular Sector</button><button onclick="render(100, Math.PI)">Annulus Sector</button></div>
实现圆弧过渡
<script type="text/javascript">var width = 400,height = 400,endAngle = 2 * Math.PI,colors = d3.scaleOrdinal(d3.schemeCategory20c);var svg = d3.select("body").append("svg").attr("class", "pie").attr("height", height).attr("width", width);function render(innerRadius) {var data = [{startAngle: 0, endAngle: 0.1 * endAngle},{startAngle: 0.1 * endAngle, endAngle: 0.2 * endAngle},{startAngle: 0.2 * endAngle, endAngle: 0.4 * endAngle},{startAngle: 0.4 * endAngle, endAngle: 0.6 * endAngle},{startAngle: 0.6 * endAngle, endAngle: 0.7 * endAngle},{startAngle: 0.7 * endAngle, endAngle: 0.9 * endAngle},{startAngle: 0.9 * endAngle, endAngle: endAngle}];var arc = d3.arc().outerRadius(200).innerRadius(innerRadius);svg.select("g").remove();svg.append("g").attr("transform", "translate(200,200)").selectAll("path.arc").data(data).enter().append("path").attr("class", "arc").attr("fill", function (d, i) {return colors(i);}).transition().duration(1000).attrTween("d", function (d) {var start = {startAngle: 0, endAngle: 0}; // <-Avar interpolate = d3.interpolate(start, d); // <-Breturn function (t) {return arc(interpolate(t)); // <-C};});}render(100);</script>

参考
【1】Data Visualization and D3.js(视频)
【2】D3 4.x 数据可视化实战手册@[加]朱启
【3】https://github.com/NickQiZhu/d3-cookbook-v2(D3 4.x 数据可视化实战手册 代码)
【4】D3.js 中文文档
