D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
jwilber
Full window
Github gist
one hundred people
Built with
blockbuilder.org
<!DOCTYPE html> <html lang="en"> <head> <link href="https://fonts.googleapis.com/css?family=PT+Sans|PT+Serif|Roboto" rel="stylesheet"> <link href="../codecss/prism.css" rel="stylesheet" /> <meta charset="UTF-8"> <title>One Hundred People</title> <style type="text/css"> body { background-color: white; font-family: 'consolas', serif; } #container { max-width: 1000px; margin-left: auto; margin-right: auto; margin-top: 5px; padding: 1px 10px 10px 10px; background-color: white; box-shadow: 1px 1px 1px 1px #fff; } h1 { font-weight: 300; color: darkslategray; font-family: "consolas", sans-serif; font-size: 56px; margin-top: 10px; margin-bottom: 0px; } h2 { font-weight: 300; color: darkslategray; font-family: "PT Sans", sans-serif; font-size: 32px; margin-bottom: 20px; margin-top: -10px; } p { font-size: 18px; margin-top: 4px; margin-bottom: 8px; color: #333; } a { color: darkslategray; } a:hover { color: darkslategray; } .footer { font-size: 14px; margin-top: 0px; } #svganchor { margin: auto; } .criterionList { font-size: 12px; color: #222; text-transform: uppercase; cursor: pointer; } .chartTitle { font-family: "PT Sans", sans-serif; font-weight: 700; font-size: 22px; color: #444; } .chartSubTitle { font-family: "PT Sans", sans-serif; font-size: 18px; color: #444; } .nodesData { font-family: 'PT Serif', serif; font-size: 14px; } .nodesDataSpan { font-family: 'PT Serif', serif; font-size: 18px; font-weight: 700; } .preciseNumberTitle { font-family: "PT Sans", sans-serif; font-size: 12px; color: #444; } .nodesDataPrecise { font-family: 'PT Serif', serif; font-size: 12px; color: #444; } svg { background-color: white; } </style> <script src="https://d3js.org/d3.v4.min.js"></script> </head> <body> <div id="container"> <h1>One Hundred People</h1> <div id="svganchor"></div> <br> </div> <script type="text/javascript"> var totalData = [{ criterion: "Sex", description: "Distribution of males and females", groups: [{ name: "Male", value: 50.43 }, { name: "Female", value: 49.57 }] }, { criterion: "Body weight", description: "People above a weight considered healthy", groups: [{ name: "Overweight", value: 21.88 }, { name: "Not overweight", value: 78.12 }] },{ criterion: "Undernourishment", description: "People with sufficient quantity or quality of nourishment", groups: [{ name: "Undernourished", value: 10.00 }, { name: "Not Undernourished", value: 90.00 }] },{ criterion: "Age", description: "Distribution by age", groups: [{ name: "Below 14 years of age", value: 26.30 }, { name: "Between 15 and 64 years", value: 65.90 }, { name: "65 years and over", value: 7.80 }] },{ criterion: "Internet access", description: "People with an internet connection", groups: [{ name: "With connection", value: 47.31 }, { name: "Without connection", value: 52.69 }] },{ criterion: "Homosexuality", description: "Distribution by sexual orientation", groups: [{ name: "Heterosexual", value: 97.4 }, { name: "Homosexual or Bisexual", value: 2.6 }] },{ criterion: "Mobile phone", description: "People using mobile phones", groups: [{ name: "Using a mobile", value: 63.90 }, { name: "Not using a mobile", value: 36.10 }] },{ criterion: "Electricity", description: "People with access to electricity", groups: [{ name: "Access", value: 84.58 }, { name: "No access", value: 15.42 }] },{ criterion: "Water", description: "People with access to clean water", groups: [{ name: "No access", value: 8.39 }, { name: "Access", value: 91.61 }] },{ criterion: "Unemployment", description: "Unemployment rate", groups: [{ name: "Unemployed", value: 5.93 }, { name: "Employed", value: 94.07 }] },{ criterion: "Rule of the road", description: "Side of the road in bidirectional traffic", groups: [{ name: "Drive on the right side", value: 66.00 }, { name: "Drive on the left side", value: 34.00 }] },{ criterion: "Income", description: "Dollars per day", groups: [{ name: "Less than $2", value: 71.00 }, { name: "Between $10 and $20", value: 13.00 }, { name: "More than $20", value: 16.00 }] },{ criterion: "Religion", description: "Distribution by religion", groups: [{ name: "Christian", value: 31.50 }, { name: "Muslim", value: 22.32 }, { name: "Atheist", value: 15.35 }, { name: "Hindu", value: 13.95 }, { name: "Other", value: 16.88 }] },{ criterion: "Continent", description: "People from different continents", groups: [{ name: "Africa", value: 14.5 }, { name: "Europe", value: 11.4 }, { name: "Americas", value: 13.2 },{ name: "Asia", value: 60.3 }, { name: "Oceania", value: 0.6 }] },{ criterion: "Literacy", description: "People able to read and write", groups: [{ name: "Literate", value: 83.7 }, { name: "Illiterate", value: 16.3 }] },{ criterion: "Handedness", description: "Preference to use one hand rather than the other", groups: [{ name: "Left-handedness", value: 8.00 }, { name: "Right-handedness", value: 92.00 }] },{ criterion: "Blood type", description: "ABO blood group system", groups: [{ name: "A", value: 33.90 }, { name: "B", value: 16.20 }, { name: "AB", value: 5.10 }, { name: "O", value: 44.80 }] },{ criterion: "Sanitation", description: "People with access to basic sanitation, like flush toilets and treated sewage", groups: [{ name: "Access", value: 67.52 }, { name: "No access", value: 32.48 }] }]; var headPath = "M251.249,127.907c17.7,0,32.781-6.232,45.254-18.7c12.467-12.467,18.699-27.554,18.699-45.253 c0-17.705-6.232-32.783-18.699-45.255C284.029,6.233,268.948,0,251.249,0c-17.705,0-32.79,6.23-45.254,18.699 c-12.465,12.469-18.699,27.55-18.699,45.255c0,17.703,6.23,32.789,18.699,45.253C218.462,121.671,233.549,127.907,251.249,127.907 z"; var bodyPath = "M381.438,153.029c-10.663-10.657-23.599-15.987-38.827-15.987H159.889c-15.23,0-28.171,5.327-38.831,15.987 c-10.657,10.66-15.987,23.604-15.987,38.831v118.776c0,7.611,2.663,14.079,7.993,19.407s11.803,7.994,19.414,7.994 c7.614,0,14.087-2.666,19.417-7.994c5.327-5.328,7.994-11.796,7.994-19.407V210.134h18.271v260.379 c0,8.754,3.144,16.275,9.423,22.559c6.28,6.276,13.796,9.418,22.554,9.418s16.278-3.142,22.557-9.418 c6.28-6.283,9.42-13.802,9.42-22.559V338.038h18.27V470.52c0,8.75,3.141,16.275,9.421,22.552 c6.279,6.283,13.802,9.425,22.556,9.425c8.76,0,16.279-3.142,22.559-9.425c6.283-6.283,9.418-13.798,9.418-22.552V210.134h18.274 v100.495c0,7.618,2.665,14.086,7.994,19.411c5.328,5.331,11.799,7.994,19.41,7.994c7.61,0,14.093-2.663,19.417-7.994 c5.328-5.325,7.994-11.793,7.994-19.411V191.86C397.43,176.63,392.095,163.689,381.438,153.029z"; var criterionListData = totalData.map(d => d.criterion); var divList = d3.select("#controler"); var criterionList = divList.selectAll(".criterionList") .data(criterionListData) .enter() .append("p") .attr("class", "criterionList"); criterionList.text(d => d); var width = 850, height = 500; var svg = d3.select("#svganchor") .append("svg") .attr("width", width) .attr("height", height); var padding = [10, 10, 10, 10]; var xScale = d3.scalePoint() .range([padding[3], width - padding[1]]) .padding(0.5); var barScale = d3.scaleLinear() .range([padding[3], width - padding[1]]) .domain([0,100]); var colorScale = d3.scaleOrdinal() .range(['#1b9e77','#d95f02','#7570b3','#e7298a','#66a61e']); var dataNodes = d3.range(100).map(() => ({id: "", xPosition:""})); var simulation = d3.forceSimulation() .force("collide", d3.forceCollide(12)) .force("x", d3.forceX(d => d.xPosition).strength(0.2)) .force("y", d3.forceY(height / 2)) .velocityDecay(0.7); var chartTitle = svg.append("text") .attr("class", "chartTitle") .attr("x", width / 2) .attr("y", 20) .attr("text-anchor", "middle"); var chartSubTitle = svg.append("text") .attr("class", "chartSubTitle") .attr("x", width / 2) .attr("y", 44) .attr("text-anchor", "middle"); var preciseNumberTitle = svg.append("text") .attr("class", "preciseNumberTitle") .attr("y", 424) .attr("x", padding[3]) .text("Precise numbers:"); draw("Sex"); function draw(criterion){ chartTitle.text(criterion); var filteredData = totalData.filter(d => d.criterion === criterion); chartSubTitle.text(filteredData[0].description); xScale.domain(filteredData[0].groups.map(d => d.name)); colorScale.domain(filteredData[0].groups.map(d => d.name)); var counterData = 0; filteredData[0].groups.forEach(d=>{ var rounded = Math.round(d.value); for(var i = counterData; i < rounded + counterData; i++){ dataNodes[i].id = d.name; dataNodes[i].xPosition = xScale(d.name); }; counterData += rounded; }); var nodes = svg.selectAll(".g") .data(dataNodes) .enter() .append("g"); nodes.attr("fill", d => colorScale(d.id)); nodes.append("path") .attr("d", headPath) .attr("transform", "translate(-12,0) scale(0.045,0.045)"); nodes.append("path") .attr("d", bodyPath) .attr("transform", "translate(-12,0) scale(0.045,0.045)"); var nodesData = svg.selectAll(".nodesData") .data(filteredData[0].groups, d => d.name) .enter() .append("text") .attr("class", "nodesData") .attr("text-anchor", "middle") .attr("y", 100) .attr("x", d => xScale(d.name)) .text(d => d.name + ": ") .append("tspan") .attr("class", "nodesDataSpan") .text(d => Math.round(d.value)); var nodesDataPrecise = svg.selectAll(".nodesDataPrecise") .data(filteredData[0].groups, d => d.name) .enter() .append("text") .attr("class", "nodesDataPrecise") .attr("text-anchor", "middle") .attr("y", 440) .attr("x", d => xScale(d.name)) .text(d => d.name + ": " + d.value + "%"); dataNodes.forEach(d =>{ d.x = width / 2; d.y = height / 2; }); simulation.nodes(dataNodes) .on("tick", tick); var bars = svg.selectAll(".bars") .data(filteredData[0].groups); var barsEnter = bars.enter() .append("rect") .attr("class", "bars") .attr("y", 470) .attr("x", (d,i) => i ? barScale(filteredData[0].groups[i-1].value) : barScale(0)) .attr("height", 20) .attr("width", d => barScale(d.value) - padding[3]) .attr("fill", d => colorScale(d.name)); var polylines = svg.selectAll(".polylines") .data(filteredData[0].groups) .enter() .append("polyline") .attr("class", "polylines") .attr("points", d => "" + xScale(d.name) + ",470 " + xScale(d.name) + ",446") .attr("stroke-width", 1) .attr("stroke", "darkslategray"); function tick(){ nodes.attr('transform', (d) => { if (d.y < 120) { d.y = 120 }; if (d.y > 380) { d.y = 380 }; return 'translate(' + (d.x) + ',' + (d.y) + ')'; }); }; function redraw(criterion){ chartTitle.text(criterion); var filteredData = totalData.filter(d => d.criterion === criterion); chartSubTitle.text(filteredData[0].description); xScale.domain(filteredData[0].groups.map(d => d.name)); colorScale.domain(filteredData[0].groups.map(d => d.name)); var counterData = 0; filteredData[0].groups.forEach(d=>{ var rounded = Math.round(d.value); for(var i = counterData; i < rounded + counterData; i++){ dataNodes[i].id = d.name; dataNodes[i].xPosition = xScale(d.name); }; counterData += rounded; }); nodes.transition() .duration(500) .attr("fill", d => colorScale(d.id)); var newNodesData = svg.selectAll(".nodesData") .data(filteredData[0].groups, d => d.name + d.value); var newNodesDataExit = newNodesData.exit() .remove(); var newNodesDataEnter = newNodesData.enter() .append("text") .attr("class", "nodesData") .attr("text-anchor", "middle") .attr("y", 100) .attr("x", d => xScale(d.name)) .text(d => d.name + ": ") .append("tspan") .attr("class", "nodesDataSpan") .text(d => Math.round(d.value)); var newNodesDataPrecise = svg.selectAll(".nodesDataPrecise") .data(filteredData[0].groups, d => d.name + d.value); newNodesDataPrecise.exit() .transition() .delay(750) .duration(10) .remove(); newNodesDataPrecise.enter() .append("text") .attr("class", "nodesDataPrecise") .attr("text-anchor", "middle") .attr("y", 440) .attr("x", d => xScale(d.name)) .transition() .delay(750) .duration(10) .text(d => d.name + ": " + d.value + "%"); var bars = svg.selectAll(".bars") .data(filteredData[0].groups); var barExit = bars.exit() .transition() .delay(750) .duration(500) .attr("x", width - padding[1]) .attr("width", 0) .remove(); var barsEnter = bars.enter() .append("rect") .attr("class", "bars") .attr("x", width - padding[1]) .merge(bars) .attr("y", 470) .attr("height", 20); barsEnter.transition() .delay(750) .duration(500) .attr("x", (d,i) =>{ if(i === 0){ return barScale(0); } else { var counter = 0; for(var j = 0; j < i; j++){ counter += filteredData[0].groups[j].value; }; return barScale(counter); } }) .attr("width", d => barScale(d.value) - padding[3]) .attr("fill", d => colorScale(d.name)); var polylines = svg.selectAll(".polylines") .data(filteredData[0].groups); var polylinesExit = polylines.exit() .transition() .delay(750) .duration(500) .attr("points", "" + width + ",470 " + width + ",458 " + width + ",458 " + width + ",446") .remove(); var polylinesEnter = polylines.enter() .append("polyline") .attr("class", "polylines") .attr("points", "" + width + ",470 " + width + ",458 " + width + ",458 " + width + ",446") .merge(polylines) .transition() .delay(750) .duration(500) .attr("points", (d,i) =>{ if(i === 0){ return "" + (barScale(0) + (barScale(filteredData[0].groups[0].value))/2) + ",470 " + (barScale(0) + (barScale(filteredData[0].groups[0].value))/2) + ",458 " + xScale(d.name) + ",458 " + xScale(d.name) + ",446"; } else { var counter = 0; for(var j = 0; j < i; j++){ counter += filteredData[0].groups[j].value; }; var thisWidth = barScale(counter); return "" + (thisWidth + (barScale(d.value) - padding[3])/2) + ",470 " + (thisWidth + (barScale(d.value) - padding[3])/2) + ",458 " + xScale(d.name) + ",458 " + xScale(d.name) + ",446"; } }) .attr("stroke-width", 1) .attr("fill", "none") .attr("stroke", "darkslategray"); setTimeout(function(){ simulation.nodes(dataNodes); simulation.alpha(0.8).restart(); }, 750) //end of redraw }; d3.selectAll(".criterionList").on("click", function(d){ redraw(d); }); //end of draw }; </script> </body> </html>
https://d3js.org/d3.v4.min.js