A snippet from ta.virot.me/tweettimeline
Built with blockbuilder.org
forked from tvirot's block: TweetTimeline
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<style>
body {
font-family: 'HelveticaNeue-Light', Arial, Helvetica, sans-serif;
font-size: 14px;
}
.container#tweet {
padding-top: 30px;
}
div.tooltip {
background: rgba(0, 0, 0, 0.6);
color: #FFFFFF;
font-size: 12px;
font-family: 'HelveticaNeue-Light', Arial, Helvetica, sans-serif;
padding: 4px 6px;
pointer-events: none;
position: absolute;
text-align: center;
}
</style>
</head>
<body>
<div class="container" id="tweet"></div>
<div class="container" id="plot"></div>
<script>
function addCommas(nStr){
nStr += '';
x = nStr.split('.');
x1 = x[0];
x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
}
var tooltip = d3.select("div#plot").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
function showtt(text){
tooltip.text(text).style("opacity", 1);
}
function movett(){
tooltip.style("left", d3.event.pageX + 12 + "px")
.style("top", d3.event.pageY + 5 + "px");
}
function hidett(){
tooltip.style("opacity", 0);
}
var data;
var isEvenSpaced = false;
var isInit = false;
d3.json("213655868590407680.json", function(json){
data = json;
plot(data, isEvenSpaced);
});
function plot(data, isEvenSpaced){
var width = 1000;
var height = 200;
var profile = d3.select("div#tweet")
.append("svg")
.attr("class", "profile")
.attr("width", width)
.attr("height", 120);
profile.append("image")
.attr("xlink:href", "https://pbs.twimg.com/profile_images/600303920125214721/uy-506Vm_400x400.jpg")
.attr("x", 50)
.attr("y", "0")
.attr("width", "48px")
.attr("height", "48px")
.style("cursor", "hand")
.on("click", function(){
window.open("https://www.twitter.com/" + data.author.screen_name, "_blank");
});
profile.append("text")
.text(data.author.name)
.attr("x", 105)
.attr("y", 2)
.attr("dy", "1em")
.style("fill", "#000000")
.style("font-weight", "bold")
.style("font-size", "24px")
.style("cursor", "hand")
.on("click", function(d,i){
window.open("https://www.twitter.com/" + data.author.screen_name, "_blank");
});
profile.append("text")
.text("@" + data.author.screen_name)
.attr("x", 105)
.attr("y", 48 - 2)
.attr("dy", "-0.1em")
.style("fill", "#999999")
.style("font-size", "14px")
.style("cursor", "hand")
.on("click", function(d,i){
window.open("https://www.twitter.com/" + data.author.screen_name, "_blank");
});
profile.append("text")
.text(data.tweet.text)
.attr("x", 50)
.attr("y", 48 + 10)
.attr("dy", "1em")
.style("fill", "#000000")
.style("font-size", "14px")
.style("cursor", "hand")
.on("mouseover", function(d,i){
d3.select(this).style("fill", "#F1B054");
})
.on("mouseout", function(d,i){
d3.select(this).style("fill", "#000000");
})
.on("click", function(d,i){
window.open("https://www.twitter.com/" + data.author.screen_name + "/status/" + data.tweet.id, "_blank");
});
profile.append("text")
.text("Retweeted " + (data.nodes.length - 1) +
" times. Displayed to " + addCommas(d3.sum(data.nodes, function(d) { return d.followers_count; })) + " users.")
.attr("x", 50)
.attr("y", 48 + 45)
.attr("dy", "1em")
.style("fill", "#993333")
.style("font-size", "14px");
var timeline = d3.select("div#plot").append("svg")
.attr("class", "timeline")
.attr("width", width)
.attr("height", height);
var created_after_list = data.nodes.map(function(n) { return n.created_after; });
var max_followers_count = d3.max(data.nodes.map(function(n) { return n.followers_count; } ));
var max_created_after = d3.max(created_after_list);
var x;
if(isEvenSpaced) {
x = d3.scale.ordinal()
.domain(created_after_list)
.rangeBands([100, width - 100]);
} else {
x = d3.scale.linear()
.domain([0, max_created_after])
.range([100, width - 100]);
}
var y = d3.scale.linear()
.domain([0, width - 2 * 100])
.range([0, height / 2 - 20]);
var r = d3.scale.log()
.domain([1, max_followers_count])
.range([0, height / 2 - 20]);
var curve_gen = d3.svg.line()
.interpolate("cardinal")
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
var arcs = timeline.selectAll("arc")
.data(data.arcs).enter();
function generateControlPoints(x1, x2){
return [ {x: x1, y: 100},
{x: (x1+x2)/2.0, y: 100 - y(Math.abs(x1-x2))},
{x: x2, y: 100} ];
}
var curves = arcs.append("path")
.style("stroke", "#444444")
.style("stroke-opacity", "0.2")
.style("stroke-width", "1.5px")
.style("fill", "none")
.attr("d", function(d) {
x1 = x(created_after_list[d.from]);
x2 = x(created_after_list[d.to]);
return curve_gen.tension(0)(generateControlPoints(x1, x2));
});
timeline.append("rect")
.attr("x", x(0))
.attr("y", 0)
.attr("width", width)
.attr("height", 100)
.style("fill", "#FFFFFF")
.transition()
.duration(function() { return (isInit) ? 0 : 1100; })
.ease("cubic-in-out")
.attr("x", width)
.remove();
var nodes = timeline.selectAll("node")
.data(data.nodes).enter();
nodes.append("circle")
.attr("class", "outer")
.attr("id", function(d,i) { return "outer-" + i; })
.attr("cx", function(d) { return x(d.created_after); } )
.attr("cy", 100)
.style("fill", "#993333")
.style("fill-opacity", 0.1)
.style("stroke", "none")
.style("stroke-width", "3px")
.transition()
.delay(function(d,i) { return (isInit) ? 0 : d.created_after * 500 / max_created_after; })
.duration(function() { return (isInit) ? 0 : 600; })
.ease("bounce")
.attr("r", function(d) { return Math.max(1, r(d.followers_count)); });
nodes.append("circle")
.attr("class", "inner")
.attr("id", function(d,i) { return "inner-" +i; })
.attr("cx", function(d) { return x(d.created_after); } )
.attr("cy", 100)
.attr("r", 3)
.style("fill", "#444444")
.style("opacity", 0.0)
.transition()
.delay(function(d,i) { return (isInit) ? 0 : d.created_after * 500 / max_created_after; })
.duration(function() { return (isInit) ? 0 : 600; })
.style("opacity", 1.0);
function created_after_toString(created_after){
var day = Math.floor(created_after / (60 * 60 * 24));
var remainder = created_after - day * (60 * 60 * 24);
var hour = Math.floor(remainder / (60 * 60));
remainder -= hour * (60 * 60);
var min = Math.floor(remainder / 60);
remainder -= min * 60;
var second = remainder;
var out = "";
if(day > 0) out += day + " days ";
if(hour > 0) out += hour + " hrs ";
if(min > 0 && day==0) out += min + " mins ";
if(second > 0 && hour==0) out += second + " secs ";
if(created_after > 0) return out;
else return "less than a second";
}
nodes.append("rect")
.attr("fill-opacity", 0.0)
.attr("x", function(d,i) {
return (i == 0) ? 0 :
(x(data.nodes[i-1].created_after) + x(data.nodes[i].created_after)) / 2.0;
})
.attr("width", function(d,i) {
x1 = (i == 0) ? 0 :
(x(data.nodes[i-1].created_after) + x(data.nodes[i].created_after)) / 2.0;
x2 = (i == data.nodes.length - 1) ? width :
(x(data.nodes[i].created_after) + x(data.nodes[i+1].created_after)) / 2.0;
return x2 - x1;
})
.attr("y", 100 - 25)
.attr("height", 100)
.style("cursor", "hand")
.on("mouseover", function(d, i) {
var xpos = x(d.created_after);
var radius = Math.max(1, r(d.followers_count));
timeline.select("circle#inner-" + i).style("fill", "#993333");
timeline.select("circle#outer-" + i)
.style("visibility", "visible")
.style("stroke", "#993333");
if (i > 0) {
timeline.append("line")
.attr("class", "time_label")
.attr("x1", xpos)
.attr("y1", 100)
.attr("x2", xpos)
.attr("y2", 100 + 67)
.style("stroke", "#444444")
.style("stroke-width", 0.5);
timeline.append("text")
.attr("class", "time_label")
.text(created_after_toString(d.created_after))
.attr("x", xpos)
.attr("dx", 5)
.attr("y", 100 + 67)
.attr("dy", "-0.1em")
.attr("text-anchor", "start")
.style("fill", "#444444")
.style("font-size", "12px");
}
var count_label = timeline.append("text").text(addCommas(d.followers_count) + " followers");
var bbox = count_label.node().getBBox();
var rect_w = bbox.width + 6;
var rect_h = bbox.height + 2;
count_label.remove();
timeline.append("rect")
.attr("class", "user_label")
.attr("x", xpos - rect_w / 2)
.attr("y", 85 - radius - rect_h / 2)
.attr("width", rect_w)
.attr("height", rect_h)
.style("fill", "#FFFFFF");
timeline.append("text")
.attr("class", "user_label")
.text(addCommas(d.followers_count) + " followers")
.attr("x", xpos)
.attr("y", 85 - radius)
.attr("dy", "0.4em")
.attr("text-anchor", "middle")
.style("fill", "#77A396");
showtt("@" + d.screen_name);
timeline.selectAll("path")
.style("stroke-opacity", function(e, j) {
if((e.from != i) && (e.to != i))
return 0.05;
else
return 0.5;
});
})
.on("mousemove", function(d, i){
movett();
})
.on("mouseout", function(d, i){
timeline.select("circle#inner-" + i).style("fill", "#444444");
timeline.select("circle#outer-" + i)
.style("stroke", "none");
if(i > 0) timeline.selectAll(".time_label").remove();
timeline.selectAll(".user_label").remove();
hidett();
timeline.selectAll("path").style("stroke-opacity", 0.2);
})
.on("click", function(d, i){
window.open("https://www.twitter.com/" + d.screen_name, "_blank");
});
timeline.append("line")
.attr("x1", x(0))
.attr("y1", 100)
.attr("x2", x(0))
.attr("y2", 100 + 100)
.style("stroke", "#444444")
.style("stroke-width", 0.5);
var date_iso = d3.time.format("%Y-%m-%dT%H:%M:%S");
var date_pretty = d3.time.format("%I:%M %p - %d %b %y");
timeline.append("text")
.text(date_pretty(new Date(date_iso.parse(data.tweet.created_at) - 60000 * (new Date().getTimezoneOffset()))))
.attr("x", x(0))
.attr("dx", 5)
.attr("y", 100 + 100)
.attr("dy", "-0.1em")
.attr("text-anchor", "start")
.style("fill", "black")
.style("font-size", "20px");
timeline.append("text")
.text("rescale")
.attr("x", width-100)
.attr("dx", 0)
.attr("y", 100 + 100)
.attr("dy", "-0.1em")
.attr("text-anchor", "end")
.style("cursor", "hand")
.style("fill", "#666666")
.style("font-size", "14px")
.on("mouseover", function(d,i) {
d3.select(this).style("fill", "#77A396");
})
.on("mouseout", function(d,i) {
d3.select(this).style("fill", "#666666");
})
.on("click", function(d,i) {
isEvenSpaced = !isEvenSpaced;
d3.selectAll("svg").remove();
plot(data, isEvenSpaced);
});
isInit = true;
}
</script>
</body>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js