From e333b6f389f77075e0133c030bcf374253805078 Mon Sep 17 00:00:00 2001 From: micahstubbs Date: Sat, 8 Nov 2014 20:11:39 -0600 Subject: [PATCH 1/2] fixed spelling of 'Reusable' in folder names at code/Chapter04 --- code/Chapter04/ReusableBarChart/index.html | 67 ++++++++++ code/Chapter04/ReusableBarChart/script.js | 96 ++++++++++++++ code/Chapter04/ReusableBarChart/snippet.html | 1 + code/Chapter04/ReusableWithAxes/index.html | 59 +++++++++ code/Chapter04/ReusableWithAxes/script.js | 131 +++++++++++++++++++ code/Chapter04/ReusableWithAxes/snippet.html | 1 + 6 files changed, 355 insertions(+) create mode 100644 code/Chapter04/ReusableBarChart/index.html create mode 100644 code/Chapter04/ReusableBarChart/script.js create mode 100644 code/Chapter04/ReusableBarChart/snippet.html create mode 100644 code/Chapter04/ReusableWithAxes/index.html create mode 100644 code/Chapter04/ReusableWithAxes/script.js create mode 100644 code/Chapter04/ReusableWithAxes/snippet.html diff --git a/code/Chapter04/ReusableBarChart/index.html b/code/Chapter04/ReusableBarChart/index.html new file mode 100644 index 0000000..fd081d5 --- /dev/null +++ b/code/Chapter04/ReusableBarChart/index.html @@ -0,0 +1,67 @@ + + + + + + + + + + + + + +
+
+ +
+
+
+ Usage: hover with the mouse over the bars to see their value information. +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Code

+
+
+
+
+
+
+
+
+ + + + diff --git a/code/Chapter04/ReusableBarChart/script.js b/code/Chapter04/ReusableBarChart/script.js new file mode 100644 index 0000000..7f62185 --- /dev/null +++ b/code/Chapter04/ReusableBarChart/script.js @@ -0,0 +1,96 @@ +// Simplified two bar charts example: The Reusable Version +/////////////////////////////////////////////////////////////////////// +// Chart module +d3.edge = {}; + +d3.edge.barChart = function module() { + var w = 400, + h = 300; + var dispatch = d3.dispatch("customHover"); + function exports(_selection) { + _selection.each(function(_data) { + var barW = w / _data.length, + scaling = h / d3.max(_data); + d3.select(this) + .append("svg") + .attr({class: "chart2", width: w, height: h}) + .append("g") + .selectAll(".bar") + .data(_data) + .enter().append("rect") + .attr({ + class: "bar", + x: function(d, i) { return i * barW; }, + width: barW, + y: function(d, i) { return h - d * scaling; }, + height: function(d, i) { return d * scaling; } + }) + .on("mouseover", dispatch.customHover); + }); + } + exports.w = function(_x) { + if (!arguments.length) return w; + w = _x; + return this; + }; + exports.h = function(_x) { + if (!arguments.length) return h; + h = _x; + return this; + }; + d3.rebind(exports, dispatch, "on"); + return exports; +}; + +// Usage + +// var msg = d3.select("#message").text; +// ^^^^^ won't work ... +// ... as D3 will crash internally with: +// `this` does not have a method `each()` +// which indeed it does not: using `msg` like that would +// feed D3 the WRONG context (`this`). This is JavaScript behaviour. +// +// D3 expects a selection context, so let's make sure it gets that. +// We do that by using another [self-invoking] closure construct, +// so we do not pollute the global variable space. +var msg = (function () { + var selection = d3.select("#message"); + return function (text_message) { + selection.text(text_message); + }; +})(); + +var barChart1 = d3.edge.barChart() + .w(100).h(200) + .on("customHover", function(d, i) { msg("chart1: " + d); }); +var barChart2 = d3.edge.barChart() + .w(300).h(100) + .on("customHover", function(d, i) { msg("chart2: " + d); }); + +var data1 = [10, 20, 30, 40]; +var data2 = [100, 40, 10, 80]; + +d3.select("#container1") + .datum(data1) + .call(barChart1); + +d3.select("#container2") + .datum(data2) + .call(barChart2); + + + +/////////////////////////////////////////////////////////////////////////////////// +// Advantages vs. "one-off" approach as shown in "classical" development such as: +// code/chapter-01.stadard-D3/02.bar-chart-one-off.html: +// +// - Not less code overall, but DRY (no repetition, a single code base to maintain) +// - Less code exposed to the user, the bar chart module can be in an external file +// - Exposes only the attributes that have to be changed (w, h, customHover) +// - Composable, a bar chart is added to a div container hence an axis could be simply added to a bar chart +// - Extensible, add get/setters, events at will +// - Public and private functions, you can keep some attributes immutable +// - Consistent API, the same approach as used in the core d3. The logic can be changed +// (i.e., adding transitions) but the API will largely stay the same. +/////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/code/Chapter04/ReusableBarChart/snippet.html b/code/Chapter04/ReusableBarChart/snippet.html new file mode 100644 index 0000000..c97c069 --- /dev/null +++ b/code/Chapter04/ReusableBarChart/snippet.html @@ -0,0 +1 @@ +
001:// Simplified two bar charts example: The Reusable Version
002:///////////////////////////////////////////////////////////////////////
003:// Chart module
004:d3.edge = {};
005:
006:d3.edge.barChart = function module() {
007:    var w = 400,
008:        h = 300;
009:    var dispatch = d3.dispatch("customHover");
010:    function exports(_selection) {
011:        _selection.each(function(_data) {
012:            var barW = w / _data.length,
013:                scaling = h / d3.max(_data);
014:            d3.select(this)
015:                .append("svg")
016:                .attr({class: "chart2", width: w, height: h})
017:                .append("g")
018:                .selectAll(".bar")
019:                .data(_data)
020:                .enter().append("rect")
021:                .attr({
022:                    class: "bar",
023:                    x: function(d, i) { return i * barW; },
024:                    width: barW,
025:                    y: function(d, i) { return h - d * scaling; },
026:                    height: function(d, i) { return d * scaling; }
027:                })
028:                .on("mouseover", dispatch.customHover);
029:        });
030:    }
031:    exports.w = function(_x) {
032:        if (!arguments.length) return w;
033:        w = _x;
034:        return this;
035:    };
036:    exports.h = function(_x) {
037:        if (!arguments.length) return h;
038:        h = _x;
039:        return this;
040:    };
041:    d3.rebind(exports, dispatch, "on");
042:    return exports;
043:};
044:
045:// Usage
046:
047:// var msg = d3.select("#message").text;
048://                                ^^^^^ won't work ...
049:// ... as D3 will crash internally with:
050://    `this` does not have a method `each()`
051:// which indeed it does not: using `msg` like that would
052:// feed D3 the WRONG context (`this`). This is JavaScript behaviour.
053://
054:// D3 expects a selection context, so let's make sure it gets that.
055:// We do that by using another [self-invoking] closure construct,
056:// so we do not pollute the global variable space.
057:var msg = (function () {
058:    var selection = d3.select("#message");
059:    return function (text_message) {
060:        selection.text(text_message);
061:    };
062:})();
063:
064:var barChart1 = d3.edge.barChart()
065:    .w(100).h(200)
066:    .on("customHover"function(d, i) { msg("chart1: " + d); });
067:var barChart2 = d3.edge.barChart()
068:    .w(300).h(100)
069:    .on("customHover"function(d, i) { msg("chart2: " + d); });
070:
071:var data1 = [10203040];
072:var data2 = [100401080];
073:
074:d3.select("#container1")
075:    .datum(data1)
076:    .call(barChart1);
077:
078:d3.select("#container2")
079:    .datum(data2)
080:    .call(barChart2);
081:
082:
083:
084:///////////////////////////////////////////////////////////////////////////////////
085:// Advantages vs. "one-off" approach as shown in "classical" development such as:
086://     code/chapter-01.stadard-D3/02.bar-chart-one-off.html:
087://
088:// - Not less code overall, but DRY (no repetition, a single code base to maintain)
089:// - Less code exposed to the user, the bar chart module can be in an external file
090:// - Exposes only the attributes that have to be changed (w, h, customHover)
091:// - Composable, a bar chart is added to a div container hence an axis could be simply added to a bar chart
092:// - Extensible, add get/setters, events at will
093:// - Public and private functions, you can keep some attributes immutable
094:// - Consistent API, the same approach as used in the core d3. The logic can be changed
095://   (i.e., adding transitions) but the API will largely stay the same.
096:///////////////////////////////////////////////////////////////////////////////////
diff --git a/code/Chapter04/ReusableWithAxes/index.html b/code/Chapter04/ReusableWithAxes/index.html new file mode 100644 index 0000000..d29554b --- /dev/null +++ b/code/Chapter04/ReusableWithAxes/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + +
+
+ +
+
+
+ Chart shows new data every second using a transition (animation). No user interaction available. +
+
+
+
+
+
+
+
+
+

Code

+
+
+
+
+
+
+
+
+ + + diff --git a/code/Chapter04/ReusableWithAxes/script.js b/code/Chapter04/ReusableWithAxes/script.js new file mode 100644 index 0000000..7f523de --- /dev/null +++ b/code/Chapter04/ReusableWithAxes/script.js @@ -0,0 +1,131 @@ +// Bar chart Module +///////////////////////////////// + +d3.custom = {}; + +d3.custom.barChart = function module() { + var margin = {top: 20, right: 20, bottom: 40, left: 40}, + width = 500, + height = 500, + gap = 0, + ease = "bounce"; + var svg; + + var dispatch = d3.dispatch("customHover"); + function exports(_selection) { + _selection.each(function(_data) { + var chartW = width - margin.left - margin.right, + chartH = height - margin.top - margin.bottom; + + var x1 = d3.scale.ordinal() + .domain(_data.map(function(d, i) { return i; })) + .rangeRoundBands([0, chartW], 0.1); + + var y1 = d3.scale.linear() + .domain([0, d3.max(_data, function(d, i) { return d; })]) + .range([chartH, 0]); + + var xAxis = d3.svg.axis() + .scale(x1) + .orient("bottom"); + + var yAxis = d3.svg.axis() + .scale(y1) + .orient("left"); + + var barW = chartW / _data.length; + + if (!svg) { + svg = d3.select(this) + .append("svg") + .classed("chart", true); + var container = svg.append("g").classed("container-group", true); + container.append("g").classed("chart-group", true); + container.append("g").classed("x-axis-group axis", true); + container.append("g").classed("y-axis-group axis", true); + } + + svg.transition().attr({width: width, height: height}); + svg.select(".container-group") + .attr({transform: "translate(" + margin.left + "," + margin.top + ")"}); + + svg.select(".x-axis-group.axis") + .transition() + .ease(ease) + .attr({transform: "translate(0," + (chartH) + ")"}) + .call(xAxis); + + svg.select(".y-axis-group.axis") + .transition() + .ease(ease) + .call(yAxis); + + var gapSize = x1.rangeBand() / 100 * gap; + var barW = x1.rangeBand() - gapSize; + var bars = svg.select(".chart-group") + .selectAll(".bar") + .data(_data); + bars.enter().append("rect") + .classed("bar", true) + .attr({x: chartW, + width: barW, + y: function(d, i) { return y1(d); }, + height: function(d, i) { return chartH - y1(d); } + }) + .on("mouseover", dispatch.customHover); + bars.transition() + .ease(ease) + .attr({ + width: barW, + x: function(d, i) { return x1(i) + gapSize / 2; }, + y: function(d, i) { return y1(d); }, + height: function(d, i) { return chartH - y1(d); } + }); + bars.exit().transition().style({opacity: 0}).remove(); + }); + } + exports.width = function(_x) { + if (!arguments.length) return width; + width = parseInt(_x); + return this; + }; + exports.height = function(_x) { + if (!arguments.length) return height; + height = parseInt(_x); + return this; + }; + exports.gap = function(_x) { + if (!arguments.length) return gap; + gap = _x; + return this; + }; + exports.ease = function(_x) { + if (!arguments.length) return ease; + ease = _x; + return this; + }; + d3.rebind(exports, dispatch, "on"); + return exports; +}; + +// Usage +///////////////////////////////// + +var chart = d3.custom.barChart(); + +function update() { + var data = randomDataset(); + d3.select("#figure") + .datum(data) + .call(chart); +} + +function randomDataset() { + return d3.range(~~(Math.random() * 50)).map(function(d, i) { + return ~~(Math.random() * 1000); + }); +} + +update(); + +setInterval(update, 1000); \ No newline at end of file diff --git a/code/Chapter04/ReusableWithAxes/snippet.html b/code/Chapter04/ReusableWithAxes/snippet.html new file mode 100644 index 0000000..4e10151 --- /dev/null +++ b/code/Chapter04/ReusableWithAxes/snippet.html @@ -0,0 +1 @@ +
001:// Bar chart Module
002://///////////////////////////////
003:
004:d3.custom = {};
005:
006:d3.custom.barChart = function module() {
007:    var margin = {top: 20, right: 20, bottom: 40, left: 40},
008:        width = 500,
009:        height = 500,
010:        gap = 0,
011:        ease = "bounce";
012:    var svg;
013:
014:    var dispatch = d3.dispatch("customHover");
015:    function exports(_selection) {
016:        _selection.each(function(_data) {
017:            var chartW = width - margin.left - margin.right,
018:                chartH = height - margin.top - margin.bottom;
019:
020:            var x1 = d3.scale.ordinal()
021:                    .domain(_data.map(function(d, i) { return i; }))
022:                    .rangeRoundBands([0, chartW], .1);
023:
024:            var y1 = d3.scale.linear()
025:                    .domain([0, d3.max(_data, function(d, i) { return d; })])
026:                    .range([chartH, 0]);
027:
028:            var xAxis = d3.svg.axis()
029:                    .scale(x1)
030:                    .orient("bottom");
031:
032:            var yAxis = d3.svg.axis()
033:                    .scale(y1)
034:                    .orient("left");
035:
036:            var barW = chartW / _data.length;
037:
038:            if (!svg) {
039:                svg = d3.select(this)
040:                    .append("svg")
041:                    .classed("chart"true);
042:                var container = svg.append("g").classed("container-group"true);
043:                container.append("g").classed("chart-group"true);
044:                container.append("g").classed("x-axis-group axis"true);
045:                container.append("g").classed("y-axis-group axis"true);
046:            }
047:
048:            svg.transition().attr({width: width, height: height});
049:            svg.select(".container-group")
050:                .attr({transform: "translate(" + margin.left + "," + margin.top + ")"});
051:
052:            svg.select(".x-axis-group.axis")
053:                .transition()
054:                .ease(ease)
055:                .attr({transform: "translate(0," + (chartH) + ")"})
056:                .call(xAxis);
057:
058:            svg.select(".y-axis-group.axis")
059:                .transition()
060:                .ease(ease)
061:                .call(yAxis);
062:
063:            var gapSize = x1.rangeBand() / 100 * gap;
064:            var barW = x1.rangeBand() - gapSize;
065:            var bars = svg.select(".chart-group")
066:                    .selectAll(".bar")
067:                    .data(_data);
068:            bars.enter().append("rect")
069:                .classed("bar"true)
070:                .attr({x: chartW,
071:                    width: barW,
072:                    y: function(d, i) { return y1(d); },
073:                    height: function(d, i) { return chartH - y1(d); }
074:                })
075:                .on("mouseover", dispatch.customHover);
076:            bars.transition()
077:                .ease(ease)
078:                .attr({
079:                    width: barW,
080:                    x: function(d, i) { return x1(i) + gapSize / 2; },
081:                    y: function(d, i) { return y1(d); },
082:                    height: function(d, i) { return chartH - y1(d); }
083:                });
084:            bars.exit().transition().style({opacity: 0}).remove();
085:        });
086:    }
087:    exports.width = function(_x) {
088:        if (!arguments.length) return width;
089:        width = parseInt(_x);
090:        return this;
091:    };
092:    exports.height = function(_x) {
093:        if (!arguments.length) return height;
094:        height = parseInt(_x);
095:        return this;
096:    };
097:    exports.gap = function(_x) {
098:        if (!arguments.length) return gap;
099:        gap = _x;
100:        return this;
101:    };
102:    exports.ease = function(_x) {
103:        if (!arguments.length) return ease;
104:        ease = _x;
105:        return this;
106:    };
107:    d3.rebind(exports, dispatch, "on");
108:    return exports;
109:};
110:
111:// Usage
112://///////////////////////////////
113:
114:var chart = d3.custom.barChart();
115:
116:function update() {
117:    var data = randomDataset();
118:    d3.select("#figure")
119:        .datum(data)
120:        .call(chart);
121:}
122:
123:function randomDataset() {
124:    return d3.range(~~(Math.random() * 50)).map(function(d, i) {
125:        return ~~(Math.random() * 1000);
126:    });
127:}
128:
129:update();
130:
131:setInterval(update, 1000);
From 829c1ca76ab4bff18527b79015731b4e8c12a9ad Mon Sep 17 00:00:00 2001 From: micahstubbs Date: Sat, 8 Nov 2014 20:19:59 -0600 Subject: [PATCH 2/2] removed folders with 'Resuable' spelling in name --- code/Chapter04/ResuableBarChart/index.html | 67 ---------- code/Chapter04/ResuableBarChart/script.js | 96 -------------- code/Chapter04/ResuableBarChart/snippet.html | 1 - code/Chapter04/ResuableWithAxes/index.html | 59 --------- code/Chapter04/ResuableWithAxes/script.js | 131 ------------------- code/Chapter04/ResuableWithAxes/snippet.html | 1 - 6 files changed, 355 deletions(-) delete mode 100644 code/Chapter04/ResuableBarChart/index.html delete mode 100644 code/Chapter04/ResuableBarChart/script.js delete mode 100644 code/Chapter04/ResuableBarChart/snippet.html delete mode 100644 code/Chapter04/ResuableWithAxes/index.html delete mode 100644 code/Chapter04/ResuableWithAxes/script.js delete mode 100644 code/Chapter04/ResuableWithAxes/snippet.html diff --git a/code/Chapter04/ResuableBarChart/index.html b/code/Chapter04/ResuableBarChart/index.html deleted file mode 100644 index fd081d5..0000000 --- a/code/Chapter04/ResuableBarChart/index.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - -
-
- -
-
-
- Usage: hover with the mouse over the bars to see their value information. -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

Code

-
-
-
-
-
-
-
-
- - - - diff --git a/code/Chapter04/ResuableBarChart/script.js b/code/Chapter04/ResuableBarChart/script.js deleted file mode 100644 index 7f62185..0000000 --- a/code/Chapter04/ResuableBarChart/script.js +++ /dev/null @@ -1,96 +0,0 @@ -// Simplified two bar charts example: The Reusable Version -/////////////////////////////////////////////////////////////////////// -// Chart module -d3.edge = {}; - -d3.edge.barChart = function module() { - var w = 400, - h = 300; - var dispatch = d3.dispatch("customHover"); - function exports(_selection) { - _selection.each(function(_data) { - var barW = w / _data.length, - scaling = h / d3.max(_data); - d3.select(this) - .append("svg") - .attr({class: "chart2", width: w, height: h}) - .append("g") - .selectAll(".bar") - .data(_data) - .enter().append("rect") - .attr({ - class: "bar", - x: function(d, i) { return i * barW; }, - width: barW, - y: function(d, i) { return h - d * scaling; }, - height: function(d, i) { return d * scaling; } - }) - .on("mouseover", dispatch.customHover); - }); - } - exports.w = function(_x) { - if (!arguments.length) return w; - w = _x; - return this; - }; - exports.h = function(_x) { - if (!arguments.length) return h; - h = _x; - return this; - }; - d3.rebind(exports, dispatch, "on"); - return exports; -}; - -// Usage - -// var msg = d3.select("#message").text; -// ^^^^^ won't work ... -// ... as D3 will crash internally with: -// `this` does not have a method `each()` -// which indeed it does not: using `msg` like that would -// feed D3 the WRONG context (`this`). This is JavaScript behaviour. -// -// D3 expects a selection context, so let's make sure it gets that. -// We do that by using another [self-invoking] closure construct, -// so we do not pollute the global variable space. -var msg = (function () { - var selection = d3.select("#message"); - return function (text_message) { - selection.text(text_message); - }; -})(); - -var barChart1 = d3.edge.barChart() - .w(100).h(200) - .on("customHover", function(d, i) { msg("chart1: " + d); }); -var barChart2 = d3.edge.barChart() - .w(300).h(100) - .on("customHover", function(d, i) { msg("chart2: " + d); }); - -var data1 = [10, 20, 30, 40]; -var data2 = [100, 40, 10, 80]; - -d3.select("#container1") - .datum(data1) - .call(barChart1); - -d3.select("#container2") - .datum(data2) - .call(barChart2); - - - -/////////////////////////////////////////////////////////////////////////////////// -// Advantages vs. "one-off" approach as shown in "classical" development such as: -// code/chapter-01.stadard-D3/02.bar-chart-one-off.html: -// -// - Not less code overall, but DRY (no repetition, a single code base to maintain) -// - Less code exposed to the user, the bar chart module can be in an external file -// - Exposes only the attributes that have to be changed (w, h, customHover) -// - Composable, a bar chart is added to a div container hence an axis could be simply added to a bar chart -// - Extensible, add get/setters, events at will -// - Public and private functions, you can keep some attributes immutable -// - Consistent API, the same approach as used in the core d3. The logic can be changed -// (i.e., adding transitions) but the API will largely stay the same. -/////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/code/Chapter04/ResuableBarChart/snippet.html b/code/Chapter04/ResuableBarChart/snippet.html deleted file mode 100644 index c97c069..0000000 --- a/code/Chapter04/ResuableBarChart/snippet.html +++ /dev/null @@ -1 +0,0 @@ -
001:// Simplified two bar charts example: The Reusable Version
002:///////////////////////////////////////////////////////////////////////
003:// Chart module
004:d3.edge = {};
005:
006:d3.edge.barChart = function module() {
007:    var w = 400,
008:        h = 300;
009:    var dispatch = d3.dispatch("customHover");
010:    function exports(_selection) {
011:        _selection.each(function(_data) {
012:            var barW = w / _data.length,
013:                scaling = h / d3.max(_data);
014:            d3.select(this)
015:                .append("svg")
016:                .attr({class: "chart2", width: w, height: h})
017:                .append("g")
018:                .selectAll(".bar")
019:                .data(_data)
020:                .enter().append("rect")
021:                .attr({
022:                    class: "bar",
023:                    x: function(d, i) { return i * barW; },
024:                    width: barW,
025:                    y: function(d, i) { return h - d * scaling; },
026:                    height: function(d, i) { return d * scaling; }
027:                })
028:                .on("mouseover", dispatch.customHover);
029:        });
030:    }
031:    exports.w = function(_x) {
032:        if (!arguments.length) return w;
033:        w = _x;
034:        return this;
035:    };
036:    exports.h = function(_x) {
037:        if (!arguments.length) return h;
038:        h = _x;
039:        return this;
040:    };
041:    d3.rebind(exports, dispatch, "on");
042:    return exports;
043:};
044:
045:// Usage
046:
047:// var msg = d3.select("#message").text;
048://                                ^^^^^ won't work ...
049:// ... as D3 will crash internally with:
050://    `this` does not have a method `each()`
051:// which indeed it does not: using `msg` like that would
052:// feed D3 the WRONG context (`this`). This is JavaScript behaviour.
053://
054:// D3 expects a selection context, so let's make sure it gets that.
055:// We do that by using another [self-invoking] closure construct,
056:// so we do not pollute the global variable space.
057:var msg = (function () {
058:    var selection = d3.select("#message");
059:    return function (text_message) {
060:        selection.text(text_message);
061:    };
062:})();
063:
064:var barChart1 = d3.edge.barChart()
065:    .w(100).h(200)
066:    .on("customHover"function(d, i) { msg("chart1: " + d); });
067:var barChart2 = d3.edge.barChart()
068:    .w(300).h(100)
069:    .on("customHover"function(d, i) { msg("chart2: " + d); });
070:
071:var data1 = [10203040];
072:var data2 = [100401080];
073:
074:d3.select("#container1")
075:    .datum(data1)
076:    .call(barChart1);
077:
078:d3.select("#container2")
079:    .datum(data2)
080:    .call(barChart2);
081:
082:
083:
084:///////////////////////////////////////////////////////////////////////////////////
085:// Advantages vs. "one-off" approach as shown in "classical" development such as:
086://     code/chapter-01.stadard-D3/02.bar-chart-one-off.html:
087://
088:// - Not less code overall, but DRY (no repetition, a single code base to maintain)
089:// - Less code exposed to the user, the bar chart module can be in an external file
090:// - Exposes only the attributes that have to be changed (w, h, customHover)
091:// - Composable, a bar chart is added to a div container hence an axis could be simply added to a bar chart
092:// - Extensible, add get/setters, events at will
093:// - Public and private functions, you can keep some attributes immutable
094:// - Consistent API, the same approach as used in the core d3. The logic can be changed
095://   (i.e., adding transitions) but the API will largely stay the same.
096:///////////////////////////////////////////////////////////////////////////////////
diff --git a/code/Chapter04/ResuableWithAxes/index.html b/code/Chapter04/ResuableWithAxes/index.html deleted file mode 100644 index d29554b..0000000 --- a/code/Chapter04/ResuableWithAxes/index.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - -
-
- -
-
-
- Chart shows new data every second using a transition (animation). No user interaction available. -
-
-
-
-
-
-
-
-
-

Code

-
-
-
-
-
-
-
-
- - - diff --git a/code/Chapter04/ResuableWithAxes/script.js b/code/Chapter04/ResuableWithAxes/script.js deleted file mode 100644 index 7f523de..0000000 --- a/code/Chapter04/ResuableWithAxes/script.js +++ /dev/null @@ -1,131 +0,0 @@ -// Bar chart Module -///////////////////////////////// - -d3.custom = {}; - -d3.custom.barChart = function module() { - var margin = {top: 20, right: 20, bottom: 40, left: 40}, - width = 500, - height = 500, - gap = 0, - ease = "bounce"; - var svg; - - var dispatch = d3.dispatch("customHover"); - function exports(_selection) { - _selection.each(function(_data) { - var chartW = width - margin.left - margin.right, - chartH = height - margin.top - margin.bottom; - - var x1 = d3.scale.ordinal() - .domain(_data.map(function(d, i) { return i; })) - .rangeRoundBands([0, chartW], 0.1); - - var y1 = d3.scale.linear() - .domain([0, d3.max(_data, function(d, i) { return d; })]) - .range([chartH, 0]); - - var xAxis = d3.svg.axis() - .scale(x1) - .orient("bottom"); - - var yAxis = d3.svg.axis() - .scale(y1) - .orient("left"); - - var barW = chartW / _data.length; - - if (!svg) { - svg = d3.select(this) - .append("svg") - .classed("chart", true); - var container = svg.append("g").classed("container-group", true); - container.append("g").classed("chart-group", true); - container.append("g").classed("x-axis-group axis", true); - container.append("g").classed("y-axis-group axis", true); - } - - svg.transition().attr({width: width, height: height}); - svg.select(".container-group") - .attr({transform: "translate(" + margin.left + "," + margin.top + ")"}); - - svg.select(".x-axis-group.axis") - .transition() - .ease(ease) - .attr({transform: "translate(0," + (chartH) + ")"}) - .call(xAxis); - - svg.select(".y-axis-group.axis") - .transition() - .ease(ease) - .call(yAxis); - - var gapSize = x1.rangeBand() / 100 * gap; - var barW = x1.rangeBand() - gapSize; - var bars = svg.select(".chart-group") - .selectAll(".bar") - .data(_data); - bars.enter().append("rect") - .classed("bar", true) - .attr({x: chartW, - width: barW, - y: function(d, i) { return y1(d); }, - height: function(d, i) { return chartH - y1(d); } - }) - .on("mouseover", dispatch.customHover); - bars.transition() - .ease(ease) - .attr({ - width: barW, - x: function(d, i) { return x1(i) + gapSize / 2; }, - y: function(d, i) { return y1(d); }, - height: function(d, i) { return chartH - y1(d); } - }); - bars.exit().transition().style({opacity: 0}).remove(); - }); - } - exports.width = function(_x) { - if (!arguments.length) return width; - width = parseInt(_x); - return this; - }; - exports.height = function(_x) { - if (!arguments.length) return height; - height = parseInt(_x); - return this; - }; - exports.gap = function(_x) { - if (!arguments.length) return gap; - gap = _x; - return this; - }; - exports.ease = function(_x) { - if (!arguments.length) return ease; - ease = _x; - return this; - }; - d3.rebind(exports, dispatch, "on"); - return exports; -}; - -// Usage -///////////////////////////////// - -var chart = d3.custom.barChart(); - -function update() { - var data = randomDataset(); - d3.select("#figure") - .datum(data) - .call(chart); -} - -function randomDataset() { - return d3.range(~~(Math.random() * 50)).map(function(d, i) { - return ~~(Math.random() * 1000); - }); -} - -update(); - -setInterval(update, 1000); \ No newline at end of file diff --git a/code/Chapter04/ResuableWithAxes/snippet.html b/code/Chapter04/ResuableWithAxes/snippet.html deleted file mode 100644 index 4e10151..0000000 --- a/code/Chapter04/ResuableWithAxes/snippet.html +++ /dev/null @@ -1 +0,0 @@ -
001:// Bar chart Module
002://///////////////////////////////
003:
004:d3.custom = {};
005:
006:d3.custom.barChart = function module() {
007:    var margin = {top: 20, right: 20, bottom: 40, left: 40},
008:        width = 500,
009:        height = 500,
010:        gap = 0,
011:        ease = "bounce";
012:    var svg;
013:
014:    var dispatch = d3.dispatch("customHover");
015:    function exports(_selection) {
016:        _selection.each(function(_data) {
017:            var chartW = width - margin.left - margin.right,
018:                chartH = height - margin.top - margin.bottom;
019:
020:            var x1 = d3.scale.ordinal()
021:                    .domain(_data.map(function(d, i) { return i; }))
022:                    .rangeRoundBands([0, chartW], .1);
023:
024:            var y1 = d3.scale.linear()
025:                    .domain([0, d3.max(_data, function(d, i) { return d; })])
026:                    .range([chartH, 0]);
027:
028:            var xAxis = d3.svg.axis()
029:                    .scale(x1)
030:                    .orient("bottom");
031:
032:            var yAxis = d3.svg.axis()
033:                    .scale(y1)
034:                    .orient("left");
035:
036:            var barW = chartW / _data.length;
037:
038:            if (!svg) {
039:                svg = d3.select(this)
040:                    .append("svg")
041:                    .classed("chart"true);
042:                var container = svg.append("g").classed("container-group"true);
043:                container.append("g").classed("chart-group"true);
044:                container.append("g").classed("x-axis-group axis"true);
045:                container.append("g").classed("y-axis-group axis"true);
046:            }
047:
048:            svg.transition().attr({width: width, height: height});
049:            svg.select(".container-group")
050:                .attr({transform: "translate(" + margin.left + "," + margin.top + ")"});
051:
052:            svg.select(".x-axis-group.axis")
053:                .transition()
054:                .ease(ease)
055:                .attr({transform: "translate(0," + (chartH) + ")"})
056:                .call(xAxis);
057:
058:            svg.select(".y-axis-group.axis")
059:                .transition()
060:                .ease(ease)
061:                .call(yAxis);
062:
063:            var gapSize = x1.rangeBand() / 100 * gap;
064:            var barW = x1.rangeBand() - gapSize;
065:            var bars = svg.select(".chart-group")
066:                    .selectAll(".bar")
067:                    .data(_data);
068:            bars.enter().append("rect")
069:                .classed("bar"true)
070:                .attr({x: chartW,
071:                    width: barW,
072:                    y: function(d, i) { return y1(d); },
073:                    height: function(d, i) { return chartH - y1(d); }
074:                })
075:                .on("mouseover", dispatch.customHover);
076:            bars.transition()
077:                .ease(ease)
078:                .attr({
079:                    width: barW,
080:                    x: function(d, i) { return x1(i) + gapSize / 2; },
081:                    y: function(d, i) { return y1(d); },
082:                    height: function(d, i) { return chartH - y1(d); }
083:                });
084:            bars.exit().transition().style({opacity: 0}).remove();
085:        });
086:    }
087:    exports.width = function(_x) {
088:        if (!arguments.length) return width;
089:        width = parseInt(_x);
090:        return this;
091:    };
092:    exports.height = function(_x) {
093:        if (!arguments.length) return height;
094:        height = parseInt(_x);
095:        return this;
096:    };
097:    exports.gap = function(_x) {
098:        if (!arguments.length) return gap;
099:        gap = _x;
100:        return this;
101:    };
102:    exports.ease = function(_x) {
103:        if (!arguments.length) return ease;
104:        ease = _x;
105:        return this;
106:    };
107:    d3.rebind(exports, dispatch, "on");
108:    return exports;
109:};
110:
111:// Usage
112://///////////////////////////////
113:
114:var chart = d3.custom.barChart();
115:
116:function update() {
117:    var data = randomDataset();
118:    d3.select("#figure")
119:        .datum(data)
120:        .call(chart);
121:}
122:
123:function randomDataset() {
124:    return d3.range(~~(Math.random() * 50)).map(function(d, i) {
125:        return ~~(Math.random() * 1000);
126:    });
127:}
128:
129:update();
130:
131:setInterval(update, 1000);