4
\$\begingroup\$

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  = <path d="M70,100a30,30 0 1,0 60,0a30,30 0 1,0 -60,0"/>

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<path1.getTotalLength()){
        l=l+1
    }
    else{
        l = 1
    }
},10)

var endAnimationfunc = function(){
    d3.select(this)
        .transition()
        .attr('x2', function(){return path1.getPointAtLength(l).x})
        .attr('y2', function(){return path1.getPointAtLength(l).y})

}

function translateAlong(path1){
    var l = path1.getTotalLength();
    // console.warn(l);
    return function(){
        // console.warn(d,i,a);

            var p = path1.getPointAtLength( l );
            console.log(p);

            return  "translate("+p.x + ","+p.y +")";

    }
}
<body></body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

\$\endgroup\$
  • \$\begingroup\$ It would be so much easier to review with a jsfiddle or similar \$\endgroup\$ – PHearst Apr 22 '15 at 18:54
  • \$\begingroup\$ @PHearst I've converted the code into a Stack Snippet in Rev 5. \$\endgroup\$ – 200_success Apr 23 '15 at 9:55
2
\$\begingroup\$

I'd suggest using nested group elements (<g>s) with transform attributes.

I.e. draw the circle (just as a <circle> 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
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

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

<svg width="200" height="200">
  <!-- root transform to center everything -->
  <g transform="translate(100, 100)">
    
    <!-- draw the outermost group and animate it -->
    <g>
      <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="10s" repeatCount="indefinite" />
      <circle cx="0" cy="0" r="40" fill="none" stroke="steelblue" />
      <line x1="0" y1="0" x2="0" y2="40" stroke="steelblue" />
      
      <!-- offset the children -->
      <g transform="translate(0, 40)">
      
        <!-- now repeat, only smaller -->
        <g>
          <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="10s" repeatCount="indefinite" />
          <circle cx="0" cy="0" r="30" fill="none" stroke="steelblue" />
          <line x1="0" y1="0" x2="0" y2="30" stroke="steelblue" />
          
          <g transform="translate(0, 30)">
            
            <!-- and again -->
            <g>
              <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="10s" repeatCount="indefinite" />
              <circle cx="0" cy="0" r="20" fill="none" stroke="steelblue" />
              <line x1="0" y1="0" x2="0" y2="20" stroke="steelblue" />
            </g>
            
          </g>
        </g>
      </g>
    </g> 
  </g>
</svg>

At any rate, you can simply skip all the getPointAtLength calls. Your path is a circle, so a) you can use a <circle> 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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.