Search code examples
javascriptanimationsvgsvg-animate

how to translate the path of svg


I have a svg of speedometer. I want to create some animation. There is a needle which is made of path. I have grouped that path so that i can translate or transform. Its a meter/needle which has to move with the user provided value. If the user provides the value between 0 and 25, the needle should be in first slice and so on. But i have no idea on moving the needle which is made of path.

I have created a jsbin of it

http://jsbin.com/danefehiro/1/edit?html,js,output

Here is the code

<input type="text" id="speedValue" />
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 344.996 344.996" xml:space="preserve">
<g>
    <path fill="rgb(132, 219, 255)" d="M7.5,243.337h52.227c0-31.141,12.622-59.334,33.03-79.741l-36.93-36.93
        C25.968,156.525,7.5,197.774,7.5,243.337z"/>
    <path fill="rgb(84, 192, 235)" d="M55.827,126.666l36.93,36.93c20.408-20.408,48.6-33.03,79.741-33.03V78.339
        C126.935,78.339,85.685,96.807,55.827,126.666z"/>
    <circle style="fill:#51B3DA;" cx="172.498" cy="243.337" r="23.32"/>
    <path fill="rgb(255, 209, 92)" d="M337.496,243.337h-52.227c0-31.141-12.622-59.334-33.03-79.741l36.93-36.93
        C319.028,156.525,337.496,197.774,337.496,243.337z"/>
    <path fill="rgb(255, 112, 88)" d="M289.169,126.666l-36.93,36.93c-20.408-20.408-48.6-33.03-79.741-33.03V78.339
        C218.061,78.339,259.311,96.807,289.169,126.666z"/>
    <g id="meter">
        <path fill="#40596B" id="needle" d="M175.884,220.29c10.162,1.482,18.179,9.5,19.661,19.662l44.785-64.447L175.884,220.29z"/>
    </g>
</g>
</svg>


  var speedValue = document.querySelector('#speedValue');
  var needle = document.querySelector('#needle');
  var meter = document.querySelector('#needle');
  speedValue.addEventListener("click", function(event){
    var value = event.target.value;
    // if value is in between 1 - 25, the needle
    // should be on first block, if 25 - 50, then
    // the needle should be on second slice and if
    // 50  - 75, then on 3rd slice and if 75 - 100
    // then on last slice
    if (value >= 0 && value <= 25) {
      meter.setAttribute("transform", "rotate(45)");
    } else if(value > 25 && value <= 50) {
      meter.setAttribute("transform", "rotate(90)"); 
    } else {
      meter.setAttribute("transform", "rotate(180)"); 
    }
  });

Solution

  • You laid a trap for yourself when the #meter path by default is already rotating 135 degrees, and you are missing a transform-origin for the diagram. The following works:

    var speedValue = document.querySelector('#speedValue');
    var needle = document.querySelector('#needle');
    var meter = document.querySelector('#meter');
    speedValue.addEventListener("click", function(event){
      var value = event.target.value;
      // if value is in between 1 - 25, the needle
      // should be on first block, if 25 - 50, then
      // the needle should be on second slice and if
      // 50  - 75, then on 3rd slice and if 75 - 100
      // then on last slice
      needle.setAttribute("transform-origin", "172.498px 243.337px");
      if (value >= 0 && value <= 25) {
        needle.setAttribute("transform", "rotate(-112.5)"); // 45 / 2 - 135 = -112.5
      } else if(value > 25 && value <= 50) {
        needle.setAttribute("transform", "rotate(-67.5)"); 
      } else if(value > 50 && value <= 75) {
        needle.setAttribute("transform", "rotate(-22.5)"); 
      } else {
        needle.setAttribute("transform", "rotate(22.5)"); 
      }
    });
    

    I added an onLoad listener to correctly orient the needle from the start.

    window.addEventListener("load", function(event){
      var value = event.target.value;
      needle.setAttribute("transform-origin", "172.498px 243.337px");
      needle.setAttribute("transform", "rotate(-112.5)");
    });
    

    According to the comments, an animateTransform element is added to show you how to use it (even though it should belong to a new question).

    A new jsbin: http://jsbin.com/qexohozido/1/edit?html,js,output

    First you add the element under #needle:

    <animateTransform id="animation" attributeName="transform" type="rotate" from="0 172.498 243.337" to="0 172.498 243.337" dur="1s" fill="freeze" restart="whenNotActive"/>
    

    Then you animate it inside your if-else:

    var from = 0;
    var animation = document.querySelector('#animation');
    ... else if(value > 50 && value <= 75) {
      animation.setAttribute("from", from + " 172.498 243.337");
      animation.setAttribute("to", "90 172.498 243.337");
      animation.beginElement();
      from = 90; 
    } ...
    

    In this case you need not offset -135 degrees in #animation because the rotation is already being offset in #needle during onLoad.