官网 https://d3js.org/ 历史版本下载,后面换为自己想要版本号 https://registry.npmjs.org/d3/-/d3-7.4.4.tgz |
yarn add d3 import * as d3 from "d3"; import {select, selectAll} from "d3"; import {mean, median} from "d3-array"; D3 in React import * as d3 from "d3"; export default function LinePlot({ data, width = 640, height = 400, marginTop = 20, marginRight = 20, marginBottom = 20, marginLeft = 20 }) { const x = d3.scaleLinear([0, data.length - 1], [marginLeft, width - marginRight]); const y = d3.scaleLinear(d3.extent(data), [height - marginBottom, marginTop]); const line = d3.line((d, i) => x(i), y); return ( <svg width={width} height={height}> <path fill="none" stroke="currentColor" stroke-width="1.5" d={line(data)} /> <g fill="white" stroke="currentColor" stroke-width="1.5"> {data.map((d, i) => (<circle key={i} cx={x(i)} cy={y(d)} r="2.5" />))} </g> </svg> ); } |
HTML直接引入 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>import d3</title> <link rel='shortcut icon' href='#'></link> <script type="text/javascript" src="static/d3.744/d3.js"></script> </head> <body> <svg width="50" height="50"> <circle cx="25" cy="25" r="22" fill="pink" stroke="gray" stroke-width="2"/> </svg> <script type="text/javascript"> d3.select("body").append("p").text("d3 demo"); </script> </body> </html> |
|
|
SVG(Scalable Vector Graphics,可缩放矢量图形),等比缩放不失真,兼容多种浏览器 相当于画布,指定width和height属性 <svg width="500" height="50"> </svg> 像素px是默认的度量单位,除了px之外,还支持em、pt、in、cm和mm等其他单位。 <rect x="0" y="0" width="500" height="50"/> <circle cx="250" cy="25" r="25"/> <ellipse cx="250" cy="25" rx="100" ry="25"/> <line x1="0" y1="0" x2="500" y2="50" stroke="black"/> stroke指定直线的颜色 |
SVG的样式 SVG的text会继承CSS为父元素指定的字体样式,除非另有指定 <text x="250" y="25" font-family="serif" font-size="25" fill="gray">好好学习 </text> 要防止任何可见的元素跑到SVG画布外面,否则会被裁切掉 <text x="250" y="50" font-family="serif" font-size="25" fill="gray">天天向上 </text> 为SVG元素添加样式 SVG的默认样式是黑色填充,没有描边。如果你想要其他样式,必须自己给元素添加 fill 区域颜色值。与CSS用法一样,可以使用颜色名、十六进制值,或RGB、RGBA值 stroke 边的颜色值 stroke-width 边的宽度,带单位的数值(通常单位是像素) opacity 0.0(完全透明)到1.0(完全不透明)之间的数值。 text元素还可以使用下面这些属性,用法与CSS的类似: font-family font-size <circle cx="25" cy="25" r="22" fill="yellow" stroke="orange" stroke-width="5"/> <circle cx="25" cy="25" r="22" class="aaa"/> svg .aaa { /* ... */ } |
重叠与透明度 d3 svg画面默认在HTML层的更上一层,并且相互之间可位置重叠,可设置透明度 <circle cx="25" cy="25" r="20" fill="rgba(128, 0, 128, 1.0)"/> <circle cx="50" cy="25" r="20" fill="rgba(0, 0, 255, 0.75)"/> <circle cx="75" cy="25" r="20" fill="rgba(0, 255, 0, 0.5)"/> <circle cx="100" cy="25" r="20" fill="rgba(255, 255, 0, 0.25)"/> <circle cx="125" cy="25" r="20" fill="rgba(255, 0, 0, 0.1)"/> <circle cx="25" cy="25" r="20" fill="purple" stroke="green" stroke-width="10" opacity="0.9"/> <circle cx="65" cy="25" r="20" fill="green" stroke="blue" stroke-width="10" opacity="0.5"/> <circle cx="105" cy="25" r="20" fill="yellow" stroke="red" stroke-width="10" opacity="0.1"/> 以第三个圆形为例,其opacity值是0.2(20%)。而紫色填充已经设置了0.75(75%)的透明通道值。 结果,紫色区域最终的透明度就是0.2 × 0.75 = 0.15(15%)。 |
|
|
将数据集绑定到元素列表上,并选出未创建元素的选集,通过append添加元素 let dataset = [ 1,2,3 ]; d3.select("body").selectAll("p") .data(dataset) .enter() .append("p") .text("day day up"); d3.select("body") 选择DOM中的body元素,将其引用交给调用链中的下一个方法 .selectAll("p") 选择DOM中的所有段落。因为还没有任何段落,所以返回空选集。可以认为空选集代表接下来会创建的段落。 .data(dataset) 遍历解析并输出数据值。dataset数组中有n个值,此后的所有方法都将执行5n遍,每次针对一个值。 .enter() 无数据的占位元素对象选集 如果数据值比对应的DOM元素多,就创建一个新的占位元素。 然后把这个新占位元素的引用交给链中的下一个方法。 .append("p") 取得enter()创建的空占位符选集,并把一个p元素附加到相应的DOM中。 然后它再把自己刚创建的元素的引用交给链中的下一个方法。 .text("day day up") 取得新创建的p元素的引用,插入文本值 每一步返回的都是对象的引用,其中从data方法开始循环遍历 通常情况下,DOM元素个数少于数据集的元素个数, 无法完成DOM元素与数据集元素的一一绑定, 所以.data绑定数据时,D3先创建一个空的DOM元素对象,绑定数据 enter()可以将空的DOM元素选取出来 再通过append追加元素,完成DOM元素与数据集元素的一一绑定 |
属性/样式与匿名函数 let dataset = [ 1,2,3 ]; d3.select("body").selectAll("p") .data(dataset) .enter() .append("p") .style("color", function(d) { if (d > 1) { return "red"; } else { return "black"; } }).text(function(d){ return "day day up "+d; }); attr()和style()可以分别用来设置选集的HTML属性和CSS属性 .classed("bar", true) 可以让一个css class生效/失效 ------------------------------------------------------------------------- |
|
|
|
stroke 英/strəʊk/ 美/stroʊk/ n.(打、击等的)一下;击球(动作);一笔; //Width and height var w = 600; var h = 100; var barPadding = 1; //Create SVG element var svg = d3.select("#line_div") .append("svg") .attr("width", w) .attr("height", h); var dataset = [[30,10,200,50]]; svg.selectAll("line") .data(dataset) .enter() .append("line") .attr("x1", function(d, i) { return d[0]; }) .attr("y1", function(d,i) { return d[1]; }) .attr("x2", function(d, i) { return d[2]; }) .attr("y2", function(d,i) { return d[3]; }) .attr("stroke", "black") .attr("stroke-width", "3"); svg.selectAll("text") .data(dataset) .enter() .append("text") .text("线") .attr("text-anchor", "middle") .attr("x", function(d, i) { return (d[0]+d[2])/2; }) .attr("y", function(d) { return d[1]+8; }) .attr("font-family", "sans-serif") .attr("font-size", "14px") .attr("fill", "blue"); |
|
|
|
|
//Width and height var w = 600; var h = 100; var barPadding = 1; //Create SVG element var svg = d3.select("#rectaa_div") .append("svg") .attr("width", w) .attr("height", h); var dataset = [[50,30,60,240]]; svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", function(d, i) { return d[0]; }) .attr("y", function(d,i) { return d[1]; }) .attr("height", function(d, i) { return d[2]; }) .attr("width", function(d,i) { return d[3]; }).attr("fill","#efef77") .attr("stroke", "#778899") .attr("stroke-width", "3"); svg.selectAll("text") .data(dataset) .enter() .append("text") .text("矩形") .attr("text-anchor", "middle") .attr("x", function(d, i) { return (d[0]+d[2])/2 + 30; }) .attr("y", function(d) { return d[1]-10; }) .attr("font-family", "sans-serif") .attr("font-size", "14px") .attr("fill", "#357210");
随机生成一组bar
div.bar { display: inline-block; width: 20px; height: 75px; margin-right: 2px; background-color: teal; }
var dataset = []; //Initialize empty array for (var i = 0; i < 25; i++) { //Loop 25 times var newNumber = Math.floor(Math.random() * 30); //New random integer (0-29) dataset.push(newNumber); //Add new number to array } d3.select("body").selectAll("div") .data(dataset) .enter() .append("div") .attr("class", "bar") .style("height",function (d) { var barHeight = d * 5; return barHeight + "px"; });
svg绘制条形图+text
//Width and height var w = 660; var h = 120; var barPadding = 8; var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13, 11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ]; //Create SVG element var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", function(d, i) { return i * (w / dataset.length);//等分 }) .attr("y", function(d) { return h - (d * 4);//左上角为起点 }) .attr("width", w / dataset.length - barPadding) .attr("height", function(d) { return d * 4;//与h-d*4形成一个统一高度h }) .attr("fill",function (d) { return "rgb(0, "+Math.round(d * 4)+", " + Math.round(d * 10) + ")"; }); svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return d; }) .attr("text-anchor", "middle") .attr("x", function(d, i) { return i * (w / dataset.length) + (w / dataset.length - barPadding) / 2; }) .attr("y", function(d) { return h - (d * 4) + 14; }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "white");
//Width and height var w = 500; var h = 150; //Data var dataset = [ 5, 10, 15, 20, 25 ]; //Create SVG element var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); var circles = svg.selectAll("circle") .data(dataset) .enter() .append("circle"); circles.attr("cx", function(d, i) { return (i * 50) + 25; }) .attr("cy", h/2) .attr("r", function(d) { return d; }) .attr("fill", "yellow") .attr("stroke", "orange") .attr("stroke-width", function(d) { return d/2; });
// 宽度和高度 var width = 700; var height = 300; var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); let dataset = [ 5, 10, 15, 20, 25 ]; svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function(d, i) { return (i * 50) + 25; }) .attr("cy", height/2) .attr("r", function(d) { return d; }) .attr("fill","pink") .attr("stroke", "orange") .attr("stroke-width", function(d) { return d/2; });
散点的半径大小体现数值的大小
var w = 600; var h = 100; var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95], [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ]; //Create SVG element var svg = d3.select("#d307_scatterplot") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function(d) { return d[0]; }) .attr("cy", function(d) { return d[1]; }) .attr("r", function(d) { return Math.sqrt(h - d[1]); }) .attr("fill","#999"); svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return d[0] + "," + d[1]; }) .attr("x", function(d) { return d[0]; }) .attr("y", function(d) { return d[1]; }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "blue");
scale:比例尺,同比/等比映射,将domain([100, 300])同比映射到range([10, 30])
let scale = d3.scaleLinear() .domain([100, 300]) .range([10, 30]); console.log(scale(200));//20
d3.max
let simpleDataset = [7, 8, 4, 5, 2]; d3.max(simpleDataset); // 返回8 let dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95], [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ]; d3.max(dataset, function(d) { return d[0]; });
动态同比缩放:将svg画布映射到 [padding, w - padding * 2]的范围内,但相对大小/位置 不变
//Width and height var w = 600; var h = 300; var padding = 30; var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95], [410, 12], [475, 44], [25, 67], [85, 21], [220, 88], [500, 150] ]; //Create scale functions var xScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d[0]; })]) .range([padding, w - padding * 2]); var yScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d[1]; })]) .range([h - padding, padding]); var rScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d[1]; })]) .range([2, 5]); //Create SVG element var svg = d3.select("#d3scatterplotdt") .append("svg") .attr("width", w) .attr("height", h); //Create circles svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function(d) { return xScale(d[0]); }) .attr("cy", function(d) { return yScale(d[1]); }) .attr("r", function(d) { return rScale(d[1]); }) .attr("fill","#999"); //Create labels svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return d[0] + "," + d[1]; }) .attr("x", function(d) { return xScale(d[0]); }) .attr("y", function(d) { return yScale(d[1]); }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "red");
比例尺举例
平方根比例尺 var aScale = d3.scaleSqrt() // -- 新代码! .domain([0, d3.max(dataset, function(d) { return d[1]; })]) .range([0, 10]); // -- 新代码!
时间比例尺
//Width and height var w = 500; var h = 300; var padding = 40; var dataset, xScale, yScale; //Empty, for now //For converting strings to Dates // 01/01/17格式的时间 var parseTime = d3.timeParse("%m/%d/%y"); //For converting Dates to strings var formatTime = d3.timeFormat("%b %e"); dataset = []; d3.csv("static/data/time_scale_data.csv",function(d,i){ var di = { Date: parseTime(d.Date), Amount: parseInt(d.Amount) } dataset.push(di); }); setTimeout(function() { console.log(dataset); aa(); }, 2000); function aa(){ //Create scale functions xScale = d3.scaleTime() .domain([ d3.min(dataset, function(d) { return d.Date; }), d3.max(dataset, function(d) { return d.Date; }) ]) .range([padding, w - padding]); yScale = d3.scaleLinear() .domain([ d3.min(dataset, function(d) { return d.Amount; }), d3.max(dataset, function(d) { return d.Amount; }) ]) .range([h - padding, padding]); //Create SVG element var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); //Generate date labels first, so they are in back svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return formatTime(d.Date); }) .attr("x", function(d) { return xScale(d.Date) + 4; }) .attr("y", function(d) { return yScale(d.Amount) + 4; }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "#bbb"); //Generate circles last, so they appear in front svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function(d) { return xScale(d.Date); }) .attr("cy", function(d) { return yScale(d.Amount); }) .attr("r", 2); }
本节摘要列表
d3 时间数轴 d3 数轴优化:增加竖线
定义刻度尺
d3.axisLeft和d3.axisRight可以生成垂直数轴, d3.axisTop和d3.axisBottom可以生成水平数轴, Left,Right,Top,Bottom指的是数轴上刻度标,就是个那个小竖条的方向, 而整个数轴顶点的默认坐标都是[0,0] //axisBottom,水平方向,起点为[0,0]的一段线段 var xAxis = d3.axisBottom() .scale(xScale); 等效于 var xAxis = d3.axisBottom(xScale);
调用刻度尺
创建一个层叫g,用于分组, 坐标轴绘制需要放到后面,即先绘制其他图形,然后再绘制坐标轴 svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + (h - padding) + ")") .call(xAxis);
优化刻度尺
var xAxis = d3.axisBottom() .scale(xScale) .ticks(5); // 粗略地设置刻度线的数量 刻度数字格式化 var formatAsPercentage = d3.format(".1%"); xAxis.tickFormat(formatAsPercentage);
// 宽度和高度 //Width and height var w = 600; var h = 200; var padding = 30; var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95], [410, 12], [475, 44], [25, 67], [85, 21], [220, 88], [600, 150] ]; var formatAsPercentage = d3.format("1"); //Create scale functions var xScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d[0]; })]) .range([padding, w - padding * 2]); var yScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d[1]; })]) .range([h - padding, padding]); //Define X axis var xAxis = d3.axisBottom() .scale(xScale) .ticks(5).tickFormat(formatAsPercentage); //这里D3根据值的分布会进行区间的调整,tick是建议,不是绝对 var yAxis = d3.axisLeft() .scale(yScale) .ticks(5).tickFormat(formatAsPercentage); //Create SVG element var svg = d3.select("#svg_keduchi111") .append("svg") .attr("width", w) .attr("height", h); var aScale = d3.scaleSqrt() .domain([0, d3.max(dataset, function(d) { return d[1]; })]) .range([0, 10]); //Create circles svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function(d) { return xScale(d[0]); }) .attr("cy", function(d) { return yScale(d[1]); }) .attr("r", function(d) { return aScale(d[1]); }).attr("fill","pink"); //Create labels svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return d[0] + "," + d[1]; }) .attr("x", function(d) { return xScale(d[0]); }) .attr("y", function(d) { return yScale(d[1]); }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "blue"); //Create X axis svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + (h - padding) + ")") .call(xAxis); svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + (padding) + ",0)") .call(yAxis);
随机数轴
//Width and height var w = 500; var h = 300; var padding = 30; /* //Static dataset var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95], [410, 12], [475, 44], [25, 67], [85, 21], [220, 88], [600, 150] ]; */ //Dynamic, random dataset var dataset = []; //Initialize empty array var numDataPoints = 12; //Number of dummy data points to create var xRange = Math.random() * 1000; //Max range of new x values var yRange = Math.random() * 1000; //Max range of new y values for (var i = 0; i < numDataPoints; i++) { //Loop numDataPoints times var newNumber1 = Math.floor(Math.random() * xRange); //New random integer var newNumber2 = Math.floor(Math.random() * yRange); //New random integer dataset.push([newNumber1, newNumber2]); //Add new number to array } //Create scale functions var xScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d[0]; })]) .range([padding, w - padding * 2]); var yScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d[1]; })]) .range([h - padding, padding]); var aScale = d3.scaleSqrt() .domain([0, d3.max(dataset, function(d) { return d[1]; })]) .range([0, 10]); //Define X axis var xAxis = d3.axisBottom() .scale(xScale) .ticks(5); //Define Y axis var yAxis = d3.axisLeft() .scale(yScale) .ticks(5); //Create SVG element var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); //Create circles svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function(d) { return xScale(d[0]); }) .attr("cy", function(d) { return yScale(d[1]); }) .attr("r", function(d) { return aScale(d[1]); }) .attr("fill","#abc"); //Create labels svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return d[0] + "," + d[1]; }) .attr("x", function(d) { return xScale(d[0]); }) .attr("y", function(d) { return yScale(d[1]); }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "#889"); //Create X axis svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + (h - padding) + ")") .call(xAxis); //Create Y axis svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + padding + ",0)") .call(yAxis);
时间数轴
//Width and height var w = 500; var h = 300; var padding = 40; var dataset, xScale, yScale, xAxis, yAxis; //Empty, for now //For converting strings to Dates var parseTime = d3.timeParse("%Y-%m-%d"); //For converting Dates to strings var formatTime = d3.timeFormat("%b %e"); dataset = []; let a1 = [{"Date":"2016-12-31","Amount":35},{"Date":"2017-01-01","Amount":30},{"Date":"2017-01-02","Amount":24},{"Date":"2017-01-03","Amount":37},{"Date":"2017-01-04","Amount":54},{"Date":"2017-01-05","Amount":55},{"Date":"2017-01-06","Amount":62},{"Date":"2017-01-07","Amount":62},{"Date":"2017-01-08","Amount":70},{"Date":"2017-01-09","Amount":66},{"Date":"2017-01-10","Amount":51},{"Date":"2017-01-11","Amount":63},{"Date":"2017-01-12","Amount":74},{"Date":"2017-01-13","Amount":58},{"Date":"2017-01-14","Amount":69},{"Date":"2017-01-15","Amount":56},{"Date":"2017-01-16","Amount":56},{"Date":"2017-01-17","Amount":50},{"Date":"2017-01-18","Amount":52},{"Date":"2017-01-19","Amount":48},{"Date":"2017-01-20","Amount":55},{"Date":"2017-01-21","Amount":44},{"Date":"2017-01-22","Amount":35},{"Date":"2017-01-23","Amount":32},{"Date":"2017-01-24","Amount":35},{"Date":"2017-01-25","Amount":21},{"Date":"2017-01-26","Amount":15},{"Date":"2017-01-27","Amount":32},{"Date":"2017-01-28","Amount":21},{"Date":"2017-01-29","Amount":12},{"Date":"2017-01-30","Amount":23}]; dataset = a1.map(function(d,i){ return { Date: parseTime(d.Date), Amount: parseInt(d.Amount) }; }); function aa(){ //Create scale functions xScale = d3.scaleTime() .domain([ d3.min(dataset, function(d) { return d.Date; }), d3.max(dataset, function(d) { return d.Date; }) ]) .range([padding, w - padding]); yScale = d3.scaleLinear() .domain([ d3.min(dataset, function(d) { return d.Amount; }), d3.max(dataset, function(d) { return d.Amount; }) ]) .range([h - padding, padding]); //Create SVG element var svg = d3.select("#svg_timsaxi") .append("svg") .attr("width", w) .attr("height", h); //Generate date labels first, so they are in back svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return formatTime(d.Date); }) .attr("x", function(d) { return xScale(d.Date) + 4; }) .attr("y", function(d) { return yScale(d.Amount) + 4; }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "#bbb"); //Generate circles last, so they appear in front svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function(d) { return xScale(d.Date); }) .attr("cy", function(d) { return yScale(d.Amount); }) .attr("r", 2); //Define X axis xAxis = d3.axisBottom() .scale(xScale) .ticks(5); //Define Y axis yAxis = d3.axisLeft() .scale(yScale) .ticks(5); //Create X axis svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + (h - padding) + ")") .call(xAxis); //Create Y axis svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + padding + ",0)") .call(yAxis); } aa();
时间数轴优化:增加竖线
//Width and height var w = 700; var h = 300; var padding = 60; var dataset, xScale, yScale, xAxis, yAxis; //Empty, for now //For converting strings to Dates var parseTime = d3.timeParse("%Y-%m-%d"); //For converting Dates to strings // var formatTime = d3.timeFormat("%b %e"); //For converting Dates to strings var formatTime = d3.timeFormat("%e"); dataset = []; // d3.csv("static/data/time_scale_data.csv",function(d,i){ // var di = { // Date: parseTime(d.Date), // Amount: parseInt(d.Amount) // } // dataset.push(di); // // alert(i); // }); a1 = [{"Date":"2016-12-31","Amount":35},{"Date":"2017-01-01","Amount":30},{"Date":"2017-01-02","Amount":24},{"Date":"2017-01-03","Amount":37},{"Date":"2017-01-04","Amount":54},{"Date":"2017-01-05","Amount":55},{"Date":"2017-01-06","Amount":62},{"Date":"2017-01-07","Amount":62},{"Date":"2017-01-08","Amount":70},{"Date":"2017-01-09","Amount":66},{"Date":"2017-01-10","Amount":51},{"Date":"2017-01-11","Amount":63},{"Date":"2017-01-12","Amount":74},{"Date":"2017-01-13","Amount":58},{"Date":"2017-01-14","Amount":69},{"Date":"2017-01-15","Amount":56},{"Date":"2017-01-16","Amount":56},{"Date":"2017-01-17","Amount":50},{"Date":"2017-01-18","Amount":52},{"Date":"2017-01-19","Amount":48},{"Date":"2017-01-20","Amount":55},{"Date":"2017-01-21","Amount":44},{"Date":"2017-01-22","Amount":35},{"Date":"2017-01-23","Amount":32},{"Date":"2017-01-24","Amount":35},{"Date":"2017-01-25","Amount":21},{"Date":"2017-01-26","Amount":15},{"Date":"2017-01-27","Amount":32},{"Date":"2017-01-28","Amount":21},{"Date":"2017-01-29","Amount":12},{"Date":"2017-01-30","Amount":23}]; dataset = a1.map(function(d,i){ return { Date: parseTime(d.Date), Amount: parseInt(d.Amount) }; }) function aa2(){ //Create scale functions xScale = d3.scaleTime() .domain([ d3.min(dataset, function(d) { return d.Date; }), d3.max(dataset, function(d) { return d.Date; }) ]) .range([padding, w - padding]); yScale = d3.scaleLinear() .domain([ d3.min(dataset, function(d) { return d.Amount; }), d3.max(dataset, function(d) { return d.Amount; }) ]) .range([h - padding, padding]); //Create SVG element var svg = d3.select("#svg_ketimyh111") .append("svg") .attr("width", w) .attr("height", h); //Generate date labels first, so they are in back svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return formatTime(d.Date); }) .attr("x", function(d) { return xScale(d.Date) + 4; }) .attr("y", function(d) { return yScale(d.Amount) + 4; }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "#bbb"); //Generate guide lines svg.selectAll("line") .data(dataset) .enter() .append("line") .attr("x1", function(d) { return xScale(d.Date); }) .attr("x2", function(d) { return xScale(d.Date); }) .attr("y1", h - padding) .attr("y2", function(d) { return yScale(d.Amount); }) .attr("stroke", "#ddd") .attr("stroke-width", 1); //Generate circles last, so they appear in front svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function(d) { return xScale(d.Date); }) .attr("cy", function(d) { return yScale(d.Amount); }) .attr("r", 2).attr("fill","blue"); //Define X axis xAxis = d3.axisBottom() .scale(xScale) .ticks(5).tickFormat(formatTime); //Define Y axis yAxis = d3.axisLeft() .scale(yScale) .ticks(5); //Create X axis svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + (h - padding) + ")") .call(xAxis); //Create Y axis svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + padding + ",0)") .call(yAxis); } aa2();
简单条形图回顾 使用分档比例尺
简单条形图回顾:没有使用比例尺,使用数据本身做简单处理
//Width and height var w = 600; var h = 200; var barPadding = 1; var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13, 11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ]; //Create SVG element var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", function(d, i) { return i * (w / dataset.length); }) .attr("y", function(d) { return h - (d * 4); }) .attr("width", w / dataset.length - barPadding) .attr("height", function(d) { return d * 4; }) .attr("fill", function(d) { return "rgb(0, 0, " + Math.round(d * 10) + ")"; }); svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return d; }) .attr("text-anchor", "middle") .attr("x", function(d, i) { return i * (w / dataset.length) + (w / dataset.length - barPadding) / 2; }) .attr("y", function(d) { return h - (d * 4) + 14; }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "white");
使用分档比例尺:d3.scaleBand()
点击这一行文本使用新数据更新条形图.
var xScale = d3.scaleBand() .domain(d3.range(dataset.length)) .rangeRound([0, w]) .paddingInner(0.05); d3.scaleBand() 是离散范围值 d3.range(dataset.length) = d3.range(20) 数组长度20,分为20档,w=600,一档宽度30 paddingInner(0.05) 30*0.05=1.5的inner padding距离 1.5宽度导致w的宽度出现小数,需要对半个像素值进行抗锯齿处理,否则条形会显得模糊。 rangeRound可以让分档比例尺把输出值四舍五入成最接近的整数, 整数值有助于将视觉元素与像素网格对齐,保证图形的边缘清晰锐利。
添加点击事件
d3.select("p") .on("click", function() { // 更新数据,然后重新绘制图形 });
动画:transition
.. // 选择元素的代码 .transition() .duration(2000) .ease(d3.easeLinear) ... // attr()的代码 transition()添加动画效果 duration(time_sec) 指定这个动画持续的时间,单位毫秒 ease(d3.easeCubicOut)指定动画转变的具体细节: d3.easeCubicOut 逐渐加速然后再逐渐减速的效果 d3.easeLinear 线性缓动,动画速度不变 d3.easeCircleIn 元素逐渐进入并加速,最后到达指定位置。 d3.easeElasticOut 描述这个效果最恰当的词是“有弹性”。 d3.easeBounceOut 像皮球落地一样反复弹跳,慢慢停下来
延时时间
ease()负责控制动画性质,delay()用于指定过渡开始的时间。 ... .transition() .delay(1000) // 1000毫秒,即1秒 .duration(2000) // 2000毫秒,即2秒 ...
//Width and height var w = 600; var h = 250; var padding = 50; var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13, 11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ]; var xScale = d3.scaleBand() .domain(d3.range(dataset.length)) .rangeRound([0, w]) .paddingInner(0.05); var yScale = d3.scaleLinear() .domain([0, d3.max(dataset)]) .range([0, h - padding]); //Create SVG element var svg = d3.select("#fendangcs_div") .append("svg") .attr("width", w) .attr("height", h); //Create bars svg.selectAll("rect") .data(dataset) .enter() .append("rect") .attr("x", function(d, i) { return xScale(i); }) .attr("y", function(d) { return h - yScale(d); }) .attr("width", xScale.bandwidth()) .attr("height", function(d) { return yScale(d); }) .attr("fill", function(d) { return "rgb(0, 0, " + Math.round(d * 10) + ")"; }); //Create labels svg.selectAll("text") .data(dataset) .enter() .append("text") .text(function(d) { return d; }) .attr("text-anchor", "middle") .attr("x", function(d, i) { return xScale(i) + xScale.bandwidth() / 2; }) .attr("y", function(d) { return h - yScale(d) + 14; }) .attr("font-family", "sans-serif") .attr("font-size", "11px") .attr("fill", "white"); d3.select("#fendangcsp_click") .on("click",function () { //New values for dataset // dataset = [ 11, 12, 15, 20, 18, 17, 16, 18, 23, 25, // 5, 10, 13, 19, 21, 25, 22, 18, 15, 13 ]; //New values for dataset var numValues = dataset.length; //Count original length of dataset var maxValue = 100; dataset = []; //Initialize empty array for (var i = 0; i < numValues; i++) { //Loop numValues times var newNumber = Math.floor(Math.random() * maxValue); //New random integer (0-24) dataset.push(newNumber); //Add new number to array } yScale.domain([0,d3.max(dataset)]); d3.select("#fendangcs_div").selectAll("rect") .data(dataset) .transition() .delay(function (d,i) { return i * 100; }) .duration(500) .ease(d3.easeLinear) .attr("y",function (d) { return h - padding - yScale(d); }) .attr("height",function (d) { return yScale(d); }) .attr("fill", function(d) { return "rgb(0, 0, " + Math.round(d * 10) + ")"; }); //Update all labels svg.selectAll("text") .data(dataset) .transition() .delay(function (d,i) { return i * 100; }) .duration(500) .ease(d3.easeLinear) .text(function(d) { return d; }) .attr("x", function(d, i) { return xScale(i) + xScale.bandwidth() / 2; }) .attr("y", function(d) { return h - padding - yScale(d) + 14; }); });