Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Interacting with svg object positions at runtime

ANSWERED

The fact that positions can be specified as percentages in the svg markup for a watchface / app is great, but if you take advantage of this feature then at runtime document elements set from percentages all report 0 for their value. 

 

After wasting hours of my life, I eventually I found this post mentioning that you can't use percentages for document element attributes in javascript (https://dev.fitbit.com/build/guides/user-interface/javascript/#interacting-with-elements

 

This is SUPER frustrating, and though the solution is actually pretty simple it took me a long time to work it out.

 

Here's a snippet of my SVG code to help illustrate:

 

<svg id="root" viewport-fill="fb-black">
  <svg id="targeting" x="2%" y="2%" width="96%" height="60%">
      <g id="leftLine" pointer-events="visible" transform="translate(5%,0%)">
        <line x1="0" y1="0" x2="0" y2="100%" fill="red" stroke-width="4"/>
      </g>
      <g id="rightLine" pointer-events="visible" transform="translate(97%,0%)">
        <line x1="0" y1="0" x2="0" y2="100%" fill="red" stroke-width="4"/>
      </g>
    </svg>
</svg>

 

 

I want to procedurally animate leftLine and rightLine positions; I need to do this in pixels as we now know, I also need to do this relative to their parent node (possibly because I've used the <g> node?), and helpfully all the attributes I set in the svg are all returning 0...

 

Turns out you can just grab the on-screen bounding box of any element using .getBBox() which returns a DOMRect that gives you the screen position, width etc. of the element.

 

By grabbing the bounds of the parent element in the svg I can now position leftLine halfway across the svg element "targeting" I specified entirely with percentages!

 

  let targeting = document.getElementById("targeting");
  let bbox = targeting.getBBox();

 

 

Hope this saves someone else a few hours of their life 🙂

 

FWIW - I've literally just started messing with the fitbit SDK today and have lost several hours of my life because of bad documentation / terrible out of date questions & answers.

 

I found a couple of posts on this site whilst trying to get to the bottom of this and either the "solutions" was unhelpful or just plain wrong - here they are so you can avoid them...

https://community.fitbit.com/t5/SDK-Development/Setting-height-width-percentages/td-p/2239180

https://community.fitbit.com/t5/SDK-Development/Get-position-of-SVG-element-with-JavaScript/td-p/406... 

 

Best Answer
1 BEST ANSWER

Accepted Solutions

No worries - thanks for getting back to me...

 

I really just wanted to make sure that whoever came along next after me would have more clues than I did, very few things are more frustrating than unanswered or incorrect forum posts so once I get a solution I always ask the question I was stuck with and post my own answer 🙂

 

I actually meant to edit the code I posted, but apparently you can't edit posts...

 

So here's how I got it working.

 

With this svg markup:

<svg id="root" viewport-fill="fb-black">
  <svg id="targeting" x="2%" y="2%" width="96%" height="60%">
      <g id="leftLine" pointer-events="visible" transform="translate(5%,0%)">
        <line x1="0" y1="0" x2="0" y2="100%" fill="red" stroke-width="4"/>
      </g>
      <g id="rightLine" pointer-events="visible" transform="translate(97%,0%)">
        <line x1="0" y1="0" x2="0" y2="100%" fill="red" stroke-width="4"/>
      </g>
    </svg>
</svg>

 

I managed to get leftLine and rightLine to move relative to their parent like this: 

  let targeting = document.getElementById("targeting");
  let leftLine = document.getElementById("leftLine");
  let rightLine = document.getElementById("rightLine");  
  let bbox = targeting.getBBox();
  let halfTargetWidth = bbox.width / 2;
  let propOfMinute = today.getSeconds() / 60;
  leftLine.groupTransform.translate.x = halfTargetWidth * propOfMinute;
  rightLine.groupTransform.translate.x = bbox.width - (halfTargetWidth * propOfMinute);  

 

View best answer in original post

Best Answer
2 REPLIES 2

Sorry that you had this experience. We're still aiming to resolve this discrepancy between the SVG and JS attributes, but unfortunately it wasn't a quick fix.

Best Answer
0 Votes

No worries - thanks for getting back to me...

 

I really just wanted to make sure that whoever came along next after me would have more clues than I did, very few things are more frustrating than unanswered or incorrect forum posts so once I get a solution I always ask the question I was stuck with and post my own answer 🙂

 

I actually meant to edit the code I posted, but apparently you can't edit posts...

 

So here's how I got it working.

 

With this svg markup:

<svg id="root" viewport-fill="fb-black">
  <svg id="targeting" x="2%" y="2%" width="96%" height="60%">
      <g id="leftLine" pointer-events="visible" transform="translate(5%,0%)">
        <line x1="0" y1="0" x2="0" y2="100%" fill="red" stroke-width="4"/>
      </g>
      <g id="rightLine" pointer-events="visible" transform="translate(97%,0%)">
        <line x1="0" y1="0" x2="0" y2="100%" fill="red" stroke-width="4"/>
      </g>
    </svg>
</svg>

 

I managed to get leftLine and rightLine to move relative to their parent like this: 

  let targeting = document.getElementById("targeting");
  let leftLine = document.getElementById("leftLine");
  let rightLine = document.getElementById("rightLine");  
  let bbox = targeting.getBBox();
  let halfTargetWidth = bbox.width / 2;
  let propOfMinute = today.getSeconds() / 60;
  leftLine.groupTransform.translate.x = halfTargetWidth * propOfMinute;
  rightLine.groupTransform.translate.x = bbox.width - (halfTargetWidth * propOfMinute);  

 

Best Answer