1

Here are two JSFiddles:

Codes that call the tick in JSFiddle 1:

path.transition()
.duration(duration)
.ease("linear")
.attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
.each("end", tick);

Codes that call the tick in JSFiddle 2:

axis.transition()
.duration(duration)
.ease("linear")
.call(xAxis)
.each("end", tick);

Do a test 1:

  1. Open JSFiddle 1 and JSFiddle2 using IE 11 or Chrome browser.
  2. Observe the animated svg line

Do a test 2:

  1. Open JSFiddle 1 and JSFiddle2 using IE9 browser.
  2. Observe the animated svg line

In test 1, I observed that the start point of animated svg line is shift together with the axis shift and therefore it visually looks synchronized. Thus, you see a smooth animated line.

In test 2, I observed that the start point of svg line is not shift together with the axis shift and therefore it visually looks non-synchronized. Thus, you see an a bit weird jiggling line.

Is there anyone know what cause this to happen? and how to solve this in IE9?

I uploaded a video which demostrate the problem : video 1

EDIT

Based on Amelia answer, I uploaded the improved version with custom tween function : video 2

I make some minor adjustment on her answer and use x(t - n + 2) instead of x(t - n + 1). Eventhough the line is not smooth but at least it is now sync with the axis shift : video 3

jhyap
  • 3,779
  • 6
  • 27
  • 47

1 Answers1

2

I suspect that the problem is simply that IE9 does not support the requestAnimationFrame() method. D3 uses requestAnimationFrame if available to synchronize transitions.

That makes a difference because the requestAnimationFrame method bundles a series of consecutive calls into a single "frame" -- and passes them all the same timestamp value. Without the requestAnimationFrame method, each transition will get a different timestamp, delayed by however long it took the browser to complete the previous task.

Unfortunately, there's no easy solution for how to fix it for someone who insists on using an out-of-date browser.

What you can do is try to make your code as efficient as possible. For example, currently you are creating separate transitions to shift each of your paths by the same transform amount. If instead you created a <g> element and drew all the paths within it, then you would only need one transform, and one transition.

var graph = g.append("svg")
     .attr("width", width)
     .attr("height", height + margin.top + margin.bottom);

var plot = graph.append("g").attr("class", "plot");

var path1 = plot.append("g").append("path")

/* etc for the other paths */

And then in your tick() function:

  • don't set any transforms on the path elements when you redraw the lines, instead clear the plot transform with

    plot.attr("transform", null);
    
  • replace all the path transitions with

     plot.transition()
         .duration(duration)
         .ease("linear")
         .attr("transform", "translate(" + x(t - n + 1) + ")");
    

http://jsfiddle.net/QBDGB/155/

(Let me know if that's a noticeable improvement in IE9 -- the IE11 emulation tools weren't really showing the problem, so I can't test effectively.)

By the way, you could also shorten your code a lot by using a selection & datajoin to draw your different lines. Check out the tutorials for more on selections and joins in d3.

Edit

Thanks for the feedback, and sorry it's still not making an improvement. The following approach is more complicated, and might be hard to follow if you're new to d3, but it should force the synchronization. Be warned that the animation will still probably be jumpy, but the line and axis should jump at the same time!

The idea is to only use one transition, and then at every tick of the transition update both the plot translation and the axis. That is done using a custom "tween" function. It assumes you've already made the changes above.

In particular, instead of allowing the default axis function to create separate transitions for each sub-component, I use the same approach as the lines: draw the axis without a transition, and then use a transform to shift it sideways. Both transforms are applied within the same tween function.

 plot.attr("transform", null);

 // redraw the axis, without transition
 axis.call(xAxis).attr("transform", null);

 // slide the line left
 plot.transition()
     .duration(duration)
     .ease("linear")
     .attrTween("transform", 
       function(){
           //this function is run for every element 
           //in the selection (which only has one element).

           //create an interpolator function
           var inter = d3.interpolateString("translate(0,0)", 
                    "translate(" + x(t - n + 1) + ")");

           return function(t) {
               //this function is called at every "tick"
               //of the transition

               //transition the axis
               axis.attr("transform", inter(t) );

               //return the value to transition the plot
               return inter(t);
           };
       })
     .each("end", tick); //loop

There was just one extra complication: The x-axis <g> element already had a transform attribute, shifting it down to the bottom of the graph. I could have repeated that transform, each time, so that the transitioned attribute would look like axis.attr("transform", "translate(0," + height + ")" + inter(t) );. However, I decided to keep it simple by using nested <g> elements for the axis: the vertical transform is applied on an outer group, and the horizontal transforms on the inner group:

var xAxis = d3.svg.axis().scale(x)
         .orient("bottom")
         .outerTickSize(0);

 var axis = graph.append("g") //outer group handles the vertical transform
     .attr("class", "x axis")
     .attr("transform", "translate(0," + height + ")")
     .append("g") 
          //this nested <g> will be stored in the `axis` variable!
          //and therefore will get the horizontal transform attribute
     .call(x.axis = xAxis);

Notice that I also added .outerTickSize(0) to the axis definition. That prevents the axis from drawing an extra tick mark at the start and end of the domain -- the outer tick was appearing as a strange flickering line at the base of Y axis, appearing every time the axis was redrawn and then immediately disappearing as the axis shifted.

http://jsfiddle.net/QBDGB/160/

P.S. If your client is still disappointed, calmly & politely remind him or her that IE9 is an out-of-date browser that doesn't have the capabilities of the latest browsers for generating smooth animation. If someone wants the latest & greatest websites, they need to use the latest & greatest browsers.

AmeliaBR
  • 27,344
  • 6
  • 86
  • 119
  • Your answer bright up my day but the suggested solution visually makes no difference. IE11 emulator can't simulate the problem. But in real IE9 version, the problem does exists. (I downdgraded my IE11 to IE9 today and verified the problem. – jhyap Apr 16 '14 at 04:53
  • I update my question with an youtube demo, so that it can help you understand the problem better. – jhyap Apr 16 '14 at 07:44
  • @jhyap: let me know how the new version looks. – AmeliaBR Apr 16 '14 at 15:11
  • impressive improvement but left minor back shift problem when ever path comes to the end of the axis. See `video 2` I uploaded for `x(t - n + 1)` problem. Hence, I adjust the `d3.interpolateString` to become `x(t - n + 2)` instead of `x(t - n + 1)`. With `n+2`, even thougth the path does not looks smooth, but at least the path shift now is sync with axis shift. – jhyap Apr 17 '14 at 00:41
  • you might also interested on my another question http://stackoverflow.com/questions/23167075/how-to-disable-the-minor-ticks-in-d3-log-scale :) – jhyap Apr 19 '14 at 07:55