An iOS-friendly histogram of my motion activity on my phone. Meant to be used inside a UIWebView.
Open on a phone and scrub back and forth with your finger.
xxxxxxxxxx
<meta name="viewport" content="initial-scale=1.0,user-scalable=no" />
<body>
<style>
body {
font: small Avenir;
padding: 0;
margin: 0;
}
.axis line, .axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.tick {
transition: fill-opacity .2s ease;
}
rect {
shape-rendering: crispEdges;
fill: hsla(0, 0%, 0%, .5);
}
.scrub line {
stroke: red;
}
.scrub text {
fill: red;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.5.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.js"></script>
<script>
var days = 1.25
, bins = days*24*10
, margin = { top: 0, right: 10, bottom: 20, left: 10 }
, width = bins
, height = 80 - margin.top - margin.bottom;
var svg = d3.select('body').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
var chart = svg.append('g')
.attr('transform', 'translate(' + margin.left + ' ' + margin.top + ')');
d3.json('motion.json', function(err, data) {
console.log(Object.keys(data[0]));
for (var i = 0; i < data.length; i++)
data[i].startDate = moment(data[i].startDate);
var max = d3.max(data, function(d) { return d.startDate });
var x = d3.time.scale()
//.domain([new Date - 1000*60*60*24*days, new Date])
.domain([max - 1000*60*60*24*days, max])
.range([0, width]);
var histogram = d3.layout.histogram()
.value(function(d) { return d.startDate })
.range(x.domain())
.bins(bins);
var walking = histogram(data.filter(function(d) { return true }));
var y = d3.scale.sqrt()
.domain([0, d3.max(walking, function(d) { return d.y })])
.range([height, 0]).nice();
var xAxis = d3.svg.axis().scale(x)
.ticks(4);
chart.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0 ' + height + ')')
.call(xAxis);
var bars = chart.selectAll('rect')
.data(walking)
.enter().append('rect')
.attr('x', function(d) { return x(d.x) })
.attr('width', x(Number(x.domain()[0]) + walking[0].dx))
.attr('y', function(d) { return y(d.y) })
.attr('height', function(d) { return height - y(d.y) });
var scrub = chart.append('g').attr('class', 'scrub');
scrub.append('line')
.attr('y2', height + xAxis.tickSize());
scrub.append('text')
.attr('y', height + 18)
.attr('text-anchor', 'middle');
d3.select('body').on('mousemove', move(d3.mouse));
d3.select('body').on('touchmove', move(function(c) { return d3.touches(c).pop() }));
function move(moveType) {
var container = chart[0][0]
, format = d3.time.format('%I:%M %p')
, ticks = chart.selectAll('g.x.axis .tick');
return function() {
d3.event.preventDefault();
var coords = moveType(container);
if (!coords) return;
scrub
.attr('transform', 'translate(' + coords[0] + ' 0)')
.select('text').text(format(x.invert(coords[0])));
ticks.style('fill-opacity', function(d) {
var dx = d3.select(this).select('text').node().getComputedTextLength();
return Math.abs(x(d) - coords[0]) < dx + 10 ? 0 : 1;
});
};
}
});
</script>
</body>
Modified http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.5.1/moment.min.js to a secure url
Modified http://cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.js to a secure url
https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.5.1/moment.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.js