|
<!doctype html> |
|
<html lang="en"> |
|
|
|
<head lang=en> |
|
<meta charset="utf-8"> |
|
<title>D3: Using a Fn in attrTween to Control Movement</title> |
|
</head> |
|
|
|
|
|
<body> |
|
<style> |
|
|
|
|
|
svg { |
|
background: #eee; |
|
} |
|
|
|
circle { |
|
fill: steelblue; |
|
stroke: #fff; |
|
stroke-width: 3px; |
|
} |
|
|
|
</style> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script> |
|
|
|
var width = 640; |
|
var height = 480; |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width) |
|
.attr("height", height) |
|
.append("g"); |
|
|
|
|
|
|
|
var circleData = []; |
|
|
|
// Define first circle and type of movement - circle |
|
var t_circle = d3.map(); |
|
t_circle.set("id", 1); |
|
t_circle.set("cr", 10); |
|
t_circle.set("rotr", 100); |
|
t_circle.set("rtype", "circle"); |
|
t_circle.set("offset", 20); |
|
circleData.push(t_circle); |
|
|
|
// Define first circle and type of movement - ellipse |
|
t_circle = d3.map(); |
|
t_circle.set("id", 2); |
|
t_circle.set("cr", 20); |
|
t_circle.set("rotrx", 200); |
|
t_circle.set("rotry", 100); |
|
t_circle.set("rtype", "ellipse"); |
|
t_circle.set("offset", -100); |
|
circleData.push(t_circle); |
|
|
|
|
|
var easeparam = "linear"; |
|
|
|
// Time to complete one transition / iteration of the circular or elliptical path |
|
var timeparam = 2000; |
|
|
|
// Add each of the two circles: |
|
var circle = svg.selectAll("circle") |
|
.data(circleData, function(d) { return d.get('id');}) |
|
.enter() |
|
.append("circle") |
|
.attr("r", function(d) { return d.get('cr'); }); |
|
|
|
// Setup each circle with a transition, each transition working on transform attribute, |
|
// and using the translateFn |
|
circle |
|
.transition() |
|
.duration(timeparam) |
|
.ease(easeparam) |
|
.attrTween("transform", translateFn()); |
|
|
|
// Setup a couple of text elements to follow the circles: |
|
var text = svg.selectAll("text") |
|
.data(circleData, function(d) { return d.get('id');}) |
|
.enter() |
|
.append("text") |
|
.text(function(d){ return d.get('id');}) |
|
|
|
text |
|
.transition() |
|
.duration(timeparam) |
|
.ease(easeparam) |
|
.attrTween("transform", translateFn()); |
|
|
|
|
|
// The function that actually does the moving: |
|
function translateFn() { |
|
|
|
// We only use 'd', but list d,i,a as params just to show can have them as params. |
|
// Code only really uses d and t. |
|
return function(d, i, a) { |
|
return function(t) { |
|
|
|
// 't': what's t? T is the fraction of time (between 0 and 1) since the |
|
// transition began. Handy. |
|
var t_offset = d.get('offset'); |
|
var t_x, t_y; |
|
|
|
// If the data says the element should follow a circular path, do that. |
|
if (d.get('rtype') == 'circle') { |
|
var rotation_radius = d.get('rotr'); |
|
var t_angle = (2 * Math.PI) * t; |
|
var t_x = rotation_radius * Math.cos(t_angle); |
|
var t_y = rotation_radius * Math.sin(t_angle); |
|
} |
|
|
|
// Likewise for an ellipse: |
|
if (d.get('rtype') == 'ellipse') { |
|
var rotation_radius_x = d.get('rotrx'); |
|
var rotation_radius_y = d.get('rotry'); |
|
var t_angle = (2 * Math.PI) * t; |
|
var t_x = rotation_radius_x * Math.cos(t_angle); |
|
var t_y = rotation_radius_y * Math.sin(t_angle); |
|
} |
|
|
|
return "translate(" + ((width/2) + t_offset + t_x) + "," + (height/2 + t_offset + t_y) + ")"; |
|
}; |
|
}; |
|
} |
|
|
|
</script> |
|
</body> |
|
</html> |
|
|