HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

D3.js interpolating line on circumference of circle

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
circumferencecircleinterpolatingline

Problem

For now, I've tried to make a line rotate on the path of a circle, but used setInterval multiple times and there might be other ways of achieving the same.

I would like your thoughts before I add two more circles just like in this webpage.



var width = 300;
var height = 300;
var body = d3.select("body")
var svg = body.append("svg")
.attr("width",width)
.attr("height",height)
.attr("fill","blue")

var rad = Math.PI;

var circlePath = svg.append("path")
.attr('d', "M70,100a30,30 0 1,0 60,0a30,30 0 1,0 -60,0")
.attr('fill', 'none')
.attr('stroke', 'steelblue' )

// var path =

console.warn(circlePath.node());
var path1 = circlePath.node();

var myLine = svg.append('line')
.attr('x1', 100)
.attr('y1', 100)
.attr('x2', function(){return path1.getPointAtLength(l).x})
.attr('y2', function(){return path1.getPointAtLength(l).y} )
.attr('stroke', "red" )
.attr('stroke-width',3)
var l = 1
setInterval(function(){
myLine.transition()
.duration(20)
.attr('stroke', "blue")

.attr('x2', function(){return path1.getPointAtLength(l).x})
.attr('y2', function(){return path1.getPointAtLength(l).y})

.each("end", endAnimationfunc)

}, 5);
setInterval(function(){
if(l

`

Solution

I'd suggest using nested group elements (`s) with transform attributes.

I.e. draw the circle (just as a
element) and the line inside a group, and then draw a nested group with a circle and line within that, and so on. When you rotate the outermost group, the nested ones will follow. Rotate each of them, and you'll get the effect from the demo.

This way, you only need to animate one thing: rotation. For instance:



var width = 200;
var height = 200;
var body = d3.select("body");
var svg = body.append("svg")
.attr("width", width)
.attr("height", height)
.attr("fill", "blue");

function addCircle(container, radius, x, y) {
// Note the hardcoded rotation; it's just an example
var group = container.append("g")
.attr("class", "circle-container")
.attr("transform", "translate(" + x + ", " + y + ")");

function rotate() {
group.transition()
.duration(10000)
.ease("linear")
.attrTween("transform", function (d, i, a) {
return function (t) {
var rotation = t * 360;
return "translate(" + x + ", " + y + ") rotate(" + String(rotation) + ")";
};
})
.each("end", rotate);
};
rotate();

group.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", radius)
.style("fill", "none")
.style("stroke", "steelblue");

group.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", radius)
.attr("y2", 0)
.style("stroke", "steelblue");

return group;
}

var c1 = addCircle(svg, 30, width/2, height/2); // centered
var c2 = addCircle(c1, 20, 30, 0); // offset by 1st circle's radius
var c3 = addCircle(c2, 15, 20, 0); // offset by 2nd circle's radius




Heck, you can do this in pure SVG (added extra groups to isolate rotation):








































At any rate, you can simply skip all the
getPointAtLength calls. Your path is a circle, so a) you can use a element, and b) any point on its circumference can be described using some simple math:

var x = Math.cos(t) * radius + centerX;
var y = Math.sin(t) * radius + centerY;


where
t is something between 0 and 2π (or beyond that; it'll just loop around). So your animation needs only change the value of t` to find a point on the circle.

Code Snippets

var x = Math.cos(t) * radius + centerX;
var y = Math.sin(t) * radius + centerY;

Context

StackExchange Code Review Q#87636, answer score: 3

Revisions (0)

No revisions yet.