Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically change SVG animation values while using fill="freeze"

I am using SVG for charts in an AJAX web app. When the initial data is received I would like to animate chart elements (such as bars) from the default 0 values to the new data value. If the user selects a new dataset I request new data from the server and then would like to animate the bar from the previous value to the new value.

<rect x="0" y="0" width="1px" height="20px" fill="red">
    <animate id="anim" attributeName="width" attributeType="XML"
        begin="indefinite" from="0" to="100px" dur="0.8s" fill="remove"/>
</rect>

Each time data is received, I set the from and to attributes and call beginElement().

If I set fill="remove" in the SVG animation then the bar animates correctly each time a new dataset is loaded, but, of course, between animations the bar returns to its default width.

Setting width.baseVal.value to establish a new base value either before OR after calling beginElement() causes very nasty flickering.

So I tried using fill="freeze" to have the bar keep its width at the end of each animation. The problem is that the first animation works, but each subsequent call to beginElement() returns the bar to its default width immediately and animation stops working.

I am testing in Chrome 12.0. Here is a sample to see how animation breaks when using freeze.

http://jsfiddle.net/4ws9W/

<!DOCTYPE html>
<html>
  <head><title>Test SVG animation</title></head>

  <body>
    <svg width="200px" height="20px" xmlns="http://www.w3.org/2000/svg" version="1.1">
      <rect x="0" y="10px" width="200px" height="2px"/>

      <rect x="0" y="0" width="1px" height="20px" fill="red">
        <animate id="anim" attributeName="width" attributeType="XML" begin="indefinite" from="0" to="100px" dur="1.3s" fill="freeze"/>
      </rect>

      parent.anim = document.getElementById('anim');

    </svg>

    <br/>
    <a href="javascript:anim.beginElement();">anim.beginElement();</a>
    <br/>
    <a href="javascript:anim.endElement();">anim.endElement();</a>
    <br/>

    <br/>
    <a href="javascript:anim.setAttribute('to', '50px');">anim.setAttribute('to', '50px');</a>
    <br/>
    <a href="javascript:anim.setAttribute('to', '150px');">anim.setAttribute('to', '150px');</a>
    <br/>

    <br/>
    <a href="javascript:anim.setAttribute('fill', 'remove');">anim.setAttribute('fill', 'remove');</a>
    <br/>
    <a href="javascript:anim.setAttribute('fill', 'freeze');">anim.setAttribute('fill', 'freeze');</a>
    <br/>

    </body>
</html>

How can I animate a bar's width, have it keep the new width, and then repeatedly animate it to new values as they arrive?

like image 987
Gavi Lock Avatar asked Jun 28 '11 13:06

Gavi Lock


2 Answers

You can call suspendRedraw method on the necessary elements to avoid flickering. HTH

like image 99
Spadar Shut Avatar answered Nov 16 '22 17:11

Spadar Shut


I think when you are updating the next 'to' value, update the current 'to' value to the width attribute of the rectangle object as well as the 'from' attribute of the animation. while u are updating this make sure u set the fill->remove and after changing the attribute value revert the fill->freeze. see if the code below works.

    <!DOCTYPE html>
    <html>
    <head><title>Test SVG animation</title>
    <script type='text/javascript'> 
    var animNode = 0; 
    function OnEndHandler(evt)
    {
      if(!animNode)
         animNode = evt.target;      
       var previousToValue = animNode.getAttribute('to'); 
      animNode.parentNode.setAttribute('width', previousToValue);   
      animNode.setAttribute('from', previousToValue);     
   }  
   function OnBtnHandler(event)
   {
      if(!animNode)
          animNode = document.querySelector('#anim'); 
     if(event.target.id == 'nextBtn')
     {
         animNode.setAttribute('fill', 'remove');
         animNode.setAttribute('to', '150px');   
         animNode.setAttribute('fill', 'freeze');
         animNode.beginElement(); 
     }
     else if(event.target.id == 'startBtn')
     {
         animNode.setAttribute('to', '50px');    
        animNode.beginElement(); 
     }   
    }

   </script>
   </head>

   <body>
    <input id= 'startBtn' type='button' value='StartBar' onclick='OnBtnHandler(event)' />
      <input id='nextBtn'  type='button' value='NextBar'  onclick='OnBtnHandler(event)'        />       
     <svg width="200px" height="20px" xmlns="http://www.w3.org/2000/svg" version="1.1">
      <rect x="0" y="10px" width="200px" height="2px"/>
      <rect x="0" y="0" width="1px" height="20px" fill="red">
        <animate id="anim" attributeName="width" attributeType="XML" begin="indefinite"  from="0" to="100px" dur="1.3s" fill="freeze" onend='OnEndHandler(evt)' />
      </rect>
     </svg>

    <br/>
    <a href="javascript:anim.beginElement();">anim.beginElement();</a>
    <br/>
    <a href="javascript:anim.endElement();">anim.endElement();</a>
    <br/>

    <br/>
    <a href="javascript:anim.setAttribute('to', '50px');">anim.setAttribute('to',  '50px');</a>
    <br/>
    <a href="javascript:anim.setAttribute('to', '150px');">anim.setAttribute('to', '150px'); </a>
    <br/>

    <br/>
    <a href="javascript:anim.setAttribute('fill', 'remove');">anim.setAttribute('fill', 'remove');</a>
    <br/>
    <a href="javascript:anim.setAttribute('fill', 'freeze');">anim.setAttribute('fill', 'freeze');</a>
    <br/>

    </body>
</html>
like image 21
rajarshi Avatar answered Nov 16 '22 16:11

rajarshi