The Old County
The Old County

Reputation: 89

d3.js drawing circles with throbbing outer lines

I am trying to create something similar to this http://demo.joostkiens.com/het-parool-4g/

Alarm throbbing points

Where the plotted circles have outer lines throbbing outwards.

This is my demo so far.

http://jsfiddle.net/NYEaX/95/

I've plotted the circles with some dummy data. On top are red based circles. How do I invoke the animation and make it more vibrant per the alarm data .. eg. alarmLevel.

I am unsure as to how to create a looping animation with the radius bouncing off past the circumference and then fading out - having this variety based on the alarmLevel threshold

Would ideally need the transition to occur like this in a loop., http://jsfiddle.net/pnavarrc/udMUx/

var speedLineGroup = sampleSVG.append("g")
                                        .attr("class", "speedlines");

                            speedLineGroup.selectAll("circle.dl-speed-static")
                                .data(dataset)
                                .enter().append("circle")
                                .style("stroke", "red")
                                .style("fill", "black")
                                .attr("r", function(d){
                                    return d.value;
                                })
                                .attr("cx", function(d){
                                    return d.xcoord;
                                })
                                .attr("cy", function(d){
                                    return d.ycoord;
                                })
                                .attr("class", "dl-speed-static")
                                .attr("stroke-opacity", function (e) {
                                    return 1;
                                    //return var
                                })
                                .attr("fill-opacity", function (e) {
                                    return 1;
                                    //return var
                                })
                                .transition()
                                .ease("linear")
                                .duration(200)
                                .attr("r", function (e) {
                                    return 1;
                                    //return var
                                })

I've merged the ideas in the post. I've placed the ring creation in its own function and removed the time out. I've also started to try and hook into the alarm threshold per marker.

http://jsfiddle.net/NYEaX/102/

but the application still seems delayed/buggy - not very clear as in the prime example. How could this be improved further. Some of the alarm counts are low - but this method is causing the ring to throb too soon or flickery. Its almost like I need to invert the value to have a low alarm - create a slower response.

            function makeRings() {
                var datapoints = circleGroup.selectAll("circle");
                var radius = 1;

                    function myTransition(circleData){

                        var transition = d3.select(this).transition();

                            speedLineGroup.append("circle")
                              .attr({"class": "ring",
                                     "fill":"red",
                                     "stroke":"red",
                                     "cx": circleData.xcoord,
                                     "cy": circleData.ycoord,
                                     "r":radius,
                                     "opacity": 0.4,
                                     "fill-opacity":0.1
                                    })
                              .transition()
                              .duration(function(){                 
                                return circleData.alarmLevel*100;
                              })
                              .attr("r", radius + 100 )
                              .attr("opacity", 0)
                              .remove();


                        transition.each('end', myTransition);
                    }

              datapoints.each(myTransition);
            } 

This is the latest code..

makeRings()

var t = window.setInterval(makeRings, 10000);

                function makeRings() {
                        var datapoints = mapSVG.selectAll("circle.location");
                        var radius = 1;

                            function myTransition(circleData){

                                console.log("circleData", circleData);

                                var transition = d3.select(this).transition();

                                    speedLineGroup.append("circle")
                                      .attr({"class": "ring",
                                             "fill":"red",
                                             "stroke":"red",
                                             "cx": circleData.x * ratio,
                                             "cy": circleData.y * ratio,
                                             "r":radius,
                                             "opacity": 0.4,
                                             "fill-opacity":0.1
                                            })
                                      .transition()
                                      .duration(function(){                 
                                        return (circleData.redSum * 100);
                                      })
                                      .attr("r", radius + 30 )
                                      .attr("opacity", 0)
                                      .remove();


                                transition.each('end', myTransition);
                            }

                      datapoints.each(myTransition);
                    } 

Upvotes: 1

Views: 1763

Answers (2)

ckersch
ckersch

Reputation: 7687

All you need to do to create looping transitions in d3 is to use the end callback on transitions. Create two functions, which each create a transition on your data, with one going from your start point to your end point, and the other going back, and have them call each other on completion, like so:

function myTransition(d){
    var transition = d3.select(this).transition();

    //Forward transition behavior goes here
    //Probably create a new circle, expand all circles, fade out last circle

    transition.each('end', myTransition); //This calls the backward transition
}

d3.select('myFlashingElement').each(myTransition);

This will encapsulate everything and keep looping at whatever the duration of your transition is. The next transition will always fire when the transition before it ends, so you don't have to worry about syncing anything.

Upvotes: 0

AmeliaBR
AmeliaBR

Reputation: 27544

The example you linked to uses minified code, so it's a bit of a pain to figure out what they're doing. However, if you just watch the changes in the DOM inspector, you'll see that each ring is a new circle that gets added, grows in size and faces away, and then gets removed. The different points vary in how big the rings get before they fade away (and therefore in how many rings are visible at a time, since they all grow at the same speed).

The approach I would take to make this continue indefinitely is:

  1. Use 'setInterval' to call a function on a regular basis (e.g., once or twice per second) that will create a new ring around each data circle.

  2. Create the rings using an .each() call on your data circles, but add them to a different <g> element, and/or with different class names so there is no confusion between the rings and the data points.

  3. Set the initial radius of the ring to be the same as the data point, but then immediately start a transition on it. Make the duration of the transition a function of the "intensity" data value for the associated data circle, and also make the final radius a function of that data value. Also transition the opacity to a value of 0.

  4. Make the final line of the transition for the rings .remove() so that each ring removes itself after it has finished expanding.

Basic code:

window.setInterval(makeRings, 1000);

function makeRings() {

  datapoints.each(function(circleData){  
       //datapoints is your d3 selection of circle elements

    speedLineGroup.append("circle")
      .attr({"class": "ring",
             "fill":"red", //or use CSS to set fill and stroke styles
             "stroke":"red",
             "cx": circleData.xCoord,
             "cy": circleData.yCoord,
                 //position according to this circle's position
             "r":radius, //starting radius, 
                         //set according to the radius used for data points
             "opacity": 0.8, //starting opacity
             "fill-opacity":0.5 //fill will always be half of the overall opacity
            })
      .transition()
      .duration( intensityTimeScale(circleData.intensity) )
          //Use an appropriate linear scale to set the time it takes for 
          //the circles to expand to their maximum radius.
          //Note that you *don't* use function(d){}, since we're using the data
          //passed to the .each function from the data point, not data
          //attached to the ring
      .attr("r", radius + intensityRadiusScale(circleData.intensity) )
          //transition radius
          //again, create an appropriate linear scale
      .attr("opacity", 0) //transition opacity
      .remove(); //remove when transition is complete

  });
}

Because both the change in radius and the duration of the transition are linear functions of the intensity value, the change will have a constant speed for all the data points.

Upvotes: 1

Related Questions