D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
zhangzihaoDT
Full window
Github gist
Dynamic bar chart trace
Built with
blockbuilder.org
<!DOCTYPE html> <html> <head> <meta charset='utf-8'> <style> .chart svg{ border:2px dashed #00a5bb; } text{ font-size: 20px; font-family: Open Sans, sans-serif; } text.title{ font-size: 36px; font-weight: 500; } text.subTitle{ font-weight: 500; fill: #777777; } text.caption{ font-weight: 400; font-size: 14px; fill: #777777; } text.label{ font-weight: 600; } text.yearText{ font-size: 64px; font-weight: 700; opacity: 0.25; } .tick text { fill: #777777; } .xAxis .tick:nth-child(2) text { text-anchor: start; } .tick line { shape-rendering: CrispEdges; stroke: #dddddd; } .tick line.origin{ stroke: #aaaaaa; } path.domain{ display: none; } </style> </head> <body> <center> <div class='chart'></div> </center> <!-- <script type="text/javascript" src="lib/d3-3.5.6.js"></script> --> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script> <script> var halo = function(text, strokeWidth) { text.select(function() { return this.parentNode.insertBefore(this.cloneNode(true), this); }) .style('fill', '#000000') .style('stroke','#ffffff') .style('stroke-width',strokeWidth) .style('stroke-linejoin','round') .style('opacity',1); }; d3.csv = d3.dsv(",", "text/csv;charset=gb2312"); d3.csv('South+Chennai+-+Sheet2+(3).csv',function(error,brandData){ if (error) throw error; const height = 668,width = 1346,top_n = 3,tickDuration = 3600, margin = { top: 80, right: 10, bottom: 20, left: 10 }; const svg = d3.select('.chart') .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom); // console.log(svg); let barPadding = (height-(margin.bottom+margin.top))/(top_n*5); let title = svg.append('text') .attr('class','title') .attr("transform", "translate(20,60)") .html('2002-2018年三大运营商营收PK'); let subTitle = svg.append('text') .attr('class','subTitle') .attr("transform", "translate(20,96)") .html('单位:亿元'); let caption = svg.append('text') .attr('class','caption') // .attr('transform','translate(800,295)') .attr('transform', function(){ return `translate(${width}, ${height+80})` }) .style('text-anchor','end') .html('数据来源:中国移动、中国电信、中国联通2002-2018年年报'); let year = 2002; brandData.forEach(d => { d.value = +d.value, d.lastValue = +d.lastValue, d.value = isNaN(d.value) ? 0 : d.value, d.year = +d.year, d.colour = d3.hsl(Math.random()*360,0.75,0.75) }); let yearSlice = brandData.filter(d => d.year == year && !isNaN(d.value)) .sort((a,b) => b.value - a.value) .slice(0,top_n); yearSlice.forEach((d,i) => d.rank = i); let x = d3.scale.linear() .domain([0, d3.max(yearSlice, d => d.value)]) .range([margin.left, width-margin.right-65]); let y = d3.scale.linear() .domain([top_n, 0]) .range([height-margin.bottom, margin.top]); let xAxis = d3.svg.axis().orient('top') .scale(x) .ticks(width > 500 ? 5:2) .tickSize(-(height-margin.top-margin.bottom)) .tickFormat(d => d3.format(',')(d)); svg.append('g') .attr('class', 'axis xAxis') .attr('transform', function(d,i){ return `translate(20, ${margin.top+60})` }) .call(xAxis) .selectAll('.tick line') .classed('origin', d => d == 0); svg.selectAll('rect.bar') .data(yearSlice, d => d.name) .enter() .append('rect') .attr('class','bar') .attr('transform',function(d){ return `translate(${x(0)+20}, ${y(d.rank)+90})` }) // .attr('width',function(d){return x(d.value)-x(0)-1}) .attr('width',function(d){return x(0)}) .attr('height',function(d){return y(1)-y(0)-barPadding}) .style('fill',d => d.colour); svg.selectAll('text.label') .data(yearSlice, d => d.name) .enter() .append('text') .attr('class','label') .attr('transform',function(d){ return `translate(${x(0)-40}, ${y(d.rank)+5+((y(1)-y(0))/1)-20})` }) .attr('text-anchor','end') .html(d => d.name); svg.selectAll('text.valueLabel') .data(yearSlice, d => d.name) .enter() .append('text') .attr('class','valueLabel') // .attr('transform',function(d){ // return `translate(${x(d.value)+5}, ${y(d.rank)+5+((y(1)-y(0))/2)+1})` // }) .attr('transform',function(d){ return `translate(${x(0)-30}, ${y(d.rank)+5+((y(1)-y(0))/1)-20})` }) .text(d => d3.format(',.0f')(d.lastValue)); let yearText = svg.append('text') .attr('class','yearText') .attr('transform',function(d,i){ return `translate(${width-margin.right}, ${height+40})` }) .style('text-anchor','end') .html(~~year) .call(halo, 10); let ticker = setInterval(e => { yearSlice = brandData.filter(d => d.year == year && !isNaN(d.value)) .sort((a,b) => b.value - a.value) .slice(0,top_n); console.log(yearSlice); yearSlice.forEach((d,i) => d.rank = i); x.domain([0, d3.max(yearSlice, d => d.value)]); svg.select('.xAxis') .transition() .duration(tickDuration) .ease('poly','1') .call(xAxis); let bars = svg.selectAll('.bar').data(yearSlice, d => d.name); bars .enter() .append('rect') .attr('class',d => `bar ${d.name.replace(/\s/g,'_')}`) .attr('transform',function(d,i){ return `translate(${x(0)+10}, ${d => y(top_n+1)+5})` }) .attr('width',d => x(d.value)-x(0)-1) .attr('height',y(1)-y(0)-barPadding) // .attrs({ // class: d => `bar ${d.name.replace(/\s/g,'_')}`, // x: x(0)+1, // width: d => x(d.value)-x(0)-1, // y: d => y(top_n+1)+5, // height: y(1)-y(0)-barPadding // }) .style('fill', d => d.colour) .transition() .duration(tickDuration) .ease('poly','1') .attr('transform',function(d){ return `translate(${x(0)+20}, ${y(top_n+1)+90})` }); bars .transition() .duration(tickDuration) .ease('poly','1') .attr('width',d => x(d.value)-x(0)-1) .attr('transform',function(d){ return `translate(${x(0)+20}, ${y(d.rank)+90})` }); bars .exit() .transition() .duration(tickDuration) .ease('poly','1') .attr('width',d => x(d.value)-x(0)-1) .attr('transform',function(d){ return `translate(0, ${y(top_n+1)+5})` }) .remove(); let labels = svg.selectAll('.label').data(yearSlice, d => d.name); labels .enter() .append('text') .attr('class','label') .attr('transform',function(d){ return `translate(${x(d.value)+10}, ${y(top_n+1)+5+((y(1)-y(0))/1)-20})` }) .attr('text-anchor','end') .attr('width',d => x(d.value)-x(0)-1) .html(d => d.name) .transition() .duration(tickDuration) .ease('poly','1') .attr('transform',function(d){ return `translate(0, ${y(d.rank)+5+((y(1)-y(0))/1)-20})` }); labels .transition() .duration(tickDuration) .ease('poly','1') .attr('transform',function(d){ return `translate(${x(d.value)+10}, ${y(d.rank)+5+((y(1)-y(0))/1)-20})` }); labels .exit() .transition() .duration(tickDuration) .ease('poly','1') .attr('transform',function(d){ return `translate(${x(d.value)+10}, ${y(top_n+1)+5})` }) .remove(); let valueLabels = svg.selectAll('.valueLabel').data(yearSlice, d => d.name); valueLabels .enter() .append('text') .attr('class','valueLabel') .attr('transform',function(d){ return `translate(${x(d.value)+30}, ${y(top_n+1)+5})` }) // .text(d => d3.format(',.0f')(d.lastValue)) .transition() .duration(tickDuration) .ease('poly','1') .attr('transform',function(d){ return `translate(0, ${y(d.rank)+5+((y(1)-y(0))/1)-20})` }); valueLabels .transition() .duration(tickDuration) .ease('poly','1') .attr('transform',function(d){ return `translate(${x(d.value)+30}, ${y(d.rank)+5+((y(1)-y(0))/1)-20})` }) .tween("text", function(d) { let k = d3.interpolateRound(d.lastValue, d.value); return function(t) { this.textContent = d3.format(',')(k(t)); }; }); valueLabels .exit() .transition() .duration(tickDuration) .ease('poly','1') .attr('transform',function(d){ return `translate(${x(d.value)+30}, ${y(top_n+1)+5})` }) .remove(); yearText.html(~~year); if(year == 2018){ clearInterval(ticker); }; // year = d3.format('.1f')((+year+5) + 0.1); year = year + 1; },tickDuration); }); </script> </body> </html>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js