Created
December 4, 2018 07:16
-
-
Save oivoodoo/1affe7683aaf618653707a4724e4c7e5 to your computer and use it in GitHub Desktop.
D3 chart example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// D3 Chart Example | |
import "../styles/index.scss"; | |
import * as d3 from "d3"; | |
d3.csv("/public/data/File.csv", function(d) { | |
return { value: +d.NumericColumnB, name: d.StringColumnA }; | |
}).then(function(data) { | |
// sort data by value | |
data.sort(function(a, b) { | |
return a.value - b.value; | |
}); | |
const svgMainWidth = 1060, | |
svgMainHeight = 960; | |
// Edit this to change the context to focus proportions | |
const contextScale = 1 / 6; | |
const margin = { top: 10, right: 10, bottom: 20 }; | |
const margin2 = { top: 10, bottom: 20, left: 40 }; | |
const combinedRenderWidth = | |
svgMainWidth - margin.right * 2 - margin2.left * 2 - 10; | |
margin2.right = | |
svgMainWidth - | |
Math.round(combinedRenderWidth * contextScale) - | |
margin2.left; | |
margin.left = svgMainWidth - margin2.right + 40 + 10; | |
const width = svgMainWidth - margin.left - margin.right, | |
width2 = svgMainWidth - margin2.left - margin2.right, | |
height = svgMainHeight - margin.top - margin.bottom; | |
// this div is used for tooltips | |
const tooltipDiv = d3 | |
.select("body") | |
.append("div") | |
.attr("class", "tooltip") | |
.style("opacity", 0); | |
// scales | |
const x = d3.scaleLinear().range([0, width]); | |
const y = d3 | |
.scaleBand() | |
.rangeRound([height, 0]) | |
.paddingInner(0.1); | |
const x2 = d3.scaleLinear().range([0, width2]); | |
const y2 = d3 | |
.scaleBand() | |
.rangeRound([height, 0]) | |
.paddingInner(0.1); | |
const textScale = d3 | |
.scaleLinear() | |
.domain([15, 50]) | |
.range([20, 6]) | |
.clamp(true); | |
const yZoom = d3 | |
.scaleLinear() | |
.range([0, height]) | |
.domain([0, height]); | |
const xAxis = d3.axisBottom(x); | |
const yAxis = d3.axisLeft(y); | |
const yAxis2 = d3.axisLeft(y2).tickValues([]); | |
const svg = d3 | |
.select("body") | |
.append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom); | |
//Add the clip path for the main bar chart | |
svg | |
.append("defs") | |
.append("clipPath") | |
.attr("id", "clip") | |
.append("rect") | |
.attr("width", width + margin.left) | |
.attr("height", height); | |
// focus is the main bar chart | |
const focus = svg | |
.append("g") | |
.attr("class", "focus") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")") | |
.append("g") | |
.attr("class", "clip-class"); | |
// context is the minimap area | |
const context = svg | |
.append("g") | |
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")"); | |
const brush = d3 | |
.brushY() | |
.extent([[0, 0], [width2, height]]) | |
// .extent([[0, y2.range()[0]], [width2, y2.range()[1]]]) | |
.on("brush", brushmove); | |
x.domain( | |
d3.extent(data, function(d) { | |
return d.value; | |
}) | |
).nice(); | |
y.domain( | |
data.map(function(d) { | |
return d.name; | |
}) | |
); | |
x2.domain(x.domain()); | |
y2.domain(y.domain()); | |
focus | |
.selectAll(".bar") | |
.data(data) | |
.enter() | |
.append("rect") | |
.attr("class", function(d) { | |
return "bar bar--" + (d.value < 0 ? "negative" : "positive"); | |
}) | |
.attr("x", function(d) { | |
return x(Math.min(0, d.value)); | |
}) | |
.attr("y", function(d) { | |
return y(d.name); | |
}) | |
.attr("width", function(d) { | |
return Math.abs(x(d.value) - x(0)); | |
}) | |
.attr("height", y.bandwidth()) | |
// tooltip | |
.on("mouseover", function(d) { | |
tooltipDiv | |
.transition() | |
.duration(200) | |
.style("opacity", 1.0); | |
tooltipDiv | |
.html("Value: " + d.value) | |
.style("left", d3.event.pageX + "px") | |
.style("top", d3.event.pageY - 28 + "px"); | |
}) | |
.on("mouseout", function(d) { | |
tooltipDiv | |
.transition() | |
.duration(500) | |
.style("opacity", 0); | |
}); | |
svg | |
.select(".focus") | |
.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
const negativeTicks = focus | |
.append("g") | |
.attr("class", "y axis") | |
.attr("transform", "translate(" + x(0) + ",0)") | |
.call(yAxis) | |
.selectAll(".tick") | |
.filter(function(d, i) { | |
return data[i].value < 0; | |
}); | |
negativeTicks.select("line").attr("x2", 6); | |
negativeTicks | |
.select("text") | |
.attr("x", 9) | |
.style("text-anchor", "start"); | |
context | |
.selectAll(".bar") | |
.data(data) | |
.enter() | |
.append("rect") | |
.attr("class", function(d) { | |
return "bar bar--" + (d.value < 0 ? "negative" : "positive"); | |
}) | |
.attr("x", function(d) { | |
return x2(Math.min(0, d.value)); | |
}) | |
.attr("y", function(d) { | |
return y2(d.name); | |
}) | |
.attr("width", function(d) { | |
return Math.abs(x2(d.value) - x2(0)); | |
}) | |
.attr("height", y2.bandwidth()); | |
context | |
.append("g") | |
.attr("class", "y axis") | |
.attr("transform", "translate(" + x2(0) + ",0)") | |
.call(yAxis2); | |
context | |
.append("g") | |
.attr("class", "y brush") | |
.call(brush) | |
.call(brush.move, [0, height / 3]) // initial brush position | |
.selectAll("rect") | |
.attr("x", 0) | |
.attr("width", width2); | |
//Function runs on a brush move - to update the big bar chart | |
function updateMainChart() { | |
const bars = focus.selectAll(".bar"); | |
//UPDATE | |
bars | |
.attr("y", function(d) { | |
return y(d.name); | |
}) | |
.attr("height", y.bandwidth()) | |
.attr("x", function(d) { | |
return x(Math.min(0, d.value)); | |
}) | |
.attr("width", function(d) { | |
return Math.abs(x(d.value) - x(0)); | |
}); | |
const negativeTicks = focus | |
.select(".y.axis") | |
.selectAll(".tick") | |
.filter(function(d, i) { | |
return data[i].value < 0; | |
}); | |
negativeTicks.select("line").attr("x2", 6); | |
negativeTicks | |
.select("text") | |
.attr("x", 9) | |
.style("text-anchor", "start"); | |
// EXIT | |
bars.exit().remove(); | |
} | |
//First function that runs on a brush move | |
function brushmove() { | |
const selection = d3.event.selection; | |
//Which bars are in selection | |
const selected = y2.domain().filter(function(d) { | |
return selection[0] - y2.bandwidth() <= y2(d) && y2(d) <= selection[1]; | |
}); | |
//Update the labels size | |
d3.selectAll(".y.axis text").style("font-size", textScale(selected.length)); | |
const originalRange = yZoom.range(); | |
yZoom.domain(selection); | |
y.domain( | |
data.map(function(d) { | |
return d.name; | |
}) | |
); | |
y.rangeRound([yZoom(originalRange[1]), yZoom(originalRange[0])]) | |
.paddingInner(0.4) | |
.paddingOuter(0); | |
//Update the y axis of the big chart | |
focus.select(".y.axis").call(yAxis); | |
updateMainChart(); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment