Built with blockbuilder.org
Last active
August 27, 2017 08:31
-
-
Save IsaKiko/bd4de582287695f96834dd92315e23f5 to your computer and use it in GitHub Desktop.
SVG inline only Gooey Effect
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit | |
border: | |
scrolling: | |
height: NaN |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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