<!DOCTYPE html>
<html>
<head>
<script data-require="[email protected]" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
#milestones_attempts_combo .context {
/*fill: #efefef; */
}
#milestones_attempts_combo .focus .right,
#milestones_attempts_combo .context .right {
fill: #0064cd;
}
#milestones_attempts_combo .focus .wrong,
#milestones_attempts_combo .context .wrong {
fill: #cd001d;
}
#milestones_attempts_combo .y-axis-label,
#milestones_attempts_combo .y2-axis-label {
fill: #aaa;
font-size: 120%;
}
#milestones_attempts_combo .focus-line,
#milestones_attempts_combo .context-line {
stroke: orange;
stroke-width: 2;
fill-opacity: 0;
}
.brush .extent {
stroke: #efefef;
fill: #666;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.stats-container {
width: 700px;
margin: 0 auto;
}
.chart .axis line,
.chart .axis path,
.chart .tick line {
fill: none;
stroke: #aaa;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="milestones_attempts_combo"></div>
<script>
(function() {
var myjsondata = {
"answer_counts": [{
"date": "2012-09-13",
"ys": [{
"y1": 9,
"y0": 0,
"class": "right"
}, {
"y1": 12,
"y0": 9,
"class": "wrong"
}],
"total": 12
}, {
"date": "2012-09-16",
"ys": [{
"y1": 16,
"y0": 0,
"class": "right"
}, {
"y1": 17,
"y0": 16,
"class": "wrong"
}],
"total": 17
}, {
"date": "2012-09-17",
"ys": [{
"y1": 12,
"y0": 0,
"class": "right"
}, {
"y1": 14,
"y0": 12,
"class": "wrong"
}],
"total": 14
}, {
"date": "2012-09-19",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-09-20",
"ys": [{
"y1": 12,
"y0": 0,
"class": "right"
}, {
"y1": 16,
"y0": 12,
"class": "wrong"
}],
"total": 16
}, {
"date": "2012-09-21",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 22,
"y0": 20,
"class": "wrong"
}],
"total": 22
}, {
"date": "2012-09-22",
"ys": [{
"y1": 1,
"y0": 0,
"class": "right"
}, {
"y1": 1,
"y0": 1,
"class": "wrong"
}],
"total": 1
}, {
"date": "2012-09-23",
"ys": [{
"y1": 10,
"y0": 0,
"class": "right"
}, {
"y1": 12,
"y0": 10,
"class": "wrong"
}],
"total": 12
}, {
"date": "2012-09-24",
"ys": [{
"y1": 9,
"y0": 0,
"class": "right"
}, {
"y1": 9,
"y0": 9,
"class": "wrong"
}],
"total": 9
}, {
"date": "2012-09-25",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 4,
"y0": 2,
"class": "wrong"
}],
"total": 4
}, {
"date": "2012-09-29",
"ys": [{
"y1": 26,
"y0": 0,
"class": "right"
}, {
"y1": 37,
"y0": 26,
"class": "wrong"
}],
"total": 37
}, {
"date": "2012-10-01",
"ys": [{
"y1": 44,
"y0": 0,
"class": "right"
}, {
"y1": 44,
"y0": 44,
"class": "wrong"
}],
"total": 44
}, {
"date": "2012-10-02",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-10-03",
"ys": [{
"y1": 13,
"y0": 0,
"class": "right"
}, {
"y1": 13,
"y0": 13,
"class": "wrong"
}],
"total": 13
}, {
"date": "2012-10-05",
"ys": [{
"y1": 47,
"y0": 0,
"class": "right"
}, {
"y1": 47,
"y0": 47,
"class": "wrong"
}],
"total": 47
}, {
"date": "2012-10-08",
"ys": [{
"y1": 17,
"y0": 0,
"class": "right"
}, {
"y1": 17,
"y0": 17,
"class": "wrong"
}],
"total": 17
}, {
"date": "2012-10-09",
"ys": [{
"y1": 19,
"y0": 0,
"class": "right"
}, {
"y1": 20,
"y0": 19,
"class": "wrong"
}],
"total": 20
}, {
"date": "2012-10-10",
"ys": [{
"y1": 31,
"y0": 0,
"class": "right"
}, {
"y1": 31,
"y0": 31,
"class": "wrong"
}],
"total": 31
}, {
"date": "2012-10-11",
"ys": [{
"y1": 6,
"y0": 0,
"class": "right"
}, {
"y1": 6,
"y0": 6,
"class": "wrong"
}],
"total": 6
}, {
"date": "2012-10-14",
"ys": [{
"y1": 6,
"y0": 0,
"class": "right"
}, {
"y1": 6,
"y0": 6,
"class": "wrong"
}],
"total": 6
}, {
"date": "2012-10-19",
"ys": [{
"y1": 30,
"y0": 0,
"class": "right"
}, {
"y1": 32,
"y0": 30,
"class": "wrong"
}],
"total": 32
}, {
"date": "2012-10-20",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 22,
"y0": 20,
"class": "wrong"
}],
"total": 22
}, {
"date": "2012-10-23",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 20,
"y0": 20,
"class": "wrong"
}],
"total": 20
}, {
"date": "2012-10-24",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 21,
"y0": 20,
"class": "wrong"
}],
"total": 21
}, {
"date": "2012-10-29",
"ys": [{
"y1": 22,
"y0": 0,
"class": "right"
}, {
"y1": 22,
"y0": 22,
"class": "wrong"
}],
"total": 22
}, {
"date": "2012-11-01",
"ys": [{
"y1": 1,
"y0": 0,
"class": "right"
}, {
"y1": 1,
"y0": 1,
"class": "wrong"
}],
"total": 1
}, {
"date": "2012-11-02",
"ys": [{
"y1": 26,
"y0": 0,
"class": "right"
}, {
"y1": 29,
"y0": 26,
"class": "wrong"
}],
"total": 29
}, {
"date": "2012-11-05",
"ys": [{
"y1": 23,
"y0": 0,
"class": "right"
}, {
"y1": 27,
"y0": 23,
"class": "wrong"
}],
"total": 27
}, {
"date": "2012-11-06",
"ys": [{
"y1": 10,
"y0": 0,
"class": "right"
}, {
"y1": 11,
"y0": 10,
"class": "wrong"
}],
"total": 11
}, {
"date": "2012-11-07",
"ys": [{
"y1": 15,
"y0": 0,
"class": "right"
}, {
"y1": 18,
"y0": 15,
"class": "wrong"
}],
"total": 18
}, {
"date": "2012-11-09",
"ys": [{
"y1": 26,
"y0": 0,
"class": "right"
}, {
"y1": 31,
"y0": 26,
"class": "wrong"
}],
"total": 31
}, {
"date": "2012-11-10",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-11-14",
"ys": [{
"y1": 15,
"y0": 0,
"class": "right"
}, {
"y1": 17,
"y0": 15,
"class": "wrong"
}],
"total": 17
}, {
"date": "2012-11-16",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 23,
"y0": 20,
"class": "wrong"
}],
"total": 23
}, {
"date": "2012-11-18",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-11-19",
"ys": [{
"y1": 23,
"y0": 0,
"class": "right"
}, {
"y1": 26,
"y0": 23,
"class": "wrong"
}],
"total": 26
}, {
"date": "2012-11-21",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 3,
"y0": 2,
"class": "wrong"
}],
"total": 3
}, {
"date": "2012-11-23",
"ys": [{
"y1": 9,
"y0": 0,
"class": "right"
}, {
"y1": 10,
"y0": 9,
"class": "wrong"
}],
"total": 10
}, {
"date": "2012-11-26",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-11-27",
"ys": [{
"y1": 25,
"y0": 0,
"class": "right"
}, {
"y1": 26,
"y0": 25,
"class": "wrong"
}],
"total": 26
}, {
"date": "2012-11-28",
"ys": [{
"y1": 73,
"y0": 0,
"class": "right"
}, {
"y1": 75,
"y0": 73,
"class": "wrong"
}],
"total": 75
}, {
"date": "2012-12-02",
"ys": [{
"y1": 19,
"y0": 0,
"class": "right"
}, {
"y1": 20,
"y0": 19,
"class": "wrong"
}],
"total": 20
}, {
"date": "2012-12-05",
"ys": [{
"y1": 1,
"y0": 0,
"class": "right"
}, {
"y1": 1,
"y0": 1,
"class": "wrong"
}],
"total": 1
}, {
"date": "2013-01-11",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 3,
"y0": 2,
"class": "wrong"
}],
"total": 3
}, {
"date": "2013-01-16",
"ys": [{
"y1": 8,
"y0": 0,
"class": "right"
}, {
"y1": 8,
"y0": 8,
"class": "wrong"
}],
"total": 8
}],
"badge_set_reached": [{
"date": "2014-05-24",
"set": 3
}, {
"date": "2014-09-29",
"set": 5
}, {
"date": "2014-11-10",
"set": 6
}, {
"date": "2014-08-29",
"set": 7
}, {
"date": "2015-08-12",
"set": 9
}, {
"date": "2016-01-09",
"set": 9
}]
}
var data = myjsondata;
// preprocess data to get usable date objects
var parse_date = d3.time.format('%Y-%m-%d').parse
for (var i in data) {
var myObj = data[i]
for (var j in myObj) {
myObj[j].date = parse_date(myObj[j].date);
}
}
console.log(data);
// set variables
var margin = {
left: 60,
right: 60,
top: 10,
bottom: 140
},
navMargin = {
top: 300,
right: 60,
bottom: 40,
left: 60
},
height = 400 - margin.top - margin.bottom,
width = 800 - margin.left - margin.right,
navWidth = width, // for context band
navHeight = 400 - navMargin.top - navMargin.bottom,
max_total_counts = d3.max(data.answer_counts, function(d) {
return d.total;
}),
max_extent_dates = d3.extent(data.answer_counts, function(d) {
return d.date;
});
max_extent_in_days = function(timescale) {
return d3.time.days(timescale.domain()[0], d3.time.day.offset(timescale.domain()[1], 1))
};
// scales
var y = d3.scale.linear().domain([0, max_total_counts]).rangeRound([height, 0]),
navY = d3.scale.linear().domain([0, max_total_counts]).rangeRound([navHeight, 0]),
time = d3.time.scale().domain(max_extent_dates).range([0, width]),
navTime = d3.time.scale().domain(max_extent_dates).range([0, width]),
x = d3.scale.ordinal().domain(max_extent_in_days(time)).rangeBands([0, width], 0.1, 0), // used to calculate bar widths
navX = d3.scale.ordinal().domain(max_extent_in_days(navTime)).rangeBands([0, width], 0.1, 0),
y2 = d3.scale.linear().domain([0, d3.max(data.badge_set_reached, function(d) {
return d.set
})]).rangeRound([height, 0]);
navY2 = d3.scale.linear().domain([0, d3.max(data.badge_set_reached, function(d) {
return d.set
})]).rangeRound([navHeight, 0]);
// axes
var x_axis = d3.svg.axis().scale(time).orient('bottom') // .tickFormat(d3.time.format('%Y-%m-%d'))
.outerTickSize(0), // at start and end of axis line
nav_x_axis = d3.svg.axis().scale(navTime).orient('bottom') // .tickFormat(d3.time.format('%Y-%m-%d'))
.outerTickSize(0), // at start and end of axis line
y_axis = d3.svg.axis().scale(y).orient('left').tickFormat(d3.format('d'));
y2_axis = d3.svg.axis().scale(y2).orient('right').tickFormat(d3.format('d'));
// add brush
var brush = d3.svg.brush()
.x(navTime)
.on("brush", brushed);
// svg context
var svg = d3.select("#milestones_attempts_combo")
.append('svg')
.attr('class', 'chart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append('g')
.attr('class', 'focus')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var context = svg.append('g')
.attr('class', 'context')
.attr('transform', 'translate(' + navMargin.left + ',' + navMargin.top + ')');
// Plot the stacked bars
var focus_bar = focus
.append("g")
.attr("class","barClipper")
.style('clip-path', 'url(#clip)')
.selectAll('.g')
.data(data.answer_counts)
.enter().append('g')
.attr('class', 'g bar stack')
.attr('transform', function(d) {
return "translate(" + x(d.date) + ",0)"
});
var focus_rects = focus_bar.selectAll('rect')
.data(function(d) {
return d.ys;
})
.enter().append('rect')
.attr('width', x.rangeBand())
.attr('height', function(d) {
return y(d.y0) - y(d.y1);
})
.attr('y', function(d) {
return y(d.y1);
})
.attr('class', function(d) {
return 'rect ' + d['class'];
});
var context_bar = context.selectAll('.g')
.data(data.answer_counts)
.enter().append('g')
.attr('class', 'g')
.attr('transform', function(d) {
return "translate(" + navTime(d.date) + ",0)"
});
var context_rects = context_bar.selectAll('rect')
.data(function(d) {
return d.ys;
})
.enter().append('rect')
.attr('width', navX.rangeBand())
.attr('height', function(d) {
return navY(d.y0) - navY(d.y1);
})
.attr('y', function(d) {
return navY(d.y1);
})
.attr('class', function(d) {
return 'rect ' + d['class'];
});
// Plot lines
var line = d3.svg.line()
.x(function(d) {
return time(d.date)
})
.y(function(d) {
return y2(d.set)
})
.interpolate('step-after');
var focus_line = focus.append('path')
.datum(data.badge_set_reached)
.attr('class', 'line focus-line')
.attr('d', line);
var line2 = d3.svg.line()
.x(function(d) {
return time(d.date)
})
.y(function(d) {
return navY2(d.set)
})
.interpolate('step-after');
var context_line = context.append('path')
.datum(data.badge_set_reached)
.attr('class', 'line context-line')
.attr('d', line2);
// Plot axes
focus.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(x_axis)
.selectAll('text')
.style('text-anchor', 'end')
// .attr('transform', 'rotate(-45)')
// .attr('dx', '-.5em')
// .attr('dy', '.5em');
focus.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(0, 0)')
.call(y_axis);
focus.append('g')
.attr('class', 'y2 axis')
.attr('transform', 'translate(' + width + ', 0)')
.call(y2_axis);
context.append('g')
.attr('class', 'navX axis')
.attr('transform', 'translate(0, ' + navHeight + ')')
.call(nav_x_axis);
// Label axes
svg.append('text')
.attr('class', 'label y-axis-label')
.attr('transform', 'rotate(-90)')
.attr('x', 0 - ((height + margin.top)/2))
.attr('y', 0)
.attr('dy', 20)
.style('text-anchor', 'middle')
.text('Paths Attempted');
svg.append('text')
.attr('class', 'label y2-axis-label')
.attr('transform', 'rotate(+90)')
.attr('y', 0 - (width + margin.left + margin.right))
.attr('x', 0 + ((height + margin.top)/2))
.attr('dy', '2em')
.style('text-anchor', 'middle')
.text('Badge Set Reached');
// Add brush to svg
context.append('g')
.attr('class', 'x brush')
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", navHeight + 7);
function brushed() {
time.domain(brush.empty() ? navTime.domain() : brush.extent())
.range([0, (width)]);
x.domain(max_extent_in_days(time))
.rangeBands([margin.left, (width)], 0.1, 0);
focus.selectAll('.bar.stack')
.attr('transform', function(d) {
return "translate(" + time(d.date) + ",0)";
})
.attr('width', x.rangeBand());
focus.selectAll('.rect')
.attr('width', x.rangeBand());
focus.selectAll('.line').attr('d', line);
focus.select(".x.axis").call(x_axis);
};
})();
</script>
</body>
</html>
非常感謝。這是一個巨大的幫助。 – monotasker