Comparing version 0.6.3 to 0.6.4
@@ -14,3 +14,4 @@ (function(global) { | ||
*/ | ||
function D3Funnel(selector) { | ||
var D3Funnel = function(selector) | ||
{ | ||
this.selector = selector; | ||
@@ -30,7 +31,9 @@ | ||
dynamicArea: false, | ||
animation: false, | ||
label: { | ||
fontSize: "14px" | ||
fontSize: "14px", | ||
fill: "#fff" | ||
} | ||
}; | ||
} | ||
}; | ||
@@ -44,3 +47,4 @@ /** | ||
*/ | ||
D3Funnel.prototype._isArray = function(value) { | ||
D3Funnel.prototype.__isArray = function(value) | ||
{ | ||
return Object.prototype.toString.call(value) === "[object Array]"; | ||
@@ -57,3 +61,4 @@ }; | ||
*/ | ||
D3Funnel.prototype._extend = function(a, b) { | ||
D3Funnel.prototype.__extend = function(a, b) | ||
{ | ||
var prop; | ||
@@ -98,8 +103,11 @@ for (prop in b) { | ||
* data counts. | ||
* @param {int} options.animation The load animation speed. If empty, | ||
* there will be no load animation. | ||
* @param {Object} options.label | ||
* @param {Object} options.label.fontSize | ||
*/ | ||
D3Funnel.prototype.draw = function(data, options) { | ||
D3Funnel.prototype.draw = function(data, options) | ||
{ | ||
// Initialize chart options | ||
this._initialize(data, options); | ||
this.__initialize(data, options); | ||
@@ -110,3 +118,3 @@ // Remove any previous drawings | ||
// Add the SVG and group element | ||
var svg = d3.select(this.selector) | ||
this.svg = d3.select(this.selector) | ||
.append("svg") | ||
@@ -116,10 +124,8 @@ .attr("width", this.width) | ||
.append("g"); | ||
var group = {}; | ||
var path = {}; | ||
var sectionPaths = this._makePaths(); | ||
this.sectionPaths = this.__makePaths(); | ||
// Define color gradients | ||
if (this.fillType === "gradient") { | ||
this._defineColorGradients(svg); | ||
this.__defineColorGradients(this.svg); | ||
} | ||
@@ -129,64 +135,67 @@ | ||
if (this.isCurved) { | ||
this._drawTopOval(svg, sectionPaths); | ||
this.__drawTopOval(this.svg, this.sectionPaths); | ||
} | ||
// Add each block section | ||
for (var i = 0; i < sectionPaths.length; i++) { | ||
// Set the background color | ||
var fill = this.fillType !== "gradient" ? | ||
this.data[i][2] : | ||
"url(#gradient-" + i + ")"; | ||
this.__drawSection(0); | ||
}; | ||
// Prepare data to assign to the section | ||
data = { | ||
index: i, | ||
label: this.data[i][0], | ||
value: this.data[i][1], | ||
baseColor: this.data[i][2], | ||
fill: fill | ||
}; | ||
/** | ||
* Draw the next section in the iteration. | ||
* | ||
* @param {int} index | ||
*/ | ||
D3Funnel.prototype.__drawSection = function(index) | ||
{ | ||
if (index === this.data.length) { | ||
return; | ||
} | ||
// Construct path string | ||
var paths = sectionPaths[i]; | ||
var pathStr = ""; | ||
// Create a group just for this block | ||
var group = this.svg.append("g"); | ||
// Iterate through each point | ||
for (var j = 0; j < paths.length; j++) { | ||
path = paths[j]; | ||
pathStr += path[2] + path[0] + "," + path[1] + " "; | ||
} | ||
// Fetch path element | ||
var path = this.__getSectionPath(group, index); | ||
path.data(this.__getSectionData(index)); | ||
// Create a group just for this block | ||
group = svg.append("g"); | ||
// Add animation components | ||
if (this.animation !== false) { | ||
var self = this; | ||
path.transition() | ||
.duration(this.animation) | ||
.ease("linear") | ||
.attr("fill", this.__getColor(index)) | ||
.attr("d", this.__getPathDefinition(index)) | ||
.each("end", function() { | ||
self.__drawSection(index + 1); | ||
}); | ||
} else { | ||
path.attr("fill", this.__getColor(index)) | ||
.attr("d", this.__getPathDefinition(index)); | ||
this.__drawSection(index + 1); | ||
} | ||
// Draw the sections's path and append the data | ||
path = group.append("path") | ||
.attr("fill", fill) | ||
.attr("d", pathStr) | ||
.data([data]); | ||
console.log(":("); | ||
// Add the hover events | ||
if (this.hoverEffects) { | ||
path.on("mouseover", this._onMouseOver) | ||
.on("mouseout", this._onMouseOut); | ||
} | ||
// Add the section label | ||
var textStr = this.data[i][0] + ": " + this.data[i][1].toLocaleString(); | ||
var textX = this.width / 2; // Center the text | ||
var textY = !this.isCurved ? // Average height of bases | ||
(paths[1][1] + paths[2][1]) / 2 : | ||
(paths[2][1] + paths[3][1]) / 2 + (this.curveHeight / this.data.length); | ||
// Add the hover events | ||
if (this.hoverEffects) { | ||
path.on("mouseover", this.__onMouseOver) | ||
.on("mouseout", this.__onMouseOut); | ||
} | ||
group.append("text") | ||
.text(textStr) | ||
.attr({ | ||
"x": textX, | ||
"y": textY, | ||
"text-anchor": "middle", | ||
"dominant-baseline": "middle", | ||
"fill": "#fff", | ||
"pointer-events": "none" | ||
}) | ||
.style("font-size", this.label.fontSize); | ||
this.__addSectionLabel(group, index); | ||
}; | ||
/** | ||
* Return the color for the given index. | ||
* | ||
* @param {int} index | ||
*/ | ||
D3Funnel.prototype.__getColor = function(index) | ||
{ | ||
if (this.fillType === "solid") { | ||
return this.data[index][2]; | ||
} else { | ||
return "url(#gradient-" + index + ")"; | ||
} | ||
@@ -196,2 +205,125 @@ }; | ||
/** | ||
* @param {Object} group | ||
* @param {int} index | ||
* | ||
* @return {Object} | ||
*/ | ||
D3Funnel.prototype.__getSectionPath = function(group, index) | ||
{ | ||
var path = group.append("path"); | ||
if (this.animation !== false) { | ||
this.__addBeforeTransition(path, index); | ||
} | ||
return path; | ||
}; | ||
/** | ||
* Set the attributes of a path element before its animation. | ||
* | ||
* @param {Object} path | ||
* @param {int} index | ||
*/ | ||
D3Funnel.prototype.__addBeforeTransition = function(path, index) | ||
{ | ||
var paths = this.sectionPaths[index]; | ||
var beforePath = ""; | ||
var beforeFill = ""; | ||
// Construct the top of the trapezoid and leave the other elements | ||
// hovering around to expand downward on animation | ||
if (!this.isCurved) { | ||
beforePath = "M" + paths[0][0] + "," + paths[0][1] + | ||
" L" + paths[1][0] + "," + paths[1][1] + | ||
" L" + paths[1][0] + "," + paths[1][1] + | ||
" L" + paths[0][0] + "," + paths[0][1]; | ||
} else { | ||
beforePath = "M" + paths[0][0] + "," + paths[0][1] + | ||
" Q" + paths[1][0] + "," + paths[1][1] + | ||
" " + paths[2][0] + "," + paths[2][1] + | ||
" L" + paths[2][0] + "," + paths[2][1] + | ||
" M" + paths[2][0] + "," + paths[2][1] + | ||
" Q" + paths[1][0] + "," + paths[1][1] + | ||
" " + paths[0][0] + "," + paths[0][1]; | ||
} | ||
// Use previous fill color, if available | ||
if (this.fillType === "solid") { | ||
beforeFill = index > 0 ? this.__getColor(index - 1) : this.__getColor(index); | ||
// Use current background if gradient (gradients do not transition) | ||
} else { | ||
beforeFill = this.__getColor(index); | ||
} | ||
path.attr("d", beforePath) | ||
.attr("fill", beforeFill); | ||
}; | ||
/** | ||
* @param {int} index | ||
* | ||
* @return {array} | ||
*/ | ||
D3Funnel.prototype.__getSectionData = function(index) | ||
{ | ||
return [{ | ||
index: index, | ||
label: this.data[index][0], | ||
value: this.data[index][1], | ||
baseColor: this.data[index][2], | ||
fill: this.__getColor(index) | ||
}]; | ||
}; | ||
/** | ||
* @param {int} index | ||
* | ||
* @return {string} | ||
*/ | ||
D3Funnel.prototype.__getPathDefinition = function(index) | ||
{ | ||
var pathStr = ""; | ||
var point = []; | ||
var paths = this.sectionPaths[index]; | ||
for (var j = 0; j < paths.length; j++) { | ||
point = paths[j]; | ||
pathStr += point[2] + point[0] + "," + point[1] + " "; | ||
} | ||
return pathStr; | ||
}; | ||
/** | ||
* @param {Object} group | ||
* @param {int} index | ||
*/ | ||
D3Funnel.prototype.__addSectionLabel = function(group, index) | ||
{ | ||
var i = index; | ||
var paths = this.sectionPaths[index]; | ||
var textStr = this.data[i][0] + ": " + this.data[i][1].toLocaleString(); | ||
var textFill = this.data[i][3] || this.label.fill; | ||
var textX = this.width / 2; // Center the text | ||
var textY = !this.isCurved ? // Average height of bases | ||
(paths[1][1] + paths[2][1]) / 2 : | ||
(paths[2][1] + paths[3][1]) / 2 + (this.curveHeight / this.data.length); | ||
group.append("text") | ||
.text(textStr) | ||
.attr({ | ||
"x": textX, | ||
"y": textY, | ||
"text-anchor": "middle", | ||
"dominant-baseline": "middle", | ||
"fill": textFill, | ||
"pointer-events": "none" | ||
}) | ||
.style("font-size", this.label.fontSize); | ||
}; | ||
/** | ||
* Initialize and calculate important variables for drawing the chart. | ||
@@ -202,5 +334,6 @@ * | ||
*/ | ||
D3Funnel.prototype._initialize = function(data, options) { | ||
if (!this._isArray(data) || data.length === 0 || | ||
!this._isArray(data[0]) || data[0].length < 2) { | ||
D3Funnel.prototype.__initialize = function(data, options) | ||
{ | ||
if (!this.__isArray(data) || data.length === 0 || | ||
!this.__isArray(data[0]) || data[0].length < 2) { | ||
throw { | ||
@@ -222,3 +355,3 @@ name: "D3 Funnel Data Error", | ||
// Set the default width and height based on the container | ||
var settings = this._extend({}, this.defaults); | ||
var settings = this.__extend({}, this.defaults); | ||
settings.width = parseInt(d3.select(this.selector).style("width"), 10); | ||
@@ -237,4 +370,8 @@ settings.height = parseInt(d3.select(this.selector).style("height"), 10); | ||
if ("label" in options) { | ||
if ("fontSize" in options.label) { | ||
settings.label.fontSize = options.label.fontSize; | ||
var validLabelOptions = /fontSize|fill/; | ||
var labelOption; | ||
for (labelOption in options.label) { | ||
if (labelOption.match(validLabelOptions)) { | ||
settings.label[labelOption] = options.label[labelOption]; | ||
} | ||
} | ||
@@ -259,4 +396,3 @@ } | ||
// If a color is not set for the record, add one | ||
if (typeof this.data[i][2] === "undefined" || | ||
!hexExpression.test(this.data[i][2])) { | ||
if (!("2" in this.data[i]) || !hexExpression.test(this.data[i][2])) { | ||
this.data[i][2] = colorScale(i); | ||
@@ -277,2 +413,3 @@ } | ||
this.dynamicArea = settings.dynamicArea; | ||
this.animation = settings.animation; | ||
@@ -300,3 +437,4 @@ // Calculate the bottom left x position | ||
*/ | ||
D3Funnel.prototype._makePaths = function() { | ||
D3Funnel.prototype.__makePaths = function() | ||
{ | ||
var paths = []; | ||
@@ -448,3 +586,4 @@ | ||
*/ | ||
D3Funnel.prototype._defineColorGradients = function(svg) { | ||
D3Funnel.prototype.__defineColorGradients = function(svg) | ||
{ | ||
var defs = svg.append("defs"); | ||
@@ -488,3 +627,4 @@ | ||
*/ | ||
D3Funnel.prototype._drawTopOval = function(svg, sectionPaths) { | ||
D3Funnel.prototype.__drawTopOval = function(svg, sectionPaths) | ||
{ | ||
var leftX = 0; | ||
@@ -517,3 +657,4 @@ var rightX = this.width; | ||
*/ | ||
D3Funnel.prototype._onMouseOver = function(data) { | ||
D3Funnel.prototype.__onMouseOver = function(data) | ||
{ | ||
d3.select(this).attr("fill", shadeColor(data.baseColor, -0.2)); | ||
@@ -525,3 +666,4 @@ }; | ||
*/ | ||
D3Funnel.prototype._onMouseOut = function(data) { | ||
D3Funnel.prototype.__onMouseOut = function(data) | ||
{ | ||
d3.select(this).attr("fill", data.fill); | ||
@@ -536,3 +678,4 @@ }; | ||
*/ | ||
function shadeColor(color, shade) { | ||
function shadeColor(color, shade) | ||
{ | ||
var f = parseInt(color.slice(1), 16); | ||
@@ -553,2 +696,2 @@ var t = shade < 0 ? 0 : 255; | ||
})(this); | ||
})(window); |
@@ -1,2 +0,2 @@ | ||
/*! d3-funnel - v0.6.2 | 2015-02-04 */ | ||
!function(a){"use strict";function b(a){this.selector=a,this.defaults={width:350,height:400,bottomWidth:1/3,bottomPinch:0,isCurved:!1,curveHeight:20,fillType:"solid",isInverted:!1,hoverEffects:!1,dynamicArea:!1,label:{fontSize:"14px"}}}function c(a,b){var c=parseInt(a.slice(1),16),d=0>b?0:255,e=0>b?-1*b:b,f=c>>16,g=c>>8&255,h=255&c,i=16777216+65536*(Math.round((d-f)*e)+f)+256*(Math.round((d-g)*e)+g)+(Math.round((d-h)*e)+h);return"#"+i.toString(16).slice(1)}b.prototype._isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)},b.prototype._extend=function(a,b){var c;for(c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a},b.prototype.draw=function(a,b){this._initialize(a,b),d3.select(this.selector).selectAll("svg").remove();var c=d3.select(this.selector).append("svg").attr("width",this.width).attr("height",this.height).append("g"),d={},e={},f=this._makePaths();"gradient"===this.fillType&&this._defineColorGradients(c),this.isCurved&&this._drawTopOval(c,f);for(var g=0;g<f.length;g++){var h="gradient"!==this.fillType?this.data[g][2]:"url(#gradient-"+g+")";a={index:g,label:this.data[g][0],value:this.data[g][1],baseColor:this.data[g][2],fill:h};for(var i=f[g],j="",k=0;k<i.length;k++)e=i[k],j+=e[2]+e[0]+","+e[1]+" ";d=c.append("g"),e=d.append("path").attr("fill",h).attr("d",j).data([a]),this.hoverEffects&&e.on("mouseover",this._onMouseOver).on("mouseout",this._onMouseOut);var l=this.data[g][0]+": "+this.data[g][1].toLocaleString(),m=this.width/2,n=this.isCurved?(i[2][1]+i[3][1])/2+this.curveHeight/this.data.length:(i[1][1]+i[2][1])/2;d.append("text").text(l).attr({x:m,y:n,"text-anchor":"middle","dominant-baseline":"middle",fill:"#fff","pointer-events":"none"}).style("font-size",this.label.fontSize)}},b.prototype._initialize=function(a,b){if(!this._isArray(a)||0===a.length||!this._isArray(a[0])||a[0].length<2)throw{name:"D3 Funnel Data Error",message:"Funnel data is not valid."};b="undefined"!=typeof b?b:{},this.data=a;var c=0,d=this._extend({},this.defaults);d.width=parseInt(d3.select(this.selector).style("width"),10),d.height=parseInt(d3.select(this.selector).style("height"),10);var e=Object.keys(b);for(c=0;c<e.length;c++)"label"!==e[c]&&(d[e[c]]=b[e[c]]);"label"in b&&"fontSize"in b.label&&(d.label.fontSize=b.label.fontSize),this.label=d.label,d.width<=0&&(d.width=this.defaults.width),d.height<=0&&(d.height=this.defaults.height);var f=d3.scale.category10();for(c=0;c<this.data.length;c++){var g=/^#([0-9a-f]{3}|[0-9a-f]{6})$/i;"undefined"!=typeof this.data[c][2]&&g.test(this.data[c][2])||(this.data[c][2]=f(c))}this.width=d.width,this.height=d.height,this.bottomWidth=d.width*d.bottomWidth,this.bottomPinch=d.bottomPinch,this.isCurved=d.isCurved,this.curveHeight=d.curveHeight,this.fillType=d.fillType,this.isInverted=d.isInverted,this.hoverEffects=d.hoverEffects,this.dynamicArea=d.dynamicArea,this.bottomLeftX=(this.width-this.bottomWidth)/2,this.dx=this.bottomPinch>0?this.bottomLeftX/(a.length-this.bottomPinch):this.bottomLeftX/a.length,this.dy=this.isCurved?(this.height-this.curveHeight)/a.length:this.height/a.length},b.prototype._makePaths=function(){var a=[],b=this.dx,c=this.dy,d=0,e=this.width,f=0;this.isInverted&&(d=this.bottomLeftX,e=this.width-this.bottomLeftX);var g=0,h=0,i=0,j=this.width/2;this.isCurved&&(f=10);for(var k=this.width,l=0,m=this.height*(this.width+this.bottomWidth)/2,n=2*this.height/(this.width-this.bottomWidth),o=0,p=0,q=0;q<this.data.length;q++)o+=this.data[q][1];for(q=0;q<this.data.length;q++){if(p=this.data[q][1],this.dynamicArea){var r=p/o,s=r*m;l=Math.sqrt((n*k*k-4*s)/n),b=k/2-l/2,c=2*s/(k+l),this.isCurved&&(c-=this.curveHeight/this.data.length),k=l}this.bottomPinch>0&&(this.isInverted?(this.dynamicArea||(b=this.dx),b=q<this.bottomPinch?0:b):q>=this.data.length-this.bottomPinch&&(b=0)),g=d+b,h=e-b,i=f+c,this.isInverted&&(g=d-b,h=e+b),a.push(this.isCurved?[[d,f,"M"],[j,f+(this.curveHeight-10),"Q"],[e,f,""],[h,i,"L"],[h,i,"M"],[j,i+this.curveHeight,"Q"],[g,i,""],[d,f,"L"]]:[[d,f,"M"],[e,f,"L"],[h,i,"L"],[g,i,"L"],[d,f,"L"]]),d=g,e=h,f=i}return a},b.prototype._defineColorGradients=function(a){for(var b=a.append("defs"),d=0;d<this.data.length;d++)for(var e=this.data[d][2],f=c(e,-.25),g=b.append("linearGradient").attr({id:"gradient-"+d}),h=[[0,f],[40,e],[60,e],[100,f]],i=0;i<h.length;i++){var j=h[i];g.append("stop").attr({offset:j[0]+"%",style:"stop-color:"+j[1]})}},b.prototype._drawTopOval=function(a,b){var d=0,e=this.width,f=this.width/2;this.isInverted&&(d=this.bottomLeftX,e=this.width-this.bottomLeftX);var g=b[0],h="M"+d+","+g[0][1]+" Q"+f+","+(g[1][1]+this.curveHeight-10)+" "+e+","+g[2][1]+" M"+e+",10 Q"+f+",0 "+d+",10";a.append("path").attr("fill",c(this.data[0][2],-.4)).attr("d",h)},b.prototype._onMouseOver=function(a){d3.select(this).attr("fill",c(a.baseColor,-.2))},b.prototype._onMouseOut=function(a){d3.select(this).attr("fill",a.fill)},a.D3Funnel=b}(this); | ||
/*! d3-funnel - v0.6.4 | 2015-03-08 */ | ||
!function(a){"use strict";function b(a,b){var c=parseInt(a.slice(1),16),d=0>b?0:255,e=0>b?-1*b:b,f=c>>16,g=c>>8&255,h=255&c,i=16777216+65536*(Math.round((d-f)*e)+f)+256*(Math.round((d-g)*e)+g)+(Math.round((d-h)*e)+h);return"#"+i.toString(16).slice(1)}var c=function(a){this.selector=a,this.defaults={width:350,height:400,bottomWidth:1/3,bottomPinch:0,isCurved:!1,curveHeight:20,fillType:"solid",isInverted:!1,hoverEffects:!1,dynamicArea:!1,animation:!1,label:{fontSize:"14px",fill:"#fff"}}};c.prototype.__isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)},c.prototype.__extend=function(a,b){var c;for(c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a},c.prototype.draw=function(a,b){this.__initialize(a,b),d3.select(this.selector).selectAll("svg").remove(),this.svg=d3.select(this.selector).append("svg").attr("width",this.width).attr("height",this.height).append("g"),this.sectionPaths=this.__makePaths(),"gradient"===this.fillType&&this.__defineColorGradients(this.svg),this.isCurved&&this.__drawTopOval(this.svg,this.sectionPaths),this.__drawSection(0)},c.prototype.__drawSection=function(a){if(a!==this.data.length){var b=this.svg.append("g"),c=this.__getSectionPath(b,a);if(c.data(this.__getSectionData(a)),this.animation!==!1){var d=this;c.transition().duration(this.animation).ease("linear").attr("fill",this.__getColor(a)).attr("d",this.__getPathDefinition(a)).each("end",function(){d.__drawSection(a+1)})}else c.attr("fill",this.__getColor(a)).attr("d",this.__getPathDefinition(a)),this.__drawSection(a+1);console.log(":("),this.hoverEffects&&c.on("mouseover",this.__onMouseOver).on("mouseout",this.__onMouseOut),this.__addSectionLabel(b,a)}},c.prototype.__getColor=function(a){return"solid"===this.fillType?this.data[a][2]:"url(#gradient-"+a+")"},c.prototype.__getSectionPath=function(a,b){var c=a.append("path");return this.animation!==!1&&this.__addBeforeTransition(c,b),c},c.prototype.__addBeforeTransition=function(a,b){var c=this.sectionPaths[b],d="",e="";d=this.isCurved?"M"+c[0][0]+","+c[0][1]+" Q"+c[1][0]+","+c[1][1]+" "+c[2][0]+","+c[2][1]+" L"+c[2][0]+","+c[2][1]+" M"+c[2][0]+","+c[2][1]+" Q"+c[1][0]+","+c[1][1]+" "+c[0][0]+","+c[0][1]:"M"+c[0][0]+","+c[0][1]+" L"+c[1][0]+","+c[1][1]+" L"+c[1][0]+","+c[1][1]+" L"+c[0][0]+","+c[0][1],e=this.__getColor("solid"===this.fillType?b>0?b-1:b:b),a.attr("d",d).attr("fill",e)},c.prototype.__getSectionData=function(a){return[{index:a,label:this.data[a][0],value:this.data[a][1],baseColor:this.data[a][2],fill:this.__getColor(a)}]},c.prototype.__getPathDefinition=function(a){for(var b="",c=[],d=this.sectionPaths[a],e=0;e<d.length;e++)c=d[e],b+=c[2]+c[0]+","+c[1]+" ";return b},c.prototype.__addSectionLabel=function(a,b){var c=b,d=this.sectionPaths[b],e=this.data[c][0]+": "+this.data[c][1].toLocaleString(),f=this.data[c][3]||this.label.fill,g=this.width/2,h=this.isCurved?(d[2][1]+d[3][1])/2+this.curveHeight/this.data.length:(d[1][1]+d[2][1])/2;a.append("text").text(e).attr({x:g,y:h,"text-anchor":"middle","dominant-baseline":"middle",fill:f,"pointer-events":"none"}).style("font-size",this.label.fontSize)},c.prototype.__initialize=function(a,b){if(!this.__isArray(a)||0===a.length||!this.__isArray(a[0])||a[0].length<2)throw{name:"D3 Funnel Data Error",message:"Funnel data is not valid."};b="undefined"!=typeof b?b:{},this.data=a;var c=0,d=this.__extend({},this.defaults);d.width=parseInt(d3.select(this.selector).style("width"),10),d.height=parseInt(d3.select(this.selector).style("height"),10);var e=Object.keys(b);for(c=0;c<e.length;c++)"label"!==e[c]&&(d[e[c]]=b[e[c]]);if("label"in b){var f,g=/fontSize|fill/;for(f in b.label)f.match(g)&&(d.label[f]=b.label[f])}this.label=d.label,d.width<=0&&(d.width=this.defaults.width),d.height<=0&&(d.height=this.defaults.height);var h=d3.scale.category10();for(c=0;c<this.data.length;c++){var i=/^#([0-9a-f]{3}|[0-9a-f]{6})$/i;"2"in this.data[c]&&i.test(this.data[c][2])||(this.data[c][2]=h(c))}this.width=d.width,this.height=d.height,this.bottomWidth=d.width*d.bottomWidth,this.bottomPinch=d.bottomPinch,this.isCurved=d.isCurved,this.curveHeight=d.curveHeight,this.fillType=d.fillType,this.isInverted=d.isInverted,this.hoverEffects=d.hoverEffects,this.dynamicArea=d.dynamicArea,this.animation=d.animation,this.bottomLeftX=(this.width-this.bottomWidth)/2,this.dx=this.bottomPinch>0?this.bottomLeftX/(a.length-this.bottomPinch):this.bottomLeftX/a.length,this.dy=this.isCurved?(this.height-this.curveHeight)/a.length:this.height/a.length},c.prototype.__makePaths=function(){var a=[],b=this.dx,c=this.dy,d=0,e=this.width,f=0;this.isInverted&&(d=this.bottomLeftX,e=this.width-this.bottomLeftX);var g=0,h=0,i=0,j=this.width/2;this.isCurved&&(f=10);for(var k=this.width,l=0,m=this.height*(this.width+this.bottomWidth)/2,n=2*this.height/(this.width-this.bottomWidth),o=0,p=0,q=0;q<this.data.length;q++)o+=this.data[q][1];for(q=0;q<this.data.length;q++){if(p=this.data[q][1],this.dynamicArea){var r=p/o,s=r*m;l=Math.sqrt((n*k*k-4*s)/n),b=k/2-l/2,c=2*s/(k+l),this.isCurved&&(c-=this.curveHeight/this.data.length),k=l}this.bottomPinch>0&&(this.isInverted?(this.dynamicArea||(b=this.dx),b=q<this.bottomPinch?0:b):q>=this.data.length-this.bottomPinch&&(b=0)),g=d+b,h=e-b,i=f+c,this.isInverted&&(g=d-b,h=e+b),a.push(this.isCurved?[[d,f,"M"],[j,f+(this.curveHeight-10),"Q"],[e,f,""],[h,i,"L"],[h,i,"M"],[j,i+this.curveHeight,"Q"],[g,i,""],[d,f,"L"]]:[[d,f,"M"],[e,f,"L"],[h,i,"L"],[g,i,"L"],[d,f,"L"]]),d=g,e=h,f=i}return a},c.prototype.__defineColorGradients=function(a){for(var c=a.append("defs"),d=0;d<this.data.length;d++)for(var e=this.data[d][2],f=b(e,-.25),g=c.append("linearGradient").attr({id:"gradient-"+d}),h=[[0,f],[40,e],[60,e],[100,f]],i=0;i<h.length;i++){var j=h[i];g.append("stop").attr({offset:j[0]+"%",style:"stop-color:"+j[1]})}},c.prototype.__drawTopOval=function(a,c){var d=0,e=this.width,f=this.width/2;this.isInverted&&(d=this.bottomLeftX,e=this.width-this.bottomLeftX);var g=c[0],h="M"+d+","+g[0][1]+" Q"+f+","+(g[1][1]+this.curveHeight-10)+" "+e+","+g[2][1]+" M"+e+",10 Q"+f+",0 "+d+",10";a.append("path").attr("fill",b(this.data[0][2],-.4)).attr("d",h)},c.prototype.__onMouseOver=function(a){d3.select(this).attr("fill",b(a.baseColor,-.2))},c.prototype.__onMouseOut=function(a){d3.select(this).attr("fill",a.fill)},a.D3Funnel=c}(window); |
{ | ||
"name": "d3-funnel", | ||
"version": "0.6.3", | ||
"version": "0.6.4", | ||
"description": "A library for rendering SVG funnel charts using D3.js", | ||
@@ -5,0 +5,0 @@ "author": "Jake Zatecky", |
@@ -62,4 +62,6 @@ # D3 Funnel | ||
// the count values rather than equal heights | ||
animation: false, // The load animation speed in millseconds | ||
label: { | ||
fontSize: "14px" // Any valid font size | ||
fontSize: "14px", // Any valid font size, | ||
fill: "#000" // Hex color to change default #fff label color | ||
} | ||
@@ -81,2 +83,13 @@ }; | ||
Further more, you can even set colors for any data point label (hex only): | ||
``` javascript | ||
var data = [ | ||
["Teal", 12000, "#008080" "#080800"], | ||
["Byzantium", 4000, "#702963"], // Defaults to options.label.fill label color | ||
["Persimmon", 2500, "#ff634d" "#6f34fd"], | ||
["Azure", 1500, "#007fff" "#07fff0"] | ||
]; | ||
``` | ||
# License | ||
@@ -87,2 +100,2 @@ | ||
[d3]: http://d3js.org/ | ||
[examples]: https://cdn.rawgit.com/jakezatecky/d3-funnel/master/examples/index.html | ||
[examples]: http://jakezatecky.github.io/d3-funnel/ |
@@ -14,3 +14,4 @@ (function(global) { | ||
*/ | ||
function D3Funnel(selector) { | ||
var D3Funnel = function(selector) | ||
{ | ||
this.selector = selector; | ||
@@ -30,7 +31,9 @@ | ||
dynamicArea: false, | ||
animation: false, | ||
label: { | ||
fontSize: "14px" | ||
fontSize: "14px", | ||
fill: "#fff" | ||
} | ||
}; | ||
} | ||
}; | ||
@@ -44,3 +47,4 @@ /** | ||
*/ | ||
D3Funnel.prototype._isArray = function(value) { | ||
D3Funnel.prototype.__isArray = function(value) | ||
{ | ||
return Object.prototype.toString.call(value) === "[object Array]"; | ||
@@ -57,3 +61,4 @@ }; | ||
*/ | ||
D3Funnel.prototype._extend = function(a, b) { | ||
D3Funnel.prototype.__extend = function(a, b) | ||
{ | ||
var prop; | ||
@@ -98,8 +103,11 @@ for (prop in b) { | ||
* data counts. | ||
* @param {int} options.animation The load animation speed. If empty, | ||
* there will be no load animation. | ||
* @param {Object} options.label | ||
* @param {Object} options.label.fontSize | ||
*/ | ||
D3Funnel.prototype.draw = function(data, options) { | ||
D3Funnel.prototype.draw = function(data, options) | ||
{ | ||
// Initialize chart options | ||
this._initialize(data, options); | ||
this.__initialize(data, options); | ||
@@ -110,3 +118,3 @@ // Remove any previous drawings | ||
// Add the SVG and group element | ||
var svg = d3.select(this.selector) | ||
this.svg = d3.select(this.selector) | ||
.append("svg") | ||
@@ -116,10 +124,8 @@ .attr("width", this.width) | ||
.append("g"); | ||
var group = {}; | ||
var path = {}; | ||
var sectionPaths = this._makePaths(); | ||
this.sectionPaths = this.__makePaths(); | ||
// Define color gradients | ||
if (this.fillType === "gradient") { | ||
this._defineColorGradients(svg); | ||
this.__defineColorGradients(this.svg); | ||
} | ||
@@ -129,64 +135,67 @@ | ||
if (this.isCurved) { | ||
this._drawTopOval(svg, sectionPaths); | ||
this.__drawTopOval(this.svg, this.sectionPaths); | ||
} | ||
// Add each block section | ||
for (var i = 0; i < sectionPaths.length; i++) { | ||
// Set the background color | ||
var fill = this.fillType !== "gradient" ? | ||
this.data[i][2] : | ||
"url(#gradient-" + i + ")"; | ||
this.__drawSection(0); | ||
}; | ||
// Prepare data to assign to the section | ||
data = { | ||
index: i, | ||
label: this.data[i][0], | ||
value: this.data[i][1], | ||
baseColor: this.data[i][2], | ||
fill: fill | ||
}; | ||
/** | ||
* Draw the next section in the iteration. | ||
* | ||
* @param {int} index | ||
*/ | ||
D3Funnel.prototype.__drawSection = function(index) | ||
{ | ||
if (index === this.data.length) { | ||
return; | ||
} | ||
// Construct path string | ||
var paths = sectionPaths[i]; | ||
var pathStr = ""; | ||
// Create a group just for this block | ||
var group = this.svg.append("g"); | ||
// Iterate through each point | ||
for (var j = 0; j < paths.length; j++) { | ||
path = paths[j]; | ||
pathStr += path[2] + path[0] + "," + path[1] + " "; | ||
} | ||
// Fetch path element | ||
var path = this.__getSectionPath(group, index); | ||
path.data(this.__getSectionData(index)); | ||
// Create a group just for this block | ||
group = svg.append("g"); | ||
// Add animation components | ||
if (this.animation !== false) { | ||
var self = this; | ||
path.transition() | ||
.duration(this.animation) | ||
.ease("linear") | ||
.attr("fill", this.__getColor(index)) | ||
.attr("d", this.__getPathDefinition(index)) | ||
.each("end", function() { | ||
self.__drawSection(index + 1); | ||
}); | ||
} else { | ||
path.attr("fill", this.__getColor(index)) | ||
.attr("d", this.__getPathDefinition(index)); | ||
this.__drawSection(index + 1); | ||
} | ||
// Draw the sections's path and append the data | ||
path = group.append("path") | ||
.attr("fill", fill) | ||
.attr("d", pathStr) | ||
.data([data]); | ||
console.log(":("); | ||
// Add the hover events | ||
if (this.hoverEffects) { | ||
path.on("mouseover", this._onMouseOver) | ||
.on("mouseout", this._onMouseOut); | ||
} | ||
// Add the section label | ||
var textStr = this.data[i][0] + ": " + this.data[i][1].toLocaleString(); | ||
var textX = this.width / 2; // Center the text | ||
var textY = !this.isCurved ? // Average height of bases | ||
(paths[1][1] + paths[2][1]) / 2 : | ||
(paths[2][1] + paths[3][1]) / 2 + (this.curveHeight / this.data.length); | ||
// Add the hover events | ||
if (this.hoverEffects) { | ||
path.on("mouseover", this.__onMouseOver) | ||
.on("mouseout", this.__onMouseOut); | ||
} | ||
group.append("text") | ||
.text(textStr) | ||
.attr({ | ||
"x": textX, | ||
"y": textY, | ||
"text-anchor": "middle", | ||
"dominant-baseline": "middle", | ||
"fill": "#fff", | ||
"pointer-events": "none" | ||
}) | ||
.style("font-size", this.label.fontSize); | ||
this.__addSectionLabel(group, index); | ||
}; | ||
/** | ||
* Return the color for the given index. | ||
* | ||
* @param {int} index | ||
*/ | ||
D3Funnel.prototype.__getColor = function(index) | ||
{ | ||
if (this.fillType === "solid") { | ||
return this.data[index][2]; | ||
} else { | ||
return "url(#gradient-" + index + ")"; | ||
} | ||
@@ -196,2 +205,125 @@ }; | ||
/** | ||
* @param {Object} group | ||
* @param {int} index | ||
* | ||
* @return {Object} | ||
*/ | ||
D3Funnel.prototype.__getSectionPath = function(group, index) | ||
{ | ||
var path = group.append("path"); | ||
if (this.animation !== false) { | ||
this.__addBeforeTransition(path, index); | ||
} | ||
return path; | ||
}; | ||
/** | ||
* Set the attributes of a path element before its animation. | ||
* | ||
* @param {Object} path | ||
* @param {int} index | ||
*/ | ||
D3Funnel.prototype.__addBeforeTransition = function(path, index) | ||
{ | ||
var paths = this.sectionPaths[index]; | ||
var beforePath = ""; | ||
var beforeFill = ""; | ||
// Construct the top of the trapezoid and leave the other elements | ||
// hovering around to expand downward on animation | ||
if (!this.isCurved) { | ||
beforePath = "M" + paths[0][0] + "," + paths[0][1] + | ||
" L" + paths[1][0] + "," + paths[1][1] + | ||
" L" + paths[1][0] + "," + paths[1][1] + | ||
" L" + paths[0][0] + "," + paths[0][1]; | ||
} else { | ||
beforePath = "M" + paths[0][0] + "," + paths[0][1] + | ||
" Q" + paths[1][0] + "," + paths[1][1] + | ||
" " + paths[2][0] + "," + paths[2][1] + | ||
" L" + paths[2][0] + "," + paths[2][1] + | ||
" M" + paths[2][0] + "," + paths[2][1] + | ||
" Q" + paths[1][0] + "," + paths[1][1] + | ||
" " + paths[0][0] + "," + paths[0][1]; | ||
} | ||
// Use previous fill color, if available | ||
if (this.fillType === "solid") { | ||
beforeFill = index > 0 ? this.__getColor(index - 1) : this.__getColor(index); | ||
// Use current background if gradient (gradients do not transition) | ||
} else { | ||
beforeFill = this.__getColor(index); | ||
} | ||
path.attr("d", beforePath) | ||
.attr("fill", beforeFill); | ||
}; | ||
/** | ||
* @param {int} index | ||
* | ||
* @return {array} | ||
*/ | ||
D3Funnel.prototype.__getSectionData = function(index) | ||
{ | ||
return [{ | ||
index: index, | ||
label: this.data[index][0], | ||
value: this.data[index][1], | ||
baseColor: this.data[index][2], | ||
fill: this.__getColor(index) | ||
}]; | ||
}; | ||
/** | ||
* @param {int} index | ||
* | ||
* @return {string} | ||
*/ | ||
D3Funnel.prototype.__getPathDefinition = function(index) | ||
{ | ||
var pathStr = ""; | ||
var point = []; | ||
var paths = this.sectionPaths[index]; | ||
for (var j = 0; j < paths.length; j++) { | ||
point = paths[j]; | ||
pathStr += point[2] + point[0] + "," + point[1] + " "; | ||
} | ||
return pathStr; | ||
}; | ||
/** | ||
* @param {Object} group | ||
* @param {int} index | ||
*/ | ||
D3Funnel.prototype.__addSectionLabel = function(group, index) | ||
{ | ||
var i = index; | ||
var paths = this.sectionPaths[index]; | ||
var textStr = this.data[i][0] + ": " + this.data[i][1].toLocaleString(); | ||
var textFill = this.data[i][3] || this.label.fill; | ||
var textX = this.width / 2; // Center the text | ||
var textY = !this.isCurved ? // Average height of bases | ||
(paths[1][1] + paths[2][1]) / 2 : | ||
(paths[2][1] + paths[3][1]) / 2 + (this.curveHeight / this.data.length); | ||
group.append("text") | ||
.text(textStr) | ||
.attr({ | ||
"x": textX, | ||
"y": textY, | ||
"text-anchor": "middle", | ||
"dominant-baseline": "middle", | ||
"fill": textFill, | ||
"pointer-events": "none" | ||
}) | ||
.style("font-size", this.label.fontSize); | ||
}; | ||
/** | ||
* Initialize and calculate important variables for drawing the chart. | ||
@@ -202,5 +334,6 @@ * | ||
*/ | ||
D3Funnel.prototype._initialize = function(data, options) { | ||
if (!this._isArray(data) || data.length === 0 || | ||
!this._isArray(data[0]) || data[0].length < 2) { | ||
D3Funnel.prototype.__initialize = function(data, options) | ||
{ | ||
if (!this.__isArray(data) || data.length === 0 || | ||
!this.__isArray(data[0]) || data[0].length < 2) { | ||
throw { | ||
@@ -222,3 +355,3 @@ name: "D3 Funnel Data Error", | ||
// Set the default width and height based on the container | ||
var settings = this._extend({}, this.defaults); | ||
var settings = this.__extend({}, this.defaults); | ||
settings.width = parseInt(d3.select(this.selector).style("width"), 10); | ||
@@ -237,4 +370,8 @@ settings.height = parseInt(d3.select(this.selector).style("height"), 10); | ||
if ("label" in options) { | ||
if ("fontSize" in options.label) { | ||
settings.label.fontSize = options.label.fontSize; | ||
var validLabelOptions = /fontSize|fill/; | ||
var labelOption; | ||
for (labelOption in options.label) { | ||
if (labelOption.match(validLabelOptions)) { | ||
settings.label[labelOption] = options.label[labelOption]; | ||
} | ||
} | ||
@@ -259,4 +396,3 @@ } | ||
// If a color is not set for the record, add one | ||
if (typeof this.data[i][2] === "undefined" || | ||
!hexExpression.test(this.data[i][2])) { | ||
if (!("2" in this.data[i]) || !hexExpression.test(this.data[i][2])) { | ||
this.data[i][2] = colorScale(i); | ||
@@ -277,2 +413,3 @@ } | ||
this.dynamicArea = settings.dynamicArea; | ||
this.animation = settings.animation; | ||
@@ -300,3 +437,4 @@ // Calculate the bottom left x position | ||
*/ | ||
D3Funnel.prototype._makePaths = function() { | ||
D3Funnel.prototype.__makePaths = function() | ||
{ | ||
var paths = []; | ||
@@ -448,3 +586,4 @@ | ||
*/ | ||
D3Funnel.prototype._defineColorGradients = function(svg) { | ||
D3Funnel.prototype.__defineColorGradients = function(svg) | ||
{ | ||
var defs = svg.append("defs"); | ||
@@ -488,3 +627,4 @@ | ||
*/ | ||
D3Funnel.prototype._drawTopOval = function(svg, sectionPaths) { | ||
D3Funnel.prototype.__drawTopOval = function(svg, sectionPaths) | ||
{ | ||
var leftX = 0; | ||
@@ -517,3 +657,4 @@ var rightX = this.width; | ||
*/ | ||
D3Funnel.prototype._onMouseOver = function(data) { | ||
D3Funnel.prototype.__onMouseOver = function(data) | ||
{ | ||
d3.select(this).attr("fill", shadeColor(data.baseColor, -0.2)); | ||
@@ -525,3 +666,4 @@ }; | ||
*/ | ||
D3Funnel.prototype._onMouseOut = function(data) { | ||
D3Funnel.prototype.__onMouseOut = function(data) | ||
{ | ||
d3.select(this).attr("fill", data.fill); | ||
@@ -536,3 +678,4 @@ }; | ||
*/ | ||
function shadeColor(color, shade) { | ||
function shadeColor(color, shade) | ||
{ | ||
var f = parseInt(color.slice(1), 16); | ||
@@ -553,2 +696,2 @@ var t = shade < 0 ? 0 : 255; | ||
})(this); | ||
})(window); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
299454
2093
99