Skip to content

Instantly share code, notes, and snippets.

@tlfrd
Last active February 10, 2018 10:20
Show Gist options
  • Save tlfrd/af94702a0b13ff7d0a491d5cbc1338f2 to your computer and use it in GitHub Desktop.
Save tlfrd/af94702a0b13ff7d0a491d5cbc1338f2 to your computer and use it in GitHub Desktop.
Gooey Effect
license: mit

Experimenting with the gooey effect - based on the work of Nadieh Bremer.

This is an attempt at an icon grid to circle transition.

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
.big-circle {
fill: none;
}
text {
fill: white;
font-family: sans-serif;
}
</style>
</head>
<body>
<script>
var margin = {top: 50, right: 50, bottom: 50, left: 50}
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + [margin.left, margin.top] + ")")
var config = {
radius: 5,
gridLength: 10,
gridPadding: 10
};
var defs = svg.append('defs');
var filter = defs.append('filter').attr('id','gooey');
filter.append('feGaussianBlur')
.attr('in','SourceGraphic')
.attr('stdDeviation', config.radius * 1.8)
.attr('result','blur');
filter.append('feColorMatrix')
.attr("class", "blurValues")
.attr('in','blur')
.attr('mode','matrix')
.attr('values', '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 ' + config.radius +' -6')
.attr('result','gooey');
filter.append("feBlend")
.attr("in", "SourceGraphic")
.attr("in2", "gooey")
.attr("operator", "atop");
var data = d3.range(60).map(function(i) {
return {
r: config.radius,
colour: "purple"
}
});
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(width / 2))
.force("y", d3.forceY(height / 2))
.force("collide", d3.forceCollide(config.radius + 1.5).iterations(2))
.stop();
var circleGroup = svg.append("g")
.style("filter", "url(#gooey)");
var smallCircles = circleGroup.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "small-circle")
.attr("r", config.radius)
.attr("cx", (d, i) => d.x = (i % config.gridLength) * (config.gridPadding + config.radius * 2))
.attr("cy", (d, i) => d.y = Math.floor(i / config.gridLength) * (config.gridPadding + config.radius * 2))
.attr("fill", d => d.colour);
var bigCircle = circleGroup.append("g")
.append("circle")
.attr("class", "big-circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("r", 0)
.style("fill", "purple");
function clusterDots() {
// Interpolate between gooey filter and no gooey filter
transitionGoo(3000);
for (var i = 0; i < 120; ++i) simulation.tick();
d3.selectAll(".small-circle")
.transition()
.duration(1500)
.delay((d,i) => calculateDistance(d, [width/2, height/2]) * 30)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
d3.select(".big-circle")
.transition()
.delay(700)
.duration(2500)
.attr("r", Math.sqrt(data.length) * 1.5 * config.radius);
}
function separateDots() {
transitionGooBack(2000);
d3.select(".big-circle")
.transition()
.duration(2100)
.attr("r", 0);
d3.selectAll(".small-circle")
.transition()
.duration(1500)
.delay((d,i) => 1500 + (config.radius - calculateDistance(d, [width/2, height/2])) * 30)
.attr("cx", (d, i) => (i % config.gridLength) * (config.gridPadding + config.radius * 2))
.attr("cy", (d, i) => Math.floor(i / config.gridLength) * (config.gridPadding + config.radius * 2));
}
function loop() {
setTimeout(clusterDots, 1000);
setTimeout(separateDots, data.length * 150);
}
loop();
setInterval(loop, data.length * 200);
function transitionGoo(duration) {
d3.selectAll(".blurValues")
.transition().duration(duration)
.attrTween("values", function() {
return d3.interpolateString("1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 6 -6",
"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 " + config.radius * 8 + " -6");
});
}
function transitionGooBack(duration) {
d3.selectAll(".blurValues")
.transition().duration(duration)
.attrTween("values", function() {
return d3.interpolateString("1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 " + config.radius * 8 + " -6", "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 6 -6");
});
}
function calculateDistance(d, point) {
return Math.sqrt(Math.pow(point[0] - d.x, 2) + Math.pow(point[1] - d.y, 2))
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment