Published
Edited
Dec 1, 2020
Insert cell
Insert cell
Insert cell
Insert cell
margin = ({top: 10, right: 40, bottom: 30, left: 100})
Insert cell
brandsX = d3.scaleLinear()
.domain([0, d3.max(brandData, d => d.value)])
.range([margin.left, width - margin.right])
Insert cell
brandsY = d3.scaleBand()
.domain(brandsOfYear_top20.map(d => d.name))
.range([margin.top, height - margin.bottom])
.padding(0.1)
Insert cell
brandsColor = d3.scaleOrdinal()
.domain(brandData.map(d => d.category))
.range(d3.schemeAccent)
Insert cell
Insert cell
viewof year = {
const years = d3.extent(brandData, d => d.date.getFullYear());
return Scrubber(
d3.range(years[0], years[1] + 1, 1), // min to max years in 1 year increments
{ autoplay: false, delay: transitionDuration, loop: false } // experiment with these settings!
);
}
Insert cell
import {Scrubber} from "@mbostock/scrubber"
Insert cell
Insert cell
chartElement = {
const svg = d3.create("svg"); // selectAll("svg.canvas").data([1]).join("svg").attr("class", "canvas");
svg.attr("width", width);
svg.attr("height", height)
.attr("font-family", "sans-serif");
return svg.node();
}
Insert cell
{
const svg = d3.select(chartElement);
const marksArea = svg.selectAll("g.marksArea").data([1])
.join("g")
.attr("class", "marksArea");
const groups = marksArea.selectAll("g.datapoint")
.data(brandsOfYear_top20, d => d.name)
.join(
enter => enter.append("g")
.attr("class", "datapoint")
.attr("transform", (d) => `translate(${brandsX(0)}, ${brandsY.range()[1]})`)
.call(appendBars),
update => update.call(updateBars),
exit => exit.remove()
)
.call(
groups => groups.transition().duration(transitionDuration)
.attr("transform", (d) => `translate(${brandsX(0)}, ${brandsY(d.name)})`)
);
svg.selectAll("g.axisLeft").data([1])
.join("g")
.attr("class", "axisLeft")
.attr("transform", `translate(${margin.left}, 0)`)
.transition().duration(transitionDuration).ease(d3.easeLinear)
.call(d3.axisLeft(brandsY));
svg.selectAll("g.axisBottom").data([1])
.join("g")
.attr("class", "axisBottom")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.transition().duration(transitionDuration)
.call(d3.axisBottom(brandsX));
return svg.node();
}
Insert cell
appendBars = (group) => {
const bars = group.append("rect")
.attr("height", brandsY.bandwidth())
.attr("width", (d) => brandsX(d.value) - brandsX(0))
.attr("fill", (d) => brandsColor(d.category));
bars.append("title")
.text(d => `${d.name} (${d.category}): ${d.value}`);
group.append("text")
.text(d => d.name)
.attr("x", d => brandsX(d.value) - brandsX(0) - 5)
.attr("y", brandsY.bandwidth() / 2)
.attr("text-anchor", "end")
.attr("dominant-baseline", "middle")
.attr("font-size", "11px");
}
Insert cell
updateBars = (group) => {
// update bar width
const bars = group.select("rect")
.call(b => b.transition().duration(transitionDuration)
.attr("width", (d) => brandsX(d.value) - brandsX(0))
);
// update hover tooltip
bars.select("title")
.text(d => `${d.name} (${d.category}): ${d.value}`);
// update label position
group.select("text").transition().duration(transitionDuration)
.attr("x", d => brandsX(d.value) - brandsX(0) - 5);
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more