Skip to content

Commit

Permalink
import code from electrify and edit to work with react
Browse files Browse the repository at this point in the history
  • Loading branch information
georgeweiler committed Nov 8, 2016
1 parent 9353002 commit 7f503e0
Show file tree
Hide file tree
Showing 7 changed files with 708 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import d3 from 'd3';

export const arc = d3.svg.arc()
.startAngle(d => angle(d.x))
.endAngle(d => angle(d.x + Math.max(d.dx - 0.025, 0.0125)))
.innerRadius(d => Math.sqrt(d.y))
.outerRadius(d => Math.sqrt(d.y + d.dy * 0.65))

export const initArc = d3.svg.arc()
.startAngle(d => angle(d.x))
.endAngle(d => angle(d.x + d.dx))
.innerRadius(d => Math.sqrt(d.y))
.outerRadius(d => Math.sqrt(d.y))

export const angle = (x) => x

// Modified version of d3's built-in bounce easing method:
// https://github.com/mbostock/d3/blob/51228ccc4b54789f2d92d268e94716d1c016c774/src/interpolate/ease.js#L105-110
export const bounceHigh = (t) => t < 1 / 2.75 ? 7.5625 * t * t
: t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .65
: t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .85
: 7.5625 * (t -= 2.625 / 2.75) * t + .975;

export const arcTween = (a) => {
const i = d3.interpolate({x: a.x0, dx: a.dx0}, a)
return (t) => {
const b = i(t)
a.x0 = b.x
a.dx0 = b.dx
return arc(b)
}
}

//
// A more complex arc tween for handling
// hover states. Returns a tween function
// which returns an interpolator for each
// datum.
//
export const hoverTween = (z) => {
let ht = 0
const harc = d3.svg.arc()
.startAngle(d => angle(d.x))
.endAngle(d => angle(d.x
+ (1 - ht) * Math.max(d.dx - 0.025, 0.0125)
+ ht * (d.dx + 0.00005)
)
)
.innerRadius(d => Math.sqrt(d.y))
.outerRadius(d => Math.sqrt(d.y + d.dy * (ht * 0.35 + 0.65)) + ht)

return (a) => {
a.t0 = a.t3 = a.t0 || 0
a.t1 = z
a.t2 = a.t1 - a.t0
return (_t) => {
ht = a.t2 * _t + a.t3
a.t0 = ht
return harc(a)
}
}
}

//
// Makes it possible to rotate
// angles greater than 180 degrees :)
//
export const rotateTween = (deg) => {
return (d) => {
return (t) => 'rotate(' + (1-t) * deg + ')'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
import d3 from 'd3';
import pretty from 'prettysize';
import schemes from './schemes';
import { arc, initArc, bounceHigh, arcTween, hoverTween, rotateTween } from './d3-utils';
import createModes, { highlightMode } from './mode';

const modeInitial = 'size'
const modeFns = {
count: () => 1
, size: (d) => d.size
}

export default function createD3Visualization(d3Data){
const domElements = d3Data.refs
, root = d3Data.root
, width = window.innerWidth * 0.8
, height = Math.max(window.innerHeight - 100, 100) * 0.8
, radius = Math.min(width, height) * 0.45
, deg = 120

const svg = d3.select(domElements.svg).append('div')
.classed('svgContainer', true)
.append('svg')
.attr("preserveAspectRatio", "xMinYMin meet") //responsive SVG needs these 2 attr and no width or hight attr
.attr("viewBox", "0 0 660 380")
.style('overflow', 'visible')
.append('g')
.attr('transform', `translate(${width / 3.6},${height * .3})`)

const partition = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, radius * radius])
.value(modeFns[modeInitial])

//
// Creates the title text in
// the center of the rings.
//
const title = svg.append('text')
.text(root.name)
.attr('x', 0)
.attr('y', -5)
.style('font-size', '18px')
.style('fill', 'white')
.style('font-weight', 500)
.style('alignment-baseline', 'middle')
.style('text-anchor', 'middle')

//
// Likewise, this is the file
// percentage size stat below the title
//
const percentageSize = svg.append('text')
.text(pretty(root.value || root.size))
.attr('x', 0)
.attr('y', 20)
.style('fill', 'white')
.style('font-size', '16px')
.style('font-weight', 300)
.style('alignment-baseline', 'middle')
.style('text-anchor', 'middle')

//
// Likewise, this is the file
// size stat below the title
//
const size = svg.append('text')
.text('(' + pretty(root.value || root.size) + ')')
.attr('x', 0)
.attr('y', 40)
.style('fill', 'white')
.style('font-size', '16px')
.style('alignment-baseline', 'middle')
.style('text-anchor', 'middle')

//
// Each arc is wrapped in a group element,
// to apply rotation transforms while
// changing size and shape.
//
const groups = svg.datum(root).selectAll('g')
.data(partition.nodes)
.enter()
.append('g')
.attr('transform', `rotate(${deg})`)

const maxdepth = groups[0].reduce((max, el) => Math.max(max, el.__data__.depth), 0)

//
// Actually create the arcs for each
// file.
//
const path = groups.append('path')
.attr('d', initArc)
.attr('display', d => d.depth ? null : 'none')
.style('stroke', '#2B2B2B')
.style('stroke-width', '0')
.style('fill-rule', 'evenodd')
.each(function(d) {
d.x0 = d.x
d.dx0 = d.dx
d.el = this
})

let found = [];
const _select = (node, selector) => {
node.enabled = selector(node);
if (node.enabled) {
found.push(node);
}
if (node.children) {
for (let c of node.children) {
_select(c, selector);
}
}
}
_select(root, () => true);

d3.select(domElements.search).on('keyup', function() {
const text = this.value.replace(/^\s+/, "").replace(/\s+$/, "")
if (text.length > 0) {
found = [];
const re = new RegExp(text, 'i');
_select(root, (node) => node.name.match(re) !== null);
if (found.length === 1) {
title.text(found[0].name)
size.text(pretty(found[0].value || found[0].size))
} else {
title.text("Multiple found")
let completeSize = 0
for (let n of found) {
completeSize += n.size;
}
size.text(`${pretty(completeSize)} total`)
}
} else {
_select(root, () => true);
}
groups
.select('path')
.transition()
.duration(200)
.style('opacity', d => {
console.log(d.name, d.enabled);
return d.enabled ? 1.0 : 0.2
})
})

//
// Colour scheme functionality.
//
// Triggered immediately with the default
// scheme, must be passed a d3 selection.
//
let background
, scheme = 0
, specials
, color

useScheme(scheme)
function useScheme(n) {
specials = schemes[n].specials

const colors = schemes[n].main
Object.keys(specials).forEach((key) => {
const idx = colors.indexOf(specials[key].toLowerCase())
if (idx === -1) return
colors.splice(idx, 1)
})

color = d3.scale
.ordinal()
.range(colors)

let _path = path.transition()
.duration(600)
.ease(bounceHigh, 1000)
.delay(d => d.x * 100 + d.y / maxdepth * 0.06125);

_path.style('fill', (d) => {
const name = d.children ? d.name : d.parent.name
d.c = schemes[n].modifier.call(d
, specials[name] || color(name)
, root
)
return d.c
})
}

let ptrans = 0
path.transition()
.duration(1000)
.each(() => ptrans++)
.ease('elastic', 2, 1)
.delay((d, i) => d.x * 100 + (i % 4) * 250 + d.y / maxdepth * 0.25)
.attr('d', arc)
.each('interrupt', () => {
d3.select(domElements.search).transition().duration(200).style('opacity', 1)
})
.each('end', () => {
ptrans--;
})

//
// Rotates the newly created
// arcs back towards their original
// position.
//
let gtrans = 0
groups.transition()
.duration(3250)
.each(() => gtrans++)
.delay((d, i) => d.x * 100 + (i % 4) * 250 + d.y / maxdepth * 0.25 + 250)
.attrTween('transform', rotateTween(deg))
.each('end', () => {
gtrans--;
if (ptrans === 0 && gtrans === 0) {
d3.select(domElements.search).transition().duration(200).style('opacity', 1)
}
})

groups.on('mouseover', (d) => {
highlight(d)
title.text(d.name)

let sizeInPercentage = (d.value/root.value*100).toFixed(2);
percentageSize.text(sizeInPercentage + " %")

size.text('(' + pretty(d.value || d.size) + ')')
}).on('mouseout', (d) => {
unhighlight(d)
title.text(root.name)
size.text(pretty(root.value || root.size))
})

highlight.tween = hoverTween(1)
function highlight(d) {
if (d.el) d3.select(d.el)
.transition()
.delay(d => (d.depth - 1) * 300 / maxdepth)
.ease('back-out', 10)
.duration(500)
.attrTween('d', highlight.tween)
.style('fill', d => d.c)

if (d.children) {
let i = d.children.length
while (i--) highlight(d.children[i])
}
}

unhighlight.tween = hoverTween(0)
function unhighlight(d) {
if (d.el) d3.select(d.el)
.transition()
.delay(d => (d.depth - 1) * 300 / maxdepth)
.ease('back-out', 4)
.duration(500)
.attrTween('d', unhighlight.tween)
.style('fill', d => d.c)

if (d.children) {
let i = d.children.length
while (i--) unhighlight(d.children[i])
}
}

createModes(updateMode, domElements);

updateMode(modeInitial, false)

function updateMode(mode, update) {
highlightMode(mode);
if (!update) return
groups
.data(partition.value(modeFns[mode]).nodes)
.select('path')
.transition()
.duration(1500)
.attrTween('d', arcTween)
}
}
Loading

0 comments on commit 7f503e0

Please sign in to comment.