Built with blockbuilder.org
forked from dhexonian's block: M&A circle timeline chart
xxxxxxxxxx
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-annotation/2.3.1/d3-annotation.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html {
background-color: hsl(0, 0%, 100%);
overflow-y: scroll;
font-family: MetricWeb, sans-serif;
}
.bounding {
margin-left: 30px;
margin-right: 30px;
margin-top: 30px;
}
#chart {
width: 100%;
max-width: 660px;
}
.header {
width: 50px;
border-top: 2.5px solid #000000;
margin-bottom: 0px;
display: block;
}
.main {
fill: #33302e;
font-size: 17px;
}
.sub {
fill: #66605c;
display: inline-block;
font-size: 13px;
opacity: 0.90;
}
.y-axis path,
.x-axis path {
stroke: #757575;
shape-rendering: crispEdges;
stroke-width: 1px;
}
.x-axis text,
.y-axis text {
font-size: 11px;
fill: #66605c;
opacity: 0.95;
}
.x-axis .tick line {
stroke: #66605c;
stroke-width: 0.5px;
opacity: 0.5;
}
.y-axis .tick line {
stroke: #66605c;
stroke-width: 0.5px;
opacity: 0.2;
}
.footnote {
font-size: 9px;
opacity: 0.85;
fill: #66605c;
}
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #333;
display: none;
font-size: 10px;
/* left: 250px; */
/* margin-top: -60px; */
position: absolute;
text-align: center;
padding: 5px;
/* top: 150px; */
width: 110px;
z-index: 10;
}
.tooltip > *:not(:last-child) {
margin-bottom: 5px;
}
</style>
<body>
<div class="bounding">
<div class="header"></div>
<div id="chart"></div>
</div>
</body>
<script>
var dataset;
var makeAnnotations;
var connectors;
var dxdy;
var xy;
d3.csv("data.csv", function (err, data) {
dataset = data;
var million = 1000000;
var billion = 1000000000;
var formatTime = d3.timeFormat("%m-%d-%Y");
dataset = dataset.filter(function (d) {
return d.Value !== "—";
})
function format(val) {
var formattedValue;
if (val > billion) {
formattedValue = val / billion;
formattedValue = "$" + formattedValue.toFixed(1) + "B"
} else {
formattedValue = val / million;
formattedValue = "$" + formattedValue.toFixed(1) + "M"
}
return formattedValue
}
dataset.forEach(function (d, i) {
d.ID = i.toString();
d.formattedDate = d.Date;
d.Date = new Date(d.Date);
d.Value = parseFloat(d.Value.split(',').join(''));
d.formattedVal = format(d.Value);
});
draw(dataset);
});
function draw(data) {
"use strict";
var margin = {
top: 50,
right: 100,
bottom: 40,
left: 10
},
container_width = 660,
container_height = 400,
width = container_width - margin.left - margin.right,
height = container_height - margin.top - margin.bottom;
var svg = d3
.select("#chart")
.append("svg")
.attr("id", "svg_container")
.attr("viewBox", `0 0 ${container_width} ${container_height}`)
.attr("preserveAspectRatio", "xMidYMid")
.append("g")
.attr("id", "container")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var main_padding = 20;
var sub_padding = 45;
var chart = d3.select('#svg_container')
.append('g')
.attr('id', 'text-label')
chart.append("text")
.attr("class", "main")
.text("Microsoft's M&A History")
.attr(
"transform",
"translate(" + 0 + "," + main_padding + ")"
);
chart
.append("text")
.attr("class", "sub")
.text("Selected acquisitions for which there is disclosed purchase price (USD)")
.attr(
"transform",
"translate(" + 0 + "," + sub_padding + ")"
);
var footer_data = ["Source: Microsoft Corporate Development"]
svg
.append("g")
.attr('class', 'footnote')
.selectAll('text')
.data(footer_data)
.enter()
.append('text')
.text(function (d) { return d; })
.attr("transform", function (d, i) {
return "translate(" + (-margin.left) + "," + (height + margin.bottom - 10) + ")"
});
var x_extent = d3.extent(data, function (d) {
return d.Date;
})
x_extent[0] = d3.timeYear.floor(x_extent[0])
x_extent[1] = d3.timeYear.ceil(x_extent[1])
var x_scale = d3
.scaleTime()
.domain(x_extent)
.range([0, width]);
var y_extent = d3.extent(data, function (d) {
return d.Value;
})
var r_scale = d3
.scaleSqrt()
.domain(y_extent)
.range([5, 100]);
var calendar = d3.timeYear
.every(1)
.range(new Date(x_extent[0]), d3.timeYear.offset(new Date(x_extent[1])), 1);
var xAxis = svg.append("g").attr("class", "x-axis");
xAxis
.attr("transform", "translate(0," + height / 2 + ")")
.call(
d3
.axisBottom(x_scale)
.tickValues(calendar)
.tickSizeInner(15)
.tickFormat(function (d) {
return d.getFullYear().toString().slice(-2) + ""
})
)
.call(g => g.select(".domain").remove())
d3.select(".x-axis")
.selectAll(".tick")
.each(function (d, i) {
if (i % 2 != 0) {
d3.select(this).select('text').remove();
d3.select(this).select('line').attr('y2', 7.5)
}
});
var circles = svg.append('g')
.attr('class', 'circles')
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr('stroke', '#1d90cf')
.attr('stroke-width', '0.3')
.attr('id', function (d) {
return 'circle' + "_" + d.ID
})
.attr("cx", function (d) {
return x_scale(d.Date);
})
.attr("cy", function (d) {
return height / 2;
})
.attr("r", function (d) {
return r_scale(d.Value);
})
.attr("fill", function (d) {
return "#93d2e6";
})
.attr('opacity', 0.3)
.attr('pointer-events', 'none')
var centroids = svg.append('g')
.attr('class', 'centroids')
.selectAll("brewski")
.data(data)
.enter()
.append("circle")
.attr('id', function (d) {
return 'centroid' + "_" + d.ID
})
.attr("cx", function (d) {
return x_scale(d.Date);
})
.attr("cy", function (d) {
return height / 2;
})
.attr("r", function (d) {
return 2.5;
})
.attr("fill", function (d) {
return "#b3315f";
})
.attr('opacity', 0.8)
var tooltip = d3
.select("#chart")
.append("div")
.attr("class", "tooltip");
tooltip
.append("div")
.attr("class", "tooltip-date");
tooltip
.append("div")
.attr("class", "tooltip-value");
tooltip
.append("div")
.attr("class", "tooltip-target");
centroids.on("mouseover", function (d) {
// highlight the circle that is involved in this
d3.select(this).attr("r", 4);
var getId = d3.select(this).attr('id').split('_')[1];
var getCircle = d3.select(`#circle_${getId}`)
getCircle.attr('stroke', '#b3315f')
.attr('fill', '#b3315f')
tooltip.select(".tooltip-date").html(d.formattedDate);
tooltip.select(".tooltip-value").html(d.formattedVal);
tooltip.select(".tooltip-target").html(d.Target);
tooltip.style("display", "block");
tooltip
.style("top", d3.event.y + 20 + "px")
.style("left", d3.event.x - 50 + "px")
.style("pointer-events", "none");
});
centroids.on("mouseout", function () {
d3.select(this).attr("r", 2.5);
var getId = d3.select(this).attr('id').split('_')[1];
var getCircle = d3.select(`#circle_${getId}`)
getCircle.attr('stroke', '#1d90cf')
.attr("fill", "#93d2e6");
tooltip.style("display", "none");
});
const type = d3.annotationCustomType(
d3.annotationCallout,
{
"className": "custom",
"note": { "lineType": "horizontal" }
})
var dxdy;
var connectors;
dxdy = [[-60, 56], [20, 112], [16, -100], [-18, 91], [-50, -93]];
var max5 = data.sort(function (x, y) {
return d3.ascending(x.Value, y.Value);
})
max5 = max5.slice(-5);
const annotations = max5.map(function (d, i) {
return {
note: {
label: d.formattedDate,
title: d.formattedVal + ' acquisiton of ' + d.Target,
bgPadding: { "top": 15, "left": 10, "right": 10, "bottom": 10 }
},
className: "show-bg",
x: x_scale(d.Date),
y: height / 2,
dx: dxdy[i][0],
dy: dxdy[i][1]
}
});
const makeAnnotations = d3.annotation()
// .editMode(true)
//also can set and override in the note.padding property
//of the annotation object
// .textWrap(140)
.textWrap(90)
.notePadding(5)
.type(type)
//accessors & accessorsInverse not needed
//if using x, y in annotations JSON
.annotations(annotations)
d3.select("#container")
.append("g")
.attr("class", "annotation-group")
.style('font-size', 9.5)
.style("fill", "black")
// .style("stroke-dasharray", 5)
.style('stroke-width', 0.75)
.call(makeAnnotations)
d3.select('.footnote')
.on('click', function () {
var connectors = makeAnnotations.annotations().map(function (d) {
return d.connector.points
})
var dxdy = makeAnnotations.annotations().map(function (d) {
return [
d._dx, d._dy
];
})
var xy = makeAnnotations.annotations().map(function (d) {
return [
d._x, d._y
];
})
console.log(JSON.stringify(dxdy), 'dxdy');
console.log(JSON.stringify(xy), 'xy');
console.log(JSON.stringify(connectors), 'connectors');
})
}
</script>
</html>
https://d3js.org/d3.v4.min.js
https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-annotation/2.3.1/d3-annotation.min.js