Experiment to visualise page size data using Sunburst charts
Data was generated using WebPageTest and the raw CSV read directly from WPT
(can only cope with data from a single run ATM)
license: cc-by-sa-4.0 |
<html> | |
<head> | |
<style> | |
body { | |
font-family: sans-serif; | |
font-size: 12px; | |
font-weight: 400; | |
background-color: #fff; | |
width: 960px; | |
min-height: 700px; | |
margin-top: 10px; | |
} | |
#details { | |
min-height: 2em; | |
} | |
</style> | |
<script src="//d3js.org/d3.v4.min.js"></script> | |
<script src=" https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> | |
</head> | |
<body> | |
<div id="details"> | |
Mouse over a segment | |
</div> | |
<div id="chart"> | |
</div> | |
<script> | |
var csv = "https://www.webpagetest.org/result/170811_Z1_e9728d4262dc67429faa87a8aa80e729/170811_Z1_e9728d4262dc67429faa87a8aa80e729_news.bbc.co.uk_requests.csv"; | |
var width = 500, | |
height = 500, | |
radius = (Math.min(width, height) / 2) - 10, | |
color = d3.scaleOrdinal(d3.schemeCategory10); | |
var x = d3.scaleLinear() | |
.range([0, 2 * Math.PI]); | |
var y = d3.scaleSqrt() | |
.range([0, radius]); | |
var formatNumber = d3.format(",d"); | |
var arc = d3.arc() | |
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x0))); }) | |
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x1))); }) | |
.innerRadius(function(d) { return Math.max(0, y(d.y0)); }) | |
.outerRadius(function(d) { return Math.max(0, y(d.y1)); }); | |
var partition = d3.partition(); | |
var nest = d3.nest() | |
.key(function(d) { return d.host; }) | |
.key(function(d) { return d.type; }); | |
var svg = d3.select("#chart").append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.append("g") | |
.attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")"); | |
d3.csv(csv, type, function(error, data) { | |
if (error) throw error; | |
var root = d3.hierarchy({values: nest.entries(data)}, function(d) { return d.values; }) | |
.sum(function(d) { return d.size; }); | |
root.sum(function(d) { return d.size; }); | |
svg.selectAll("path") | |
.data(partition(root).descendants()) | |
.enter().append("path") | |
.attr("display", function(d) { return d.depth ? null : "none"; }) // hide inner ring | |
.attr("d", arc) | |
.style("stroke", "#fff") | |
.style("fill", function(d) { return typeColors(d.data.type || d.data.key); }) | |
.on("mouseover", mouseover ) | |
.on("mouseleave", mouseleave ) | |
.append("title") | |
.text(function(d) { return (d.data.url || d.data.key) + "\n" + formatNumber(d.value); }); | |
}); | |
function mouseover(d) { | |
var details = d3.select("#details") | |
.text((d.data.url || d.data.key) + " (" + formatNumber(d.value) + " bytes)"); | |
} | |
function mouseleave(d) { | |
var details = d3.select("#details") | |
.text(""); | |
} | |
function type(d) { | |
return { | |
host: d["Host"], | |
url: d["URL"], | |
type: normaliseType(d["Content Type"]), | |
size: d["Bytes In"] | |
} | |
} | |
/* | |
* Order entries by resource type and then size (descending) | |
*/ | |
function orderEntries(a, b) { | |
var result = a.type.localeCompare(b.type); | |
if(result !== 0) { | |
return result; | |
} | |
else { | |
return b.size - a.size; | |
} | |
} | |
/* | |
* Convert mimetype into normalised value e.g. html, css, js etc. | |
*/ | |
function normaliseType(type) { | |
switch(type.toLowerCase()) { | |
case "text/html": | |
return "html"; | |
break; | |
case "text/css": | |
return "css"; | |
break; | |
case "application/x-javascript": | |
case "application/javascript": | |
case "application/ecmascript": | |
case "text/javascript": | |
case "text/ecmascript": | |
return "js"; | |
break; | |
case "application/font-woff": | |
case "font/woff2": | |
case "application/vnd.ms-fontobject ": | |
case "application/font-sfnt": | |
return "font"; | |
break; | |
case "image/jpeg": | |
case "image/png": | |
case "image/gif": | |
case "image/webp": | |
case "image/svg+xml": | |
case "image/x-icon": | |
return "image"; | |
break; | |
} | |
return "other"; | |
} | |
/* | |
* Maps a colour to a content type | |
* Based on WebPageTest colours | |
*/ | |
function typeColors(type) { | |
switch(type) { | |
case 'html': | |
return d3.rgb(130,181,252); | |
case 'css': | |
return d3.rgb(178,234,148); | |
case 'image': | |
return d3.rgb(196,154,232); | |
case 'js': | |
return d3.rgb(254,197,132); | |
case 'font': | |
return d3.rgb(255,82,62); | |
} | |
return d3.rgb(186,186,186); | |
} | |
</script> | |
</body> | |
</html> |