Skip to content

Instantly share code, notes, and snippets.

@pbogden
Last active November 9, 2017 13:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pbogden/c57e358281b69132c2f07ec955ab4db0 to your computer and use it in GitHub Desktop.
Save pbogden/c57e358281b69132c2f07ec955ab4db0 to your computer and use it in GitHub Desktop.
Toggling buttons
d3.button = function() {
var dispatch = d3.dispatch('press', 'release');
var padding = 10,
radius = 10,
stdDeviation = 5,
offsetX = 2,
offsetY = 4;
function my(selection) {
selection.each(function(d, i) {
var g = d3.select(this)
.attr('id', 'd3-button' + i)
.attr('transform', 'translate(' + d.x + ',' + d.y + ')');
var text = g.append('text').text(d.label);
var defs = g.append('defs');
var bbox = text.node().getBBox();
var rect = g.insert('rect', 'text')
.attr("x", bbox.x - padding)
.attr("y", bbox.y - padding)
.attr("width", bbox.width + 2 * padding)
.attr("height", bbox.height + 2 * padding)
.attr('rx', radius)
.attr('ry', radius)
.on('mouseover', activate)
.on('mouseout', deactivate)
.on('click', toggle)
addShadow.call(g.node(), d, i);
addGradient.call(g.node(), d, i);
});
}
function addGradient(d, i) {
var defs = d3.select(this).select('defs');
var gradient = defs.append('linearGradient')
.attr('id', 'gradient' + i)
.attr('x1', '0%')
.attr('y1', '0%')
.attr('x2', '0%')
.attr('y2', '100%');
gradient.append('stop')
.attr('id', 'gradient-start')
.attr('offset', '0%')
gradient.append('stop')
.attr('id', 'gradient-stop')
.attr('offset', '100%')
d3.select(this).select('rect').attr('fill', 'url(#gradient' + i + ")" );
}
function addShadow(d, i) {
var defs = d3.select(this).select('defs');
var rect = d3.select(this).select('rect').attr('filter', 'url(#dropShadow' + i + ")" );
var shadow = defs.append('filter')
.attr('id', 'dropShadow' + i)
.attr('x', rect.attr('x'))
.attr('y', rect.attr('y'))
.attr('width', rect.attr('width') + offsetX)
.attr('height', rect.attr('height') + offsetY)
shadow.append('feGaussianBlur')
.attr('in', 'SourceAlpha')
.attr('stdDeviation', 2)
shadow.append('feOffset')
.attr('dx', offsetX)
.attr('dy', offsetY);
var merge = shadow.append('feMerge');
merge.append('feMergeNode');
merge.append('feMergeNode').attr('in', 'SourceGraphic');
}
function activate() {
var gradient = d3.select(this.parentNode).select('linearGradient')
d3.select(this.parentNode).select("rect").classed('active', true)
if (!gradient.node()) return;
gradient.select('#gradient-start').classed('active', true)
gradient.select('#gradient-stop').classed('active', true)
}
function deactivate() {
var gradient = d3.select(this.parentNode).select('linearGradient')
d3.select(this.parentNode).select("rect").classed('active', false)
if (!gradient.node()) return;
gradient.select('#gradient-start').classed('active', false);
gradient.select('#gradient-stop').classed('active', false);
}
function toggle(d, i) {
if (d3.select(this).classed('pressed')) {
release.call(this, d, i);
deactivate.call(this, d, i);
} else {
press.call(this, d, i);
activate.call(this, d, i);
}
}
function press(d, i) {
dispatch.call('press', this, d, i)
d3.select(this).classed('pressed', true);
var shadow = d3.select(this.parentNode).select('filter')
if (!shadow.node()) return;
shadow.select('feOffset').attr('dx', 0).attr('dy', 0);
shadow.select('feGaussianBlur').attr('stdDeviation', 0);
}
function release(d, i) {
dispatch.call('release', this, d, i)
my.clear.call(this, d, i);
}
my.clear = function(d, i) {
d3.select(this).classed('pressed', false);
var shadow = d3.select(this.parentNode).select('filter')
if (!shadow.node()) return;
shadow.select('feOffset').attr('dx', offsetX).attr('dy', offsetY);
shadow.select('feGaussianBlur').attr('stdDeviation', stdDeviation);
}
my.on = function() {
var value = dispatch.on.apply(dispatch, arguments);
return value === dispatch ? my : value;
};
return my;
}
<!DOCTYPE html>
<meta charset="utf-8">
<title>toggling buttons</title>
<body>
<style>
body {
font-family: Univers, Arial, sans-serif;
}
.button rect {
stroke: #999faa; /* navy 40% */
stroke-width: 2px;
}
.button rect.pressed {
fill: #000f2b; /* navy 100% */
}
.button #gradient-start {
stop-color: #999faa; /* navy 40% */
stop-opacity: 1;
}
.button #gradient-stop {
stop-color: #4d576b; /* navy 70% */
stop-opacity: 1;
}
.button #gradient-start.active, .button #gradient-start.pressed {
stop-color: #4d576b; /* navy 70% */
}
.button #gradient-stop.active, .button #gradient-stop.pressed {
stop-color: #000f2b; /* navy 100% */
}
.button text {
font-size: 30px;
fill: #eee;
pointer-events: none;
text-anchor: middle;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="d3.button.js"></script>
<script>
var width = 960, height = 500;
var data = [{label: "Click me", x: width / 4, y: height / 4 },
{label: "Click me too", x: width / 2, y: height / 2 }];
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
var button = d3.button()
.on('press', function(d, i) { clearAll(); })
.on('release', function(d, i) { console.log("Released", d, i, this.parentNode)});
// Add buttons
var buttons = svg.selectAll('.button')
.data(data)
.enter()
.append('g')
.attr('class', 'button')
.call(button);
function clearAll() {
buttons.selectAll('rect')
.each(function(d, i) { button.clear.call(this, d, i) });
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment