开发者

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

开发者 https://www.devze.com 2023-03-15 18:12 出处:网络
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 selec

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/>
    &l开发者_JAVA百科t;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?


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


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>


Just an idea: set the 'begin' attributes to be the current amount of seconds elapsed since the page loaded.
Kinda like so:

function seconds_elapsed ()     { 
  var date_now = new Date (); 
  var time_now = date_now.getTime (); 
  var time_diff = time_now - startTime; 
  var seconds_elapsed = Math.floor ( time_diff / 1000 ); 
  return ( seconds_elapsed ); 
} 

And/or add new animations with .appendChild() rather than fiddle with existing values.

0

精彩评论

暂无评论...
验证码 换一张
取 消