Skip to content

Instantly share code, notes, and snippets.

@karlbright
Last active August 29, 2015 14:07
Show Gist options
  • Save karlbright/74201fd7e070fced0376 to your computer and use it in GitHub Desktop.
Save karlbright/74201fd7e070fced0376 to your computer and use it in GitHub Desktop.
// GV Slider
"use strict";
var gvcharts = gvcharts || {};
gvcharts.defaults = {
margin: {top: 80, right: 80, bottom: 240, left: 200},
padding: 20,
axisBorder: 4,
axisRadius: 8,
width: $(".module").width(),
height: $(".module").height(),
sliderHeight: 50
}
d3.chart("gv-slider", {
initialize: function(options) {
$.extend(this, gvcharts.defaults, options);
var chart = this;
chart.height = chart.height - chart.margin.top - chart.margin.bottom;
chart.width = chart.width - chart.margin.left - chart.margin.right;
chart.barHeight = (chart.sliderHeight - (chart.axisBorder * 2));
chart.barWidth = chart.width;
// axis
var t, b, bAxis, tAxis;
var linear = d3.scale.linear();
var directions = {
"lr": [0, chart.width],
"rl": [chart.width, 0],
"tb": [chart.height, 0],
"bt": [0, chart.height]
};
t = linear.range(directions["lr"]);
b = linear.range(directions["lr"]);
tAxis = d3.svg.axis().scale(t).tickSize(0).orient("top");
bAxis = d3.svg.axis().scale(b).tickSize(10).orient("bottom");
// layers
chart.base.classed("gv-slider", true);
chart.svg = chart.base
.append("svg")
.classed("svg", true)
.attr("width", "100%")
.attr("height", "100%");
chart.layers = chart.svg
.append("g")
.classed("group", true)
.attr("transform", "translate(" + chart.margin.left + "," + chart.margin.top + ")");
chart.bg = chart.layers
.append("g")
.classed("bg", true)
.append("rect")
.attr("width", chart.width)
.attr("height", chart.sliderHeight)
.attr("fill", "black")
.attr("rx", chart.axisRadius)
.attr("ry", chart.axisRadius);
chart.svg.append("defs")
.append("pattern")
.attr("id", "diagonal")
.attr("patternUnits", "userSpaceOnUse")
.attr("width", 10)
.attr("height", 10)
.append("path")
.attr("d", "M-1,1 l2,-10 M0,10 l10,-10");
chart.actual = chart.layers
.append("g")
.classed("actual", true)
.attr("transform", "translate(" + (chart.axisBorder - 2) + "," + (chart.axisBorder - 2) + ")");
chart.ideal = chart.layers
.append("g")
.classed("ideal", true)
.attr("transform", "translate(" + (chart.axisBorder - 2) + "," + (chart.axisBorder - 2) + ")");
chart.topaxis = chart.layers
.append("g")
.classed("top-axis axis", true);
chart.bottomaxis = chart.layers
.append("g")
.classed("bottom-axis axis", true)
.attr("transform", "translate(" + 0 + "," + (chart.sliderHeight - 2) + ")");
// helper functions
var maxvalue = function(d) { return d.maxvalue; },
minvalue = function(d) { return d.minvalue; },
value = function(d) { return d.value; };
var h = (chart.sliderHeight - chart.axisBorder),
r = chart.axisBorder + 2;
var rInner = r;
var tl, tr, bl, br = 0;
var actualSize = function(d) {
var end = roundedRect(0, 0, t(d.maxvalue) - chart.axisBorder, h, r, rInner, rInner, rInner, rInner),
start = roundedRect(0, 0, 0, h, r, tl, tr, bl, br),
mid = roundedRect(0, 0, t(d.value) - chart.axisBorder, h, r, rInner, tr, rInner, br);
if (d.value >= d.maxvalue) {
return end;
} else if (d.value <= 0 || d.value <= minActual) {
return start;
} else {
return mid;
}
}
var idealSize = function(d) {
var start = roundedRect(0, 0, (b(d.maxvalue) + chart.axisBorder), h, r, tl, tr, bl, br),
mid = roundedRect(0, 0, (b(d.maxvalue - d.minvalue + minActual) + chart.axisBorder), h, r, tl, tr, bl, br),
end = roundedRect(0, 0, (b(maxActual - d.minvalue) + chart.axisBorder), h, r, tl, tr, bl, br),
endBoth = roundedRect(0, 0, (b(maxActual) + chart.axisBorder), h, r, tl, tr, bl, br);
// ideal values vs actual
if (d.minvalue <= minActual && d.maxvalue <= maxActual) {
return start;
} else if (d.minvalue >= minActual && d.maxvalue >= maxActual) {
return end;
} else if (d.minvalue <= minActual && d.maxvalue >= maxActual) {
return endBoth;
} else {
return mid;
}
}
var idealTranslate = function(d) {
var start = "translate(" + (0 - chart.axisBorder) + ", 0)",
mid = "translate(" + (b(d.minvalue) - chart.axisBorder) + ", 0)";
if (d.minvalue <= minActual) {
return start;
} else {
return mid;
}
}
var roundedRect = function (x, y, w, h, r, tl, tr, bl, br) {
var retval;
retval = "M" + (x + r) + "," + y;
retval += "h" + (w - 2*r);
if (tr) { retval += "a" + r + "," + r + " 0 0 1 " + r + "," + r; } else { retval += "h" + r; retval += "v" + r; }
retval += "v" + (h - 2*r);
if (br) { retval += "a" + r + "," + r + " 0 0 1 " + -r + "," + r; } else { retval += "v" + r; retval += "h" + -r; }
retval += "h" + (2*r - w);
if (bl) { retval += "a" + r + "," + r + " 0 0 1 " + -r + "," + -r; } else { retval += "h" + -r; retval += "v" + -r; }
retval += "v" + (2*r - h);
if (tl) { retval += "a" + r + "," + r + " 0 0 1 " + r + "," + -r; } else { retval += "v" + -r; retval += "h" + r; }
retval += "z";
return retval;
}
// actual layer
var actualData, idealData, suffix, rangeActual, minActual, maxActual;
chart.layer("actual", chart.actual, (function(){
return {
dataBind: function(data) {
suffix = "ppm";
actualData = $.map(data, function(set) { set.actual; });
actualData = $.map(actualData, function(set) { set.minValue; });
actualData = $.map(actualData, function(set) { set.maxValue; });
rangeActual = [d3.min(actualData, minvalue), d3.max(actualData, maxvalue)];
tAxis.tickValues(rangeActual).tickFormat(function(d) { return d + " " + suffix; });
t.domain(rangeActual);
return this.selectAll("path").data(actualData, value);
},
insert: function() {
this.append("path")
.attr("height", chart.barHeight)
.attr("d", actualSize)
.attr("data-name", function(d, i) { return "actual-" + (i + 1); });
this.append("text")
.text(function(d) { return d.value + " " + suffix; })
.attr("y", (chart.sliderHeight/2) + 2)
.attr("x", -20)
.style("text-anchor", "end");
return this;
}
};
}()));
// ideal layer
chart.layer("ideal", chart.ideal, {
dataBind: function(data) {
var min, max, rangeIdeal;
min = d3.max(idealData, minvalue),
max = d3.max(idealData, maxvalue);
rangeIdeal = [(function() {
if (min <= 0 || min <= minActual) {
return minActual;
} else {
return min;
}
})(), (function() {
if (max >= maxActual) {
return maxActual;
} else {
return max;
}
})()];
bAxis.tickValues(rangeIdeal)
.tickFormat(function(d) { return d + " " + suffix; });
b.domain(rangeActual);
return this.selectAll("path").data(idealData, value);
},
insert: function() {
this.append("path")
.attr("height", chart.barHeight)
.attr("transform", idealTranslate)
.attr("d", idealSize)
.attr('fill', 'url(#diagonal)')
.attr("data-name", function(d, i) {
return "ideal-" + (i + 1);
});
return this;
}
});
// top-axis layer
chart.layer("topaxis", chart.topaxis, {
dataBind: function(data) {
chart.topaxis.call(tAxis);
return this.selectAll('text').data(data);
},
insert: function() {
chart.topaxis
.selectAll("text")
.attr("dy", "-0.75em");
chart.topaxis.selectAll("text")
.style("text-anchor", null);
$(".top-axis .tick").last().find("text").attr("text-anchor", "end");
$(".top-axis .tick").first().find("text").attr("text-anchor", "start");
chart.topaxis
.selectAll("path")
.remove();
return this;
}
});
// bottom-axis layer
chart.layer("bottomaxis", chart.bottomaxis, {
dataBind: function(data) {
chart.bottomaxis.call(bAxis);
return this.selectAll('text').data(data);
},
insert: function() {
chart.bottomaxis
.selectAll("text")
.attr("dy", "1em");
chart.bottomaxis
.selectAll("path")
.remove();
return this;
}
});
// responsive
var responsive = function() {
// reset width
var width = $(".module").width() - chart.margin.left - chart.margin.right;
// redraw bg
chart.bg.attr("width", width);
// redraw axis
t.range([0, width]);
chart.topaxis.call(tAxis);
chart.topaxis.selectAll("text").attr("dy", "-0.75em");
chart.topaxis.selectAll("text").style("text-anchor", null);
$(".top-axis .tick").last().find("text").attr("text-anchor", "end");
$(".top-axis .tick").first().find("text").attr("text-anchor", "start");
chart.topaxis.selectAll("path").remove();
b.range([0, width]);
chart.bottomaxis.call(bAxis);
chart.bottomaxis.selectAll("text").attr("dy", "1em");
chart.bottomaxis.selectAll("path").remove();
// actual/ideal
chart.actual.selectAll("path")
.attr("d", actualSize);
chart.ideal.selectAll("path")
.attr("transform", idealTranslate)
.attr("d", idealSize);
}
$(window).on("resize", responsive);
responsive();
}
});
// init
var dataset = [
{
"actual": [
{
"value": 50,
"minvalue": 20,
"maxvalue": 400,
"suffix": "ppm",
"group": "Group 1"
}
],
"ideal": [
{
"minvalue": 50,
"maxvalue": 200,
"suffix": "ppm",
"group": "Group 1"
}
]
},
{
"actual": [
{
"value": 50,
"minvalue": 20,
"maxvalue": 400,
"suffix": "ppm",
"group": "Group 1"
}
],
"ideal": [
{
"minvalue": 50,
"maxvalue": 200,
"suffix": "ppm",
"group": "Group 1"
}
]
}
];
var slider = d3.select(".module")
.chart("gv-slider", {
defaults: gvcharts.defaults
});
slider.draw(dataset);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment