Last active
August 22, 2016 02:21
-
-
Save 1wheel/52da010eeef9cc5aca2e to your computer and use it in GitHub Desktop.
expandable-rectanges
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
function jetpack(d3) { | |
d3.selection.prototype.translate = function(xy) { | |
return this.attr('transform', function(d,i) { | |
return 'translate('+[typeof xy == 'function' ? xy(d,i) : xy]+')'; | |
}); | |
}; | |
d3.transition.prototype.translate = function(xy) { | |
return this.attr('transform', function(d,i) { | |
return 'translate('+[typeof xy == 'function' ? xy(d,i) : xy]+')'; | |
}); | |
}; | |
d3.selection.prototype.tspans = function(lines, lh) { | |
return this.selectAll('tspan') | |
.data(lines) | |
.enter() | |
.append('tspan') | |
.text(function(d) { return d; }) | |
.attr('x', 0) | |
.attr('dy', function(d,i) { return i ? lh || 15 : 0; }); | |
}; | |
d3.selection.prototype.append = | |
d3.selection.enter.prototype.append = function(name) { | |
var n = d3_parse_attributes(name), s; | |
//console.log(name, n); | |
name = n.attr ? n.tag : name; | |
name = d3_selection_creator(name); | |
s = this.select(function() { | |
return this.appendChild(name.apply(this, arguments)); | |
}); | |
return n.attr ? s.attr(n.attr) : s; | |
}; | |
d3.selection.prototype.insert = | |
d3.selection.enter.prototype.insert = function(name, before) { | |
var n = d3_parse_attributes(name), s; | |
name = n.attr ? n.tag : name; | |
name = d3_selection_creator(name); | |
before = d3_selection_selector(before); | |
s = this.select(function() { | |
return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); | |
}); | |
return n.attr ? s.attr(n.attr) : s; | |
}; | |
var d3_parse_attributes_regex = /([\.#])/g; | |
function d3_parse_attributes(name) { | |
if (typeof name === "string") { | |
var attr = {}, | |
parts = name.split(d3_parse_attributes_regex), p; | |
name = parts.shift(); | |
while ((p = parts.shift())) { | |
if (p == '.') attr['class'] = attr['class'] ? attr['class'] + ' ' + parts.shift() : parts.shift(); | |
else if (p == '#') attr.id = parts.shift(); | |
} | |
return attr.id || attr['class'] ? { tag: name, attr: attr } : name; | |
} | |
return name; | |
} | |
function d3_selection_creator(name) { | |
return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? function() { | |
return this.ownerDocument.createElementNS(name.space, name.local); | |
} : function() { | |
return this.ownerDocument.createElementNS(this.namespaceURI, name); | |
}; | |
} | |
function d3_selection_selector(selector) { | |
return typeof selector === "function" ? selector : function() { | |
return this.querySelector(selector); | |
}; | |
} | |
d3.wordwrap = function(line, maxCharactersPerLine) { | |
var w = line.split(' '), | |
lines = [], | |
words = [], | |
maxChars = maxCharactersPerLine || 40, | |
l = 0; | |
w.forEach(function(d) { | |
if (l+d.length > maxChars) { | |
lines.push(words.join(' ')); | |
words.length = 0; | |
l = 0; | |
} | |
l += d.length; | |
words.push(d); | |
}); | |
if (words.length) { | |
lines.push(words.join(' ')); | |
} | |
return lines; | |
}; | |
d3.ascendingKey = function(key) { | |
return typeof key == 'function' ? function (a, b) { | |
return key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : key(a) >= key(b) ? 0 : NaN; | |
} : function (a, b) { | |
return a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : a[key] >= b[key] ? 0 : NaN; | |
}; | |
}; | |
d3.descendingKey = function(key) { | |
return typeof key == 'function' ? function (a, b) { | |
return key(b) < key(a) ? -1 : key(b) > key(a) ? 1 : key(b) >= key(a) ? 0 : NaN; | |
} : function (a, b) { | |
return b[key] < a[key] ? -1 : b[key] > a[key] ? 1 : b[key] >= a[key] ? 0 : NaN; | |
}; | |
}; | |
d3.f = function(){ | |
var functions = arguments; | |
//convert all string arguments into field accessors | |
var i = 0, l = functions.length; | |
while (i < l) { | |
if (typeof(functions[i]) === 'string' || typeof(functions[i]) === 'number'){ | |
functions[i] = (function(str){ return function(d){ return d[str] }; })(functions[i]) | |
} | |
i++; | |
} | |
//return composition of functions | |
return function(d) { | |
var i=0, l = functions.length; | |
while (i++ < l) d = functions[i-1].call(this, d); | |
return d; | |
}; | |
}; | |
// store d3.f as convenient unicode character function (alt-f on macs) | |
if (!window.hasOwnProperty('ƒ')) window.ƒ = d3.f; | |
} | |
if (typeof d3 === 'object' && d3.version) jetpack(d3); | |
else if (typeof define === 'function' && define.amd) { | |
define(['d3'], jetpack); | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.conventions = function(c){ | |
c = c || {} | |
c.width = c.width || 900 | |
c.height = c.height || 500 | |
c.margin = c.margin || {top: 20, right: 20, bottom: 20, left: 25} | |
c.parentSel = c.parentSel || d3.select('body') | |
c.svg = c.svg || c.parentSel.append("svg") | |
.attr("width", c.width + c.margin.left + c.margin.right) | |
.attr("height", c.height + c.margin.top + c.margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + c.margin.left + "," + c.margin.top + ")") | |
c.color = c.color || d3.scale.category10() | |
c.x = c.x || d3.scale.linear().range([0, c.width]) | |
c.y = c.y || d3.scale.linear().range([c.height, 0]) | |
c.rScale = c.rScale || d3.scale.sqrt().range([5, 20]) | |
c.line = c.line || d3.svg.line() | |
c.xAxis = c.xAxis || d3.svg.axis().scale(c.x).orient("bottom"); | |
c.yAxis = c.yAxis || d3.svg.axis().scale(c.y).orient("left") | |
c.drawAxis = function(){ | |
c.svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + c.height + ")") | |
.call(c.xAxis); | |
c.svg.append("g") | |
.attr("class", "y axis") | |
.call(c.yAxis); | |
} | |
return c | |
} | |
d3.attachTooltip = function(sel, fieldFns){ | |
sel | |
.on('mouseover.attachTooltip', ttDisplay) | |
.on('mousemove.attachTooltip', ttMove) | |
.on('mouseout.attachTooltip', ttHide) | |
var d = sel.datum() | |
fieldFns = fieldFns || d3.keys(d) | |
.filter(function(str){ | |
return (typeof d[str] != 'object') && (d[str] != 'array') | |
}) | |
.map(function(str){ | |
return function(d){ return str + ': <b>' + d[str] + '</b>'} }) | |
function ttDisplay(d){ | |
d3.select('.tooltip') | |
.classed('tooltip-hidden', false) | |
.html('') | |
.dataAppend(fieldFns, 'div') | |
.html(function(fn){ return fn(d) }) | |
d3.select(this).classed('tooltipped', true) | |
} | |
function ttMove(d){ | |
var tt = d3.select('.tooltip') | |
if (!tt.size()) return | |
var e = d3.event, | |
x = e.clientX, | |
y = e.clientY, | |
doctop = (window.scrollY)? window.scrollY : (document.documentElement && document.documentElement.scrollTop)? document.documentElement.scrollTop : document.body.scrollTop; | |
n = tt.node(), | |
nBB = n.getBoundingClientRect() | |
tt.style('top', (y+doctop-nBB.height-18)+"px"); | |
tt.style('left', Math.min(Math.max(0, (x-nBB.width/2)), window.innerWidth - nBB.width)+"px"); | |
} | |
function ttHide(d){ | |
d3.select('.tooltip').classed('tooltip-hidden', true); | |
d3.selectAll('.tooltipped').classed('tooltipped', false) | |
} | |
} | |
d3.selection.prototype.dataAppend = function(data, name){ | |
return this.selectAll(name) | |
.data(data).enter() | |
.append(name) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
text { | |
font: 12px sans-serif; | |
pointer-events: none; | |
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff; | |
} | |
.axis line, | |
.axis path { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
div.tooltip { | |
top: -1000px; | |
position: absolute; | |
padding: 10px; | |
background: rgba(255, 255, 255, .90); | |
border: 1px solid lightgray; | |
pointer-events: none; | |
} | |
.tooltip-hidden{ | |
opacity: 0; | |
} | |
rect.category{ | |
stroke: white; | |
} | |
rect.subcategory{ | |
stroke: steelblue; | |
} | |
path.dash{ | |
stroke: black; | |
stroke-dasharray: 10 10 5 5; | |
} | |
</style> | |
<body> | |
<div id='graph'></div> | |
<div class='tooltip'></div> | |
</body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script> | |
<script src='d3-jetpack.js'></script> | |
<script src='d3-starterkit.js'></script> | |
<script src='script.js'></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// d3.tsv('data.tsv', function(data){ | |
// c = d3.conventions({parentSel: d3.select('#graph')}) | |
// c.x.domain(d3.extent(data, ƒ('sepalWidth')) ).nice() | |
// c.y.domain(d3.extent(data, ƒ('sepalLength'))).nice() | |
// c.drawAxis() | |
// c.svg.dataAppend(data, 'circle') | |
// .attr('cx', ƒ('sepalWidth', c.x)) | |
// .attr('cy', ƒ('sepalLength',c.y)) | |
// .attr('fill', ƒ('species', c.color)) | |
// .attr({r: 5, stroke: '#000'}) | |
// .call(d3.attachTooltip) | |
// var legend = c.svg.dataAppend(c.color.domain(), 'g.legend') | |
// .translate(function(d, i){ return [0, i*20] }) | |
// legend.append('rect') | |
// .attr({x: c.width - 18, width: 18, height: 18}) | |
// .style('fill', c.color) | |
// legend.append('text') | |
// .attr({x: c.width - 24, y: 9, dy: '.33em', 'text-anchor': 'end'}) | |
// .text(ƒ()) | |
// }) | |
var height = 600, | |
width = 300 | |
var categories = generateCatories(100) | |
categories.forEach(function(d){ | |
d.subcategories = generateCatories(d.value) | |
}) | |
var y = categories.y | |
var svg = d3.select('body') | |
.append('svg') | |
.attr({width: width, height: height}) | |
svg.dataAppend(categories, 'rect.category') | |
.attr('height', ƒ('value', y)) | |
.attr('y', ƒ('prevValue', y)) | |
.attr('width', 100) | |
.on('click', expand) | |
var topPath = svg.append('path.dash') | |
var botPath = svg.append('path.dash') | |
var sideBoxes = svg.append('g') | |
function expand(d){ | |
topPath | |
.attr('d', ['M', [100, y(d.prevValue)], 'L', [100, y(d.prevValue)]].join('')) | |
.transition().duration(700).ease('linear') | |
.attr('d', ['M', [100, y(d.prevValue)], 'L', [200, 0]].join('')) | |
botPath | |
.attr('d', ['M', [100, y(d.prevValue + d.value)], 'L', [100, y(d.prevValue + d.value)]].join('')) | |
.transition().duration(700).ease('linear') | |
.attr('d', ['M', [100, y(d.prevValue + d.value)], 'L', [200, height]].join('')) | |
//TODO - use a transition before removing | |
sideBoxes.selectAll('*').remove() | |
sideBoxes.dataAppend(d.subcategories, 'rect.subcategory') | |
.attr('width', 100) | |
.attr('x', 000) | |
.attr('y', function(e){ return y(d.prevValue + e.prevValue) }) | |
.attr('height', ƒ('value', y)) | |
.transition().duration(700).ease('linear') | |
.attr('x', 200) | |
.attr('width', 100) | |
.attr('y', ƒ('prevValue', d.subcategories.y)) | |
.attr('height', ƒ('value', d.subcategories.y)) | |
} | |
function generateCatories(normalize){ | |
var categories = d3.range(10).map(function(d){ | |
return { | |
name: 'cat ' + d, | |
value: Math.random() | |
} | |
}) | |
var totalValue = d3.sum(categories, ƒ('value')) | |
categories.forEach(function(d){ | |
d.value = d.value*normalize/totalValue | |
}) | |
categories = _.sortBy(categories, 'value') | |
var curValue = 0 | |
categories.forEach(function(d){ | |
d.prevValue = curValue | |
curValue += d.value | |
}) | |
var y = d3.scale.linear() | |
.domain([0, normalize]) | |
.range([0, height]) | |
categories.y = y | |
return categories | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment