Skip to content

Instantly share code, notes, and snippets.

@IsaKiko
Last active August 27, 2017 08:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IsaKiko/bd4de582287695f96834dd92315e23f5 to your computer and use it in GitHub Desktop.
Save IsaKiko/bd4de582287695f96834dd92315e23f5 to your computer and use it in GitHub Desktop.
SVG inline only Gooey Effect
license: mit
border:
scrolling:
height: NaN
<!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; }
</style>
</head>
<body>
<div style='width:200px; margin:auto;'>
<button style="font-size:1rem; color:white; font-weight:bold; padding:10px; border:0px; background-color:lightblue" id='button'>Click me!</button>
</div>
<script>
pairs = [
{"x1":[50,55],"y1":[20,30],"x2":[70,40],"y2":[20,40],"key":'one'},
{"x1":[50,45],"y1":[20,30],"x2":[80,45],"y2":[30,10],"key":'two'},
{"x1":[50,30],"y1":[20,30],"x2":[60,30],"y2":[40,35],"key":'three'}
]
d3.functor = function functor(v) {
return typeof v === "function" ? v : function() {
return v;
};
};
var svg = d3.select("body").append("svg")
.attr('id','svg')
.attr("width", "100%")
.attr("height", "800")
.attr("viewBox", "0 0 100 100");
// goop generation for pairs of circles
function goopTransform() {
//set defaults
var r1 = function(d) { return d.r1; },
r2 = function(d) { return d.r2; },
c1x = function(d) { return d.c1x; },
c2x = function(d) { return d.c2x; },
c1y = function(d) { return d.c1y; },
c2y = function(d) { return d.c2y; };
//returned function to generate goop path
function gooptrans(d) {
var r1 = d3.functor(d.c1.r).call(this, d),
c1x = d3.functor(d.c1.x).call(this, d),
c2x = d3.functor(d.c2.x).call(this, d),
c1y = d3.functor(d.c1.y).call(this, d),
c2y = d3.functor(d.c2.y).call(this, d),
r2 = d3.functor(d.c2.r).call(this, d);
var pi = 3.14159
var angle = Math.atan2((c2y-c1y),(c2x-c1x))/pi*180
var transform = 'translate(' + (c1x-r1) + ' ' + (c1y-r1) + ') ' + ' rotate(' + angle + ' ' +r1 + ' ' + r1 + ' )'
return transform;
}
gooptrans.r1 = function(value) {
if (!arguments.length) return r1; r1 = value; return gooptrans;
};
gooptrans.r2 = function(value) {
if (!arguments.length) return r2; r2 = value; return gooptrans;
};
gooptrans.c1x = function(value) {
if (!arguments.length) return c1x; c1x = value; return gooptrans;
};
gooptrans.c2x = function(value) {
if (!arguments.length) return c2x; c2x = value; return gooptrans;
};
gooptrans.c1y = function(value) {
if (!arguments.length) return c1y; c1y = value; return gooptrans;
};
gooptrans.c2y = function(value) {
if (!arguments.length) return c2y; c2y = value; return gooptrans;
};
return gooptrans;
}
function goopGen() {
//set defaults
var r1 = function(d) { return d.r1; },
r2 = function(d) { return d.r2; },
c1x = function(d) { return d.c1x; },
c2x = function(d) { return d.c2x; },
c1y = function(d) { return d.c1y; },
c2y = function(d) { return d.c2y; };
//return function to generate goop path
function goop(d) {
var r1 = d3.functor(d.c1.r).call(this, d),
c1x = d3.functor(d.c1.x).call(this, d),
c2x = d3.functor(d.c2.x).call(this, d),
c1y = d3.functor(d.c1.y).call(this, d),
c2y = d3.functor(d.c2.y).call(this, d),
r2 = d3.functor(d.c2.r).call(this, d);
var pi = 3.14159
var dist = Math.sqrt((c2x-c1x)*(c2x-c1x)+(c2y-c1y)*(c2y-c1y))
var alpha1 = pi/4;
var alpha2 = pi/4;
var t1 = -Math.exp(-(dist-Math.log(0.75*r1)-Math.cos(alpha1)*r1-Math.cos(alpha2)*r2))+0.75*r1
var t2 = -Math.exp(-(dist-Math.log(0.75*r2)-Math.cos(alpha1)*r1-Math.cos(alpha2)*r2))+0.75*r2
var t1 = t(dist)[0]
var t2 = t(dist)[1]
function t(dist){
function sigmoid(dist_real){
return 0.7/(1+Math.exp(-dist_real))
}
var dist_real = dist-r1-r2
var t1 = -Math.exp(-(dist-Math.log(sigmoid(dist_real)*r1)-Math.cos(alpha1)*r1-Math.cos(alpha2)*r2))+sigmoid(dist_real)*r1
var t2 = -Math.exp(-(dist-Math.log(sigmoid(dist_real)*r2)-Math.cos(alpha1)*r1-Math.cos(alpha2)*r2))+sigmoid(dist_real)*r2
return [t1, t2]
}
c12startx = r1+Math.cos(alpha1)*r1;
c12starty = r1- Math.sin(alpha1)*r1;
c12endx = r1+dist-Math.cos(alpha2)*r2;
c12endy = r1-Math.sin(alpha2)*r2;
c21startx = r1+dist-Math.cos(alpha2)*r2;
c21starty = r1+Math.sin(alpha2)*r2;
c21endx = r1+Math.cos(alpha1)*r1;
c21endy = r1+Math.sin(alpha1)*r1;
var pathM = 'M ' + c21endx + ' ' + c21endy;
var pathA11 = "A" + r1 + "," + r1 + " 0 0,1 " + ' 0,' + r1
var pathA12 = "A" + r1 + "," + r1 + " 0 0,1 " + c12startx + ', '+ c12starty
var pathC12 = 'C ' + (c12startx+t1) + ' ' + (c12starty+t1) + ', ' + (c12endx-t2) + ' ' + (c12endy+t2) + ', ' + c12endx + ' ' + c12endy;
var pathA21 = "A" + r2 + ',' + r2 + ' 0 0,1 ' + (r1+dist+r2) + ',' + r1
var pathA22 = "A" + r2 + ',' + r2 + ' 0 0,1 ' + c21startx + ', '+ c21starty
var pathC21 = 'C ' + (c21startx-t2) + ' ' + (c21starty-t2) + ', ' + (c21endx+t1) + ' ' + (c21endy-t1) + ', ' + c21endx + ' ' + c21endy;
var path = pathM +' ' + pathA11 + ' ' + pathA12 + ' ' + pathC12 + ' ' + pathA21 + ' ' + pathA22 + ' ' + pathC21;
return path;
}
//getter-setter methods
goop.r1 = function(value) {
if (!arguments.length) return r1; r1 = value; return goop;
};
goop.r2 = function(value) {
if (!arguments.length) return r2; r2 = value; return goop;
};
goop.c1x = function(value) {
if (!arguments.length) return c1x; c1x = value; return goop;
};
goop.c2x = function(value) {
if (!arguments.length) return c2x; c2x = value; return goop;
};
goop.c1y = function(value) {
if (!arguments.length) return c1y; c1y = value; return goop;
};
goop.c2y = function(value) {
if (!arguments.length) return c2y; c2y = value; return goop;
};
return goop;
}
function gradGen(col1,col2, id, el){
while (el.tagName.toLowerCase() != 'svg'){
el = el.parentNode;
};
var node = d3.select(el);
var grad = node.insert('defs',':first-child').append('linearGradient').attr('id', id)
console.log(col1)
grad.append('stop')
.style('stop-color', col1)
.attr('offset', '0%');
grad.append('stop')
.style('stop-color', col2)
.attr('offset', '100%');
var ret_string = 'url(#'+id+')';
return ret_string;
}
var myG = goopGen()
.c1x(function(d) { return d.c1.x; })
.c2x(function(d) { return d.c2.x; })
.c1y(function(d) { return d.c1.y; })
.c2y(function(d) { return d.c2.y; })
.r1(function(d) { return d.c1.r; })
.r2(function(d) { return d.c2.r; });
var myT = goopTransform()
.c1x(function(d) { return d.c1.x; })
.c2x(function(d) { return d.c2.x; })
.c1y(function(d) { return d.c1.y; })
.c2y(function(d) { return d.c2.y; })
var data = pairs.map(function(el){
var col1 = 'hsl(' + (100+Math.round(el.x1[1])/10*36) + ',50%, 50%)'
var col2 = 'hsl(' + (100+Math.round(el.x1[0])/10*36) + ',50%, 50%)'
d = {
id : 'id' + el.key,
c1: {
x: Math.round(el.x1[0]),
y: Math.round(el.y1[0]),
r: 3.5,
col: col1
},
c2: {
x: Math.round(el.x1[1]),
y: Math.round(el.y1[1]),
r: 4.5,
col: col2
} }
return d
})
update_plot();
function update_plot(){
svg.selectAll("path.goop")
.data(data, function(d){return d.id})
.enter().append("path")
.attr("class", "goop")
.attr("d", myG)
.attr("transform", myT)
.style('opacity','.55')
.style('fill', function(d){var url = (gradGen(d.c1.col, d.c2.col, d.id, this)); return url})
}
var button = document.getElementById('button');
button.addEventListener('click', toggle)
var tog = 0
function toggle()
{
if (tog == 0){
tog = 1
data = pairs.map(function(el){
var col1 = 'hsl(' + (100+Math.round(el.x1[1])/10*36) + ',50%, 50%)'
var col2 = 'hsl(' + (100+Math.round(el.x1[0])/10*36) + ',50%, 50%)'
d = {
id : 'id' + el.key,
c1: {
x: Math.round(el.x2[0]),
y: Math.round(el.y2[0]),
r: 5,
col: col1
},
c2: {
x: Math.round(el.x2[1]),
y: Math.round(el.y2[1]),
r: 4,
col: col2
} }
return d
})
}
else {
tog = 0
data = pairs.map(function(el){
var col1 = 'hsl(' + (100+Math.round(el.x1[1])/10*36) + ',50%, 50%)'
var col2 = 'hsl(' + (100+Math.round(el.x1[0])/10*36) + ',50%, 50%)'
d = {
id : 'id' + el.key,
c1: {
x: Math.round(el.x1[0]),
y: Math.round(el.y1[0]),
r: 3.5,
col: col1
},
c2: {
x: Math.round(el.x1[1]),
y: Math.round(el.y1[1]),
r: 4.5,
col: col2
} }
return d
})
}
svg.selectAll("path.goop")
.data(data, function(d){return d.id})
.transition()
.ease(d3.easeCubic)
.duration(2000)
.attr("d", myG)
.attr("transform", myT)
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment