Skip to content

Instantly share code, notes, and snippets.

@plmrry
Last active August 29, 2015 14:24
Show Gist options
  • Save plmrry/947faec6b7e089d7f65b to your computer and use it in GitHub Desktop.
Save plmrry/947faec6b7e089d7f65b to your computer and use it in GitHub Desktop.
Stacked Attribute Transitions

This is a technique for storing attribute states in a stack.

When your mouse exits a node, it returns to the most recent state that was pushed into an array of states. This allows for state transitions.

An alternative method could use getComputedStyle to pre-compute styles from a stylesheet (which would then be used in a transition).

<!DOCTYPE html>
<meta charset="utf-8">
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script type="text/coffeescript">
launch = ->
[width, height] = [960, 500]
svg = d3.select("body").append("svg")
.attr({ width: width, height: height })
data = d3.range(11).map(Object)
x = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangePoints([0, width], 1)
circles = svg.selectAll("circle").data(data)
.enter().append("circle")
.attr({ r: 30, cy: height / 2, cx: (_, i) -> x(i) })
.style({ "fill-opacity": 0.5 })
.call(repeating_color_sweep("blue"))
.on("mouseenter", ->
d3.select(this)
.each(highlight("style", "fill", "green"))
.classed("selected", true)
.call(transition_fill)
)
.on("mouseleave", ->
d3.select(this)
.classed("selected", false)
.each(highlight("style", "fill", null))
.call(transition_fill)
)
repeating_color_sweep = (color) ->
(selection) ->
d3.transition("sweepAll").duration(1000)
.each(->
selection.each(highlight("style", "fill", color))
.call(transition_fill);
)
.each("end", ->
next_color = if color is "blue" then undefined else "blue"
selection.call(repeating_color_sweep(next_color))
);
transition_fill = (selection) ->
selection.transition().duration(100)
.delay((_, i) -> i * 50)
.style({ fill: (d) -> d["style"]["fill"].value });
highlight = (type, name, new_value) ->
(d) ->
thiz = d3.select(this)
d[type] ?= {}
d[type][name] ?= {}
thiz[type](name, (d) -> d[type][name].value)
current_value = thiz[type](name)
selected = thiz.classed("selected")
d[type][name].value ?= current_value
previous = d[type][name].previous ?= []
previous.pop() if (! new_value and selected)
previous.push(current_value) if (new_value and ! selected)
previous.push(new_value) if (new_value)
previous.push(current_value) if (selected)
d[type][name].value = previous.pop()
launch()
</script>
<script src="//coffeescript.org/extras/coffee-script.js"></script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment