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

Any way to dynamically generate SVG elements?

For my app, the user will be creating an unknown number of exercises, and I need to be able to list those exercises. There doesn't seem to be a createElement()/createElementNS() method in the document object. So is the only solution here to hardcode a slew of hidden dummy elements in index.gui and to then manipulate them and their visibility?

Best Answer
12 REPLIES 12

There isn't, but we will have a new API in the next release which will help you! Look out for Tile List.

Best Answer

Cool. When can we expect that release? Days? Weeks? What other features will it include? I'd hate to waste my time writing hacky workarounds for existing API limitations that will soon have a proper built-in solution. Is there a publicly available roadmap somewhere?

Best Answer

Sorry I don't have any specific dates, and there isn't a public roadmap at this time.

 

That being said, we're trying to keep a regular schedule for the developer preview releases.

Best Answer
0 Votes


I'd hate to waste my time writing hacky workarounds for existing API limitations that will soon have a proper built-in solution.

It seems there is no documentation for tile list yet, but it is used in the Bart example: https://github.com/Fitbit/sdk-bart/blob/master/resources/index.gui. However, still used in a static way.

Probably if you try to use that you are already a bit prepared.

Best Answer
0 Votes

Any update on this? Is this possible yet? 

Best Answer
0 Votes

any news on this?

 

I have some json object list and want to show this list dynamically on the screen. I already tried "document.createElementNS" but will get an "Expected a function.", so this function seems to be not implemented here... how can i add some new list items to a "tile-list"?

Best Answer

We still need to provide the documentation for this, but we have a virtual tile list which you can use:

 

 

<!-- widgets.gui -->
    <link rel='import' href='/mnt/sysassets/widgets/tile_list_widget.gui' />

<!-- index.gui --> <svg> <!-- Create a tile-list-item that can be re-used, with a simple -- coloured background. -- Things to note: -- - display='none': Tile list items need to be hidden by default! --> <symbol id='colour-item' href='#tile-list-item' height='50' display='none'> <!-- We put this in a transform so that way the elements inside -- can be animated by the List container when shown / hidden. --> <g id='transform'> <rect id='bg' height='100%' width='100%' /> <text id='title-text' fill='white' y='35' x='5'></text> <!-- Visual separator between tiles --> <rect id='tile-divider-bottom' class='list-separator' /> </g> </symbol> <symbol id='separate-item' href='#tile-list-item' height='5' display='none'> <rect id='bg' height='100%' width='100%' fill='black' /> <rect id='tile-divider-bottom' class='list-separator' /> </symbol> <use id='my-tile-list' href='#tile-list'> <!-- We need to mark this as a VirtualTileList --> <var id='virtual' value='1' /> <!-- Each of our tiles have the 2px 'tile-divider-bottom'. -- In order to properly calculate offsets when scrolling, -- we need to tell the tile list how tall our separator is. --> <var id='separator-height-bottom' value='2'/> <!-- The item pools may each only contain a single type of -- tile, and each pool should contain at least enough tiles -- to fit on screen. For smoother scrolling, more are -- recommended. -- In this case, we can fit a maximum of 300/50 = 6 on screen -- so we will keep 10 tiles in each pool. --> <use id='colour-pool' href='#tile-list-pool'> <use id='colour-pool[1]' href='#colour-item' /> <use id='colour-pool[2]' href='#colour-item' /> <use id='colour-pool[3]' href='#colour-item' /> <use id='colour-pool[4]' href='#colour-item' /> <use id='colour-pool[5]' href='#colour-item' /> <use id='colour-pool[6]' href='#colour-item' /> <use id='colour-pool[7]' href='#colour-item' /> <use id='colour-pool[8]' href='#colour-item' /> <use id='colour-pool[9]' href='#colour-item' /> <use id='colour-pool[10]' href='#colour-item' /> <use id='colour-pool[11]' href='#colour-item' /> <use id='colour-pool[12]' href='#colour-item' /> <use id='colour-pool[13]' href='#colour-item' /> </use> <use id='separate-pool' href='#tile-list-pool'> <use id='separate-pool[1]' href='#separate-item' /> <use id='separate-pool[2]' href='#separate-item' /> <use id='separate-pool[3]' href='#separate-item' /> <use id='separate-pool[4]' href='#separate-item' /> <use id='separate-pool[5]' href='#separate-item' /> <use id='separate-pool[6]' href='#separate-item' /> <use id='separate-pool[7]' href='#separate-item' /> <use id='separate-pool[8]' href='#separate-item' /> <use id='separate-pool[9]' href='#separate-item' /> <use id='separate-pool[10]' href='#separate-item' /> </use> </use> </svg>

/* styles.css */
/* We separate list items with a 2px rect.
* Make sure to set
*/
.list-separator {
x: 0;
y: 100%-2;
width: 100%;
height: 2;
fill: fb-extra-dark-gray;
}

/* index.js */
import document from "document";

function toHexString(x) {
var str = x.toString(16);
while (str.length < 2) str = '0' + str;
return str;
}

// Adapted from
// https://stackoverflow.com/questions/17242144/javascript-convert-hsb-hsv-color-to-rgb-accurately
function HSVtoRGB(h, s, v) {
var r, g, b, i, f, p, q, t;
i = Math.floor(h * 6);
f = h * 6 - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
var hex = [r, g, b].map(function(z) {
return toHexString(Math.round(z * 255));
}).reduce(function(sum, value) {
return sum + value;
});
return "#" + hex;
}

var VTList = document.getElementById('my-tile-list');
var TILES_PER_SEPARATOR = Math.floor(Math.random() * 5) + 1;
var NUM_ELEMS = 100;
var SATURATION = (Math.random() % 0.5) + 0.5;
var VALUE = (Math.random() % 0.5) + 0.5;
var HUE_CONST = 1.0 / NUM_ELEMS;
var STARTING_POINT = Math.random() * NUM_ELEMS;

/****************************************************************************************************
* The delegate object is what allows for us to customize tiles in the virtual list.
* - .getTileInfo should return an object which contains a "type" field, specifying the id
* of the pool to get the tile from. Other than that, you can put any free-form data
* you want in there, and it will be passed in to configureTils.
* - .configureTile takes in the actual VirtualTileListItem, and the info returned from .getTileInfo.
* This is where you can custom-style the element. Make sure you are styling it correctly
* for the pool specified in the type field.
***************************************************************************************************/
VTList.delegate = {
getTileInfo : function(index) {
var tile_type = ((index+1) % TILES_PER_SEPARATOR == 0) ? "separate-pool" : "colour-pool";
return { type : tile_type,
color : HSVtoRGB(HUE_CONST * ((index + STARTING_POINT) % NUM_ELEMS),
SATURATION, VALUE),
index : index - Math.floor((index + 1) / TILES_PER_SEPARATOR), };
},
configureTile : function(tile, info) {
if (info.type == 'colour-pool') {
tile.getElementById('bg').style.fill = info.color;
tile.getElementById('title-text').text = "Tile #" + info.index;
}
},
};

// KNOWN ISSUE: It is currently required that VTList.length be set AFTER VTList.delegate
VTList.length = NUM_ELEMS;

 

 

Best Answer

Can you put this on your GitHub?

 

https://github.com/Fitbit?utf8=%E2%9C%93&q=&type=&language=

 

It would be nice to pull up the entire project in studio. 

Best Answer
0 Votes

Hi there! I'm having trouble making these virtual tiles respond to clicks/activates. Can you perhaps alter the same to include responding to a tile selection?

Best Answer
0 Votes

it seems like all this would be simpler if fitbit let in a bit more standard svg and javascript functionality. It's a bit insincere to pitch fitbit apps as simply js, svg, and css when what fitbit really provides is a severely and deliberately reduced subset of svg and js capability. 

 

I get it - it's a small display and you want to make sure developers don't create unstable or exploitative applications and you want to enforce a more fitbit look and feel, but there are better ways than actively making it more difficult for experienced developers to use the sdk.

Best Answer

This example, while it works flawlessly on my Versa, doesn't seem to work at all on the simulator.  All I get is a series of grey horizontal lines.  I also tried using the example downloaded from Dropbox, so there are no typing mistakes.

 

And when I tried enclosing the <symbol> tags in index.gui between <defs> tags (as shown on the web guides), the example on the watch behaved exactly as bad as on the simulator (10 horizontal grey lines).  This seems to show that the simulator isn't taking the <symbol> tags correctly.

 

Do you know why is this happening?  Thanks in advance.

 

 

Best Answer
0 Votes

I just found out what is happening.  There are two different but correlated issues:

 

1) The simulator (not the real device) doesn't really bring random numbers with Math.random() instruction, but a determinate series of numbers, just like when you forgot to put "randomize timer" in QBasic.  That makes the line

var TILES_PER_SEPARATOR = Math.floor(Math.random() * 5) + 1;

always yield the number 1.  That's when the second issue comes along.

 

2) The variable TILES_PER_SEPARATOR is a misnomer.  Looking at the line

var tile_type = ((index+1) % (TILES_PER_SEPARATOR) == 0) ? "separate-pool" : "colour-pool";

the variable should be called something like BEFORE_WHICH_TILE_SHOULD_COME_A_SEPARATOR.  Ugly name, i know.  But if this variable equals to 1, the function will insert a separator in every cycle of the loop.  And that is exactly what you get when you run the example on the simulator.

 

Luckily, the problem has an easy work-around.  You just have to add 1 to TILES_PER_SEPARATOR as in

var TILES_PER_SEPARATOR = Math.floor(Math.random() * 5) + 2;

 

---------------------------------------------

As a side note, I found out another bug in the example.  You define the variable NUM_ELEMS to set the number of tiles that you want to show, but the loop is counting both numbered and separator tiles.  That way, you will get to show the last numbered tile only if TILES_PER_SEPARATOR is greater than NUM_ELEMS.  To fix this bug, in the file index.js you would have to change 

VTList.length = NUM_ELEMS;

to

VTList.length = NUM_ELEMS + Math.floor(NUM_ELEMS / (TILES_PER_SEPARATOR - 1));

 

I hope that this information (obviously alongside the original example by JonFitBit) will help anyone trying to implement a VirtualTileList.

 

Bye!

Best Answer