drawChart = (container) => {
const width = 400;
const height = 400;
const svg = container
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", `translate(${width / 2 - 50},${height / 2 - 30})`);
const radiusScale = d3
.scaleSqrt()
.domain([0, d3.max(data, (d) => d.sum)])
.range([10, 100]);
const circles = svg
.selectAll("circle")
.data((d) => d[1])
.join("circle")
.attr("r", (d) => radiusScale(d.sum))
.attr("fill", (d) => colorScale(d.chart_category))
.attr("opacity", 0.9)
.attr("stroke", (d) => newShade(colorScale(d.chart_category), -50))
.attr("stroke-width", 1.25);
const tooltip = container.append("div").attr("class", "tooltip");
array.forEach((d) => {
const simulation = d3
.forceSimulation(d[1])
.force("xPosition", d3.forceX((d) => radiusScale(d.sum)).strength(0.2))
.force("yPosition", d3.forceY((d) => radiusScale(d.sum)).strength(0.2))
.force(
"collision",
d3.forceCollide().radius((d) => radiusScale(d.sum) + 2)
);
simulation
.on("tick", () => {
circles.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
circles
.on("mouseover", (e, d) => {
const tooltipContent = `
<div style="text-transform: uppercase; font-size: 0.65rem; font-weight: 700;">${
d.chart_category === "Ad serving/targeting companies"
? "Ad serving/targeting"
: d.chart_category
}</div>
<div style="font-size: 1.1rem; color: #fff;">${d.vendor}</div>
<div style="color: #fff;">Received <strong>${d3.format("$,.2s")(
d.sum
)}</strong></div>
`;
tooltip.html(tooltipContent);
tooltip
.style(
"transform",
`translate(${e.pageX + 5}px,${e.pageY - 100}px)`
)
.style("color", newShade(colorScale(d.chart_category), 50))
.transition()
.duration(500)
.style("opacity", 0.9);
})
.on("mouseout", () => tooltip.transition().style("opacity", 0));
svg
.selectAll("text")
.data((d) => d[1])
.join("text")
.attr("class", "company-labels")
.text((d) =>
d.vendor
.replace("Bustle Digital Group", "Bustle")
.replace("New Tang Dynasty", "NTD")
.replace("Programmatic Mechanics", "PM")
.replace("MightyHive", "MH")
.replace("OM Trade Desk", "OM")
.replace("MobileFuse", "MF")
.replace(
"Snapchat",
d.campaign_flight == "Dec '19-Jan '20" ? "SC" : "Snapchat"
)
.replace(
"Facebook",
d.campaign_flight == "Dec '19-Jan '20" ? "FB" : "Facebook"
)
)
.attr("x", (d) => d.x)
.attr("y", (d) => d.y)
.attr("text-anchor", "middle")
.style("font-size", "0.8rem");
const campaignLabels = svg
.append("text")
.html((d) => d[0])
.attr("text-anchor", "middle")
.attr("x", 50)
.attr("y", -140)
.style("font-size", "1rem")
.style("font-weight", 500);
campaignLabels
.append("tspan")
.attr("x", 50)
.attr("dy", "1.25em")
.html(function (d) {
const accumulator = d[1]
.map((d) => d.sum)
.reduce((accumulator, currentValue) => {
return currentValue + accumulator;
}, 0);
return `Campaign total: ${
accumulator > 1000000
? d3.format("$,.1s")(accumulator).toLowerCase()
: d3.format("$,.3s")(accumulator).toLowerCase()
}`;
})
.style("font-size", "0.9rem")
.style("font-weight", 400)
.style("fill", "#989898");
});
});
}