Dynamic SVG graphics

Recently I have been using Scalable Vector Graphics (SVG) in my applications, in place of typical raster images or even text in some cases. SVG graphics are defined through code, as per the examples here: https://www.w3schools.com/graphics/svg_examples.asp

As the images are defined with code, you can CONCATENATE() strings of code together while including application variables, to make the images dynamic. In this way, you could have animated images which react to user input.

Another thing to note, is SVG graphics scale/resize losslessly (unlike raster images). So everything will always look crisp! Also, when properly optimized, the file-sizes are often smaller than raster images.

There is a trade-off, in that the device must decode and render the graphics. Older devices/computers may have a hard time rendering poorly optimized or complex SVG code. Also, older browsers do not support some SVG functionality.


Some examples:

Richer looking detail headers, and small filesize gradient backgrounds:

SVG thumbnails, without having to rely on external services like Image Placeholder:

Dynamic progress bars:

@morgan
@praveen

10 Likes

@Jonathon - love the idea - especially the “Dynamic progress bars”. As these are code, I am assuming you can use variables to change them dynamically, rather than needing a bunch of png files to simulate the changes…

Can you show how you created the progress bars inside appsheet? what went in which columns, and how do you call the SVG code? Where is the svg hosted?

I like the idea of not needing yet another service on top of appsheet+datasource. Not sure the drawbacks, but very interesting. Thanks for sharing.

I’ll leave it up to the community to come up with their own graphics, but I will share some code for a simple square ‘progress bar’ that should explain the concept.

You can experiment / follow along with the example by putting your SVG code in this site: https://www.w3schools.com/graphics/tryit.asp?filename=trysvg_rect


This code will define a simple rectangle outline:

<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <rect width="100%" height="100%" style="fill:none;stroke-width:3;stroke:black"/> 
</svg>

To this, we want to add a second rectangle which grows to represent progress. This second rectangle does not need an outline, just a solid fill. You can control the progress with the width style property:

<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <rect width="20%" height="100%" style="fill:black;"/> 
  <rect width="100%" height="100%" style="fill:none;stroke-width:3;stroke:black"/> 
</svg>

If you preface the code string with data:image/svg+xml;utf8,, it will render in the browser. As a test, copy and paste the below into your browser address bar and it should load:

data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <rect width="20%" height="100%" style="fill:black;"/> 
  <rect width="100%" height="100%" style="fill:none;stroke-width:3;stroke:black"/> 
</svg>

Thus, we have all the necessary pieces of the puzzle. In AppSheet, input the above string or ‘code’ into the app formula of an image column, bounded by quotes:

image

To make it dynamic, incorporate CONCATENATE() to alter the width value based on some progress metric. One thing to note here is, the CONCATENATE() formula will need to double quote all the SVG quotation marks. Something like this:

CONCATENATE("
    data:image/svg+xml;utf8,<svg xmlns=""http://www.w3.org/2000/svg"" width=""200"" height=""200"">
      <rect width=""",[YOUR LOGIC HERE],"%"" height=""100%"" style=""fill:black;""/> 
      <rect width=""100%"" height=""100%"" style=""fill:none;stroke-width:3;stroke:black""/> 
    </svg>
")

Note I didn’t test the above, so I may have messed up a quotation somewhere… but it should run.

4 Likes

Very neat @Jonathon. If this is appealing to our users, we could make this a lot easier by providing a suite of builtin SVG image functions. @morgan and @tony what do you think?

9 Likes

@Jonathon - thanks for sharing the knowledge! Really interesting approach!

WAY COOL!!! :smiley:

The “https://www.w3.org/2000/svg” is just the SVG namespace. Including the namespace lets the browser know how to decode the following xml code (i.e. that it is an SVG, and the version of SVG). There isn’t any risk of this going down - it’s not a host.

You can stick your SVG code in your database / google sheet if you want. For example:

image

In other words, there is no external dependencies beyond AppSheet / your existing database.

In a SQL database, there is no real limitation to how large the SVG can be. In Google Sheets, there is a 50,000 character limit in a cell, so your code would have to be less than that.

2 Likes

Love it even more. Will have to try that out. I am already using a lot of individual images to simulate dynamic graphics, while SVG would solve that. Thanks for sharing this approach.

If this would be better supported in Appsheet directly, that would be a powerful addition.

Hi @praveen!

I’m very interested in graphics that show progress so I think @Jonathon’s idea is great and I’d love to see AppSheet add builtin SVG image functions, as you indicated. One concern I have, however, is that the data that is being displayed graphically needs to be up-to-date. On a flashcard app I’m continuing to refine, I’ve had difficulty getting accurate progress statistics without syncing:

I wish AppSheet would allow us to designate a particular calculation (say, the count of a slice, for example) as “must be current” or “always calculate immediately.” I know that somewhere inside my AppSheet app there is always accurate information about what is in a particular slice and what is not, because the native navigation within the slice always works correctly and the number of records shown in the deck view is always correct. However, I’m not always able to access that information in realtime; the number of “records remaining” based on my count() expression is often inaccurate until the sync is complete.

One work around is to force the app to write the current count to my sheet before the “progress” is displayed:

However, this adds to the “sync queue” in the app – another problem I’ve been trying to ameliorate.

So, if some kind of “must be real time” tool could be given to developers (with the understanding that overuse can cause problems), I think that would make graphics of this sort all the more attractive. Or, if by default counts of slices could be made to always be accurate, that would solve the problem.

@Jonathon - I can see how easy it is to create dynamic images (like progress bars) this way. I created a nice and thin bar with a quick modification to your code example. Sweet. Did a bit of looking and could not find an easy example of the circular progress bars. All had html+css+JavaScript. Not clear to me how to create something like you showed above. Can you use CSS and javascript as well? Care to share a code snippet on how you did the circulate progress bars?

These links explain it pretty well:

https://codepen.io/xgad/post/svg-radial-progress-meters

1 Like

Thanks @Jonathon!

thanks for this tips, very helpful !
a question : how do you integrate the css & js code beside the svg code ?

@jerome_houix - I am messing around with that as well (and failing!). Trying to get a single line SVG of a radial progress chart. Have a linear progress bar working. Can’t seem to get the radial path working. Have example of in-line css with no script required, but keep getting syntax errors.

If anyone has an example radial path SVG with inline CSS that would be great. Otherwise, I will keep trying to track down the problem - but after a lot of reading - an html file with inline css looks possible (between the tags.

No need to integrate CSS /JS.


<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="grey" stroke-width="12" />
<circle cx="60" cy="60" r="54" fill="none" stroke="black" stroke-width="12" stroke-dasharray="339.292" stroke-dashoffset="-100" transform="rotate(-90 60 60)"/>
</svg>

In the above code, you want to replace the stroke-dasharray="…" variable with the INVERSE of your percentage metric, as a fraction of the 339.292.

As an example, if you want to represent 60%, your formula would calculate as:

(1-0.6)*339.292

2 Likes

Thanks @Jonathon! I now have a great starting point to play around with. Have not done and HTML/CSS work in many many years (and that was pretty crude), so it’s like a new toy! :smile:

Your example was great. No JS or CSS (styles done in-line). Super simple.
Next up is to add in the variables like you did at the start of this conversation.

data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120"> 
<circle cx="60" cy="60" r="54" fill="none" stroke="lightgrey" stroke-width="12" /> 
<circle cx="60" cy="60" r="54" fill="none" stroke="Navy" stroke-width="10" stroke-dasharray="339.292" stroke-dashoffset="-100" transform="rotate(-90 60 60)"/> 
<text fill="Navy" font-family="sans-serif" font-size="150%" x="40" y="60" >90%</text>
</svg>

learning…learning…learning…

2 Likes

Hey @Mike, it seems now we have a new toy to play with :smile:

1 Like

Yes @LeventK! I want to explore this further. Still not succeeding so decided i will create a separate SVG app so I can do some testing. Might see if I can share that as a “playground”. I see potential here and want to figure out the performance implications…

1 Like

Gotcha. May be we can work on that together @Mike. What do you think?

1 Like

Sure! I might slow you down, but could be fun. I will throw something together later and add you as an admin.

1 Like

I would be interested in collaborating as well if you are interested!

2 Likes