index.html
<div class=header>
</div>
<div class=fixie>
</div>
config.json
{"description":"1024 members scrolly","endpoint":"","display":"div","public":true,"require":[{"name":"crossfilter","url":"http://square.github.io/crossfilter/crossfilter.v1.min.js"}],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"meetups.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"rsvps.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"members.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"style.css":{"default":true,"vim":false,"emacs":false,"fontSize":12},"index.html":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"fullscreen":false,"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"period","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01,"thumbnail":"http://i.imgur.com/rns10Nv.png"}
inlet.js
var baseAvatarUrl = "http://photos1.meetupstatic.com/photos/member"
var avatarWidth = 40;
var avatarHeight = 40;
var rectWidth = 20;
var rectHeight = 20;
var rectSpace = 10;
var membersDict = {};
var members = tributary.members;
members.sort(function(a,b) {
return a.joined - b.joined
})
members.forEach(function(member) {
membersDict[member.id] = member;
});
var firstMember = tributary.members[0];
var lastMember = tributary.members[tributary.members.length - 1];
var meetupsDict = {};
var meetups = tributary.meetups;
meetups.sort(function(a,b) {
return a.time - b.time
})
meetups.forEach(function(meetup) {
meetupsDict[meetup.id] = meetup;
});
var memberXf = crossfilter(members);
var memberDims = {
"joined": memberXf.dimension(function(d) { return d.joined })
}
var dayGroup = memberDims.joined.group(function(d) { return d3.time.day.floor(new Date(d)) });
var membersByDay = dayGroup.all();
var dayBuckets = {}
members.forEach(function(member) {
var day = d3.time.day.floor(new Date(member.joined))
if(!dayBuckets[day]) {
dayBuckets[day] = [member]
} else {
dayBuckets[day].push(member)
}
})
var xf = crossfilter(tributary.rsvps);
var dims = {
"member": xf.dimension(function(d) { return d.id }),
"events": xf.dimension(function(d) { return d.evt }),
"rsvp": xf.dimension(function(d) { return d.response })
};
dims.rsvp.filter('yes');
var display = d3.select("#display")
var width = tributary.sw;
var height = tributary.sh;
var timelineHeight = 100;
var visHeight = membersByDay.length * (rectHeight + rectSpace);
var timeScale = d3.time.scale()
.domain([firstMember.joined, lastMember.joined])
.range([0, visHeight])
var mbdExtent = d3.extent(membersByDay, function(d) { return d.value })
var mbdScale = d3.scale.linear()
.domain(mbdExtent)
.range([rectWidth,200])
var format = d3.time.format("%b %d, %Y")
var meetupdiv = display.append("div").classed("meetups", true)
var descs = meetupdiv.selectAll("div.meetup")
.data(meetups)
.enter()
.append("div")
.classed("meetup", true)
.style({
top: function(d,i) { return timeScale(d3.time.day.ceil(new Date(d.time))) + "px" },
left: "0px",
width: "800px",
height: "20px"
})
.append("div")
.classed("meetup-desc", true)
descs.append("span").text(function(d) { return d.name }).classed("name", true)
descs.append("br")
descs.append("span").text(function(d) { return format(new Date(d.time)) })
descs.append("br")
descs.append("span").text(function(d) { return "@ " +d.venue })
descs.append("br")
descs.append("span")
.classed("rsvplink", true)
.text(function(d) {
var rsvpers = dims.events.filter(d.id);
var nRsvpers = rsvpers.top(Infinity).length;
return nRsvpers
})
.on("click", function(d,i) {
})
var bardiv = display.append("div")
.classed("facebar", true)
.style({
height: 20 + visHeight + "px"
})
bardiv.selectAll(".face")
.data(members, function(d) { return d.id })
.enter()
.append("div")
.classed("face", true)
.style({
top: function(d,i) { return timeScale(d3.time.day.floor(new Date(d.joined))) + "px" },
left: function(d,i) {
var day = d3.time.day.floor(new Date(d.joined));
var x = dayBuckets[day].indexOf(d) * rectWidth
return x + "px"
},
width: rectWidth + "px",
height: rectHeight + "px"
})
.style("display", "none")
.append("img")
.attr({
src: function(d) { return baseAvatarUrl + d.avatar },
width: rectWidth,
height: rectHeight,
title: function(d) { return d.name }
})
display.on("scroll", scroller);
function scroller() {
var scroll = this.scrollTop || 0;
var y0 = timeScale.invert(scroll - 100);
var y1 = timeScale.invert(scroll + height);
var filtered = memberDims.joined.filter([y0, y1]).top(Infinity)
var faces = bardiv.selectAll(".face")
.data(filtered, function(d) { return d.id })
faces
.style("display", "")
faces.exit()
.style("display", "none")
}
meetups.json
[{"id":"52020632","name":"Discuss Transitions","venue":"Epicenter Cafe","time":1329442200000},{"id":"53698992","name":"Open Data Hacking at the Library","venue":"Sf public Library Main Branch","time":1330643700000},{"id":"55266432","name":"Getting your Data Drivers License","venue":"swissnex San Francisco ","time":1331863200000},{"id":"57477392","name":"Getting Started with Nutrition and BART","venue":"Nokia Research Center Palo Alto","time":1333503000000},{"id":"64825422","name":"Visualization Open Mic","venue":"BitTorrent","time":1337824800000},{"id":"84262482","name":"Open Data Hacking at the Library","venue":"Sf public Library Main Branch","time":1349472600000},{"id":"102356032","name":"d3.selectAll('meetup').data([2013]).enter()","venue":"Trulia","time":1361500200000},{"id":"51422112","name":".enter() (the first d3.js user group meeting)","venue":"Epicenter Cafe","time":1328835600000},{"id":"52773222","name":"Real-time Dashboards","venue":"Boundary HQ","time":1330050600000},{"id":"54700892","name":"Co-Working Session","venue":"Noisebridge","time":1331173800000},{"id":"55699192","name":"Put some d3 source files up on the screen and discuss","venue":"San Francisco Public Library; Potrero Branch","time":1332281700000},{"id":"58592772","name":"Exploratory Graph Visualization","venue":"Twitter","time":1334280600000},{"id":"57231572","name":"Underscore.js and Canvas","venue":"Tolman Hall, University of California, Berkeley, Berkeley, CA 94720","time":1335313800000},{"id":"63143652","name":"D3 BOF at Fluent Conference 2012","venue":"Hilton Hotel","time":1338346800000},{"id":"67903012","name":"Visual Perception and Illusions","venue":"Groupon","time":1339723800000},{"id":"74765322","name":"d3 goes mobile","venue":"Twitter","time":1344562200000},{"id":"75754562","name":"Rackspace Visualization Hack Day","venue":"Rackspace San Francisco","time":1346428800000},{"id":"84485982","name":"Data geography with d3.geo","venue":"RocketSpace Inc","time":1350610200000},{"id":"102072932","name":"Urban Data Challenge Kick-off","venue":"swissnex San Francisco ","time":1360204200000},{"id":"103428452","name":"Urban Data Hackathon","venue":"Gray Area Foundation for the Arts (GAFFTA)","time":1361642400000},{"id":"111696362","name":"Urban Data Challenge Awards","venue":"swissnex San Francisco ","time":1365271200000}]
style.css
#display {
overflow:scroll;
}
.facebar {
position:absolute;
width: 100px;
left: 10px;
display:block;
}
.bar {
position:absolute;
border: 1px solid;
}
.face {
position:absolute;
}
div.node {
position:absolute;
}
.meetup {
position:absolute;
background: #ffffff;
width: 100px;
display:block;
}
.meetup-desc {
position:relative;
background: rgba(244, 244, 244, 0.51);
padding-right: 20px;
text-align:right;
font-size: 12px;
font-family: "Helvetica Neue", Helvetica;
line-height: 22px;
}
.meetup-desc .name {
font-weight: 600;
font-size: 14px;
}
.active-meetup {
stroke: #ff0000;
}