|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
circle { |
|
fill: #000; |
|
} |
|
|
|
body{ |
|
width:100%; |
|
height: 100%; |
|
} |
|
|
|
#serpent { |
|
width:100%; |
|
height: 100%; |
|
} |
|
|
|
svg { |
|
width:100%; |
|
height: 100%; |
|
} |
|
|
|
</style> |
|
<body> |
|
|
|
<div id="serpent"> |
|
<svg id="viewport" width="1000" height="1000"> |
|
<filter id="fancy-goo"> |
|
<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" /> |
|
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9" result="goo" /> |
|
<feComposite in="SourceGraphic" in2="goo" operator="atop"/> |
|
</filter> |
|
</svg> |
|
</div> |
|
|
|
|
|
<script src="//d3js.org/d3.v4.0.0-alpha.28.min.js"></script> |
|
<script> |
|
|
|
/* |
|
|
|
http://bimba.info/en/new/2016/04/17/huge-prehistoric-whales-found-in-egyptian-desert |
|
*/ |
|
|
|
var margin = {top: 40, right: 40, bottom: 40, left: 40}, |
|
width = 1000 - margin.left - margin.right, |
|
height = 1000 - margin.top - margin.bottom; |
|
|
|
var y = d3.scalePoint() |
|
.domain(d3.range(20)) |
|
.range([0, height]); |
|
|
|
var z = d3.scaleLinear() |
|
.domain([10, 0]) |
|
.range(["hsl(62,100%,90%)", "hsl(228,30%,20%)"]) |
|
.interpolate(d3.interpolateHcl); |
|
|
|
var svg = d3.select("#viewport") |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
|
|
var defs = svg.append('defs'); |
|
|
|
// gooey seems to perform better than fancy-goo |
|
|
|
var filter = defs.append('filter').attr('id','gooey'); |
|
filter.append('feGaussianBlur') |
|
.attr('in','SourceGraphic') |
|
.attr('stdDeviation','10') |
|
.attr('result','blur'); |
|
filter.append('feColorMatrix') |
|
.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 18 -7') |
|
.attr('result','gooey'); |
|
filter.append('feComposite') |
|
.attr('in','SourceGraphic') |
|
.attr('in2','gooey') |
|
.attr('operator','atop'); |
|
|
|
svg |
|
.append("g") |
|
.style("filter", "url(#gooey)") |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
// svg:ellipse cx="0" cy="0" rx="0" ry="0" |
|
|
|
var getAngle = function(shipPoint, targetPoint) { |
|
|
|
var cx = shipPoint.x; |
|
var cy = shipPoint.y; |
|
var ex = targetPoint.x; |
|
var ey = targetPoint.y; |
|
|
|
var dy = ey - cy; |
|
var dx = ex - cx; |
|
var theta = Math.atan2(dy, dx); // range (-PI, PI] |
|
theta *= 180 / Math.PI; // rads to degs, range (-180, 180] |
|
//if (theta < 0) theta = 360 + theta; // range [0, 360) |
|
return theta; |
|
} |
|
|
|
svg.selectAll("ellipse") |
|
.data(y.domain()) |
|
.enter().append("ellipse") |
|
.attr("rx", 25) |
|
.attr("ry", 75) |
|
.attr("cx", 0) |
|
.attr("cy", y) |
|
.style("fill", function(d) { return z(Math.abs(d % 20 - 10)); }) |
|
.transition() |
|
.duration(3500) |
|
.delay(function(d) { return d * 90; }) |
|
.on("start", slide); |
|
|
|
var end = {x: 500, y: 500}; |
|
var start = {x: 0, y: 0}; |
|
|
|
function onSlideComplete(){ |
|
end.x = Math.floor( Math.random() * width); |
|
end.y = Math.floor( Math.random() * height); |
|
|
|
start.x = 0; |
|
start.y = Math.floor( Math.random() * height); |
|
} |
|
|
|
/* todo |
|
- change the rotation so the disc points in the direction of the next point |
|
- apply a sine wave or something to the body |
|
*/ |
|
|
|
//var rotate = d3.svg.transform().rotate(-45); |
|
|
|
var getTransform = function(ship,target){ |
|
var shipX = d3.select(ship).attr("cx"); |
|
var shipY = d3.select(ship).attr("cy"); |
|
|
|
var shipPoint = {x:shipX,y:shipY}; |
|
var targetPoint = {x:target.x,y:target.y}; |
|
var angle = getAngle(shipPoint, targetPoint); |
|
return "translate(" + target.x + "," + target.y + ") rotate(" + angle + ")" |
|
} |
|
|
|
function slide() { |
|
d3.active(this) |
|
.attr("cy", end.x) |
|
.attr("cx", end.y) |
|
//.attr("transform", getTransform(this,end) ) |
|
.transition() |
|
.attr("cx", start.x) |
|
.attr("cy", start.y) |
|
//.attr("transform", getTransform(this,start) ) |
|
.transition() |
|
.on("start", slide) |
|
.on("end", onSlideComplete); |
|
} |
|
|
|
</script> |