Figma SVGs and Property Editor

Hi everyone, I've spent some time in the past few days lurking in these forms and reading up on implementing SVGs in Ignition Perspective. I am very new to all of this, so I apologize in advance if there are things I do not understand or do not know.

I've been trying to implement some custom SVGs I made in Figma and make it so that I can animate them according to information they take in.

Here is an example of one of my SVGs. A simple gauge.

OEE

When I embed this in Perspective I get the following.

Screenshot 2024-06-17 083725

I want to be able to change the fill of this gauge. But it's not very clear to me how I would go about doing that. All I get is the D path and trying to edit that in the property editor has not been very giving.

Thank you in advance to anyone who can help me with this!

You have 2 svg elements, both of which are paths. The d property is the one containing the path "commands", and is the property you want to change in this case.

Learn a bit about how they work: Paths - SVG: Scalable Vector Graphics | MDN. More specifically, look at either Bezier Curves or Arcs (since this svg is probably using one of the two.)

Once you understand the math and which numbers have to change, bind to the d property with an expression. Do whatever calculations you need in the expression and return a string with all the path commands.

Thank you!

This was one of the options I'm thinking of taking. As it seems I might not have any other way of going about changing the path dynamically. I appreciate the reply and help :+1:

Because svgs from editors like figma or inkscape usually come with funky numbers, I usually just create the path from scratch with round numbers that are easier to work with.

1 Like

That is a great idea! I feel like I'm reading binary by looking at these paths :sweat_smile:

There is actually a full explanation about how to do this (completely within Ignition no third party software needed) here:

However, for the TL:DR (not really much of one really) :rofl:

In that post, I use a script transform, which works well, but the same can be done with an expression (all be it a little ugly). The advantage is, an expression will be more performant.

(I don't have you're full path, so this is the path from my previous post)

if({this.custom.value} = 100,
    //True Return default if value = 100
    "M0.7963 165.436 A150 150 0 1 1 299.2037 165.436 A10 10 0 1 1 " +
    "279.2567 164.4069 A130 130 0 1 0 20.7433 164.4039 A10 10 0 1 1 " +
    "0.7963 165.436",
    //False Return check if value = 0
    if( {this.custom.value} = 0,
        //True Return default if value = 0
        "M0.7963 165.436 A10 10 0 1 1 20.7433 164.4039 A10 10 0 1 1 0.7963 165.436",
        //False Return use calculated outside and inside stops
        toStr( 150 - 150 * cos(toRadians((200 * {this.custom.value} / 100) - 15))) + " " +
        toStr( 150 - 150 * sin(toRadians((200 * {this.custom.value} / 100) - 15))) + " A10 10 0 1 1 " +
        toStr( 150 - 130 * cos(toRadians((200 * {this.custom.value} / 100) - 15))) + " " +
        toStr( 150 - 130 * sin(toRadians((200 * {this.custom.value} / 100) - 15))) + " A130 130 0 " +
        if ( {this.custom.value} >= 95, "1 0 ", "0 0") +
        "20.7433 164.4039 A10 10 0 1 1 0.7963 165.436"
        )
    )

Honestly, that can be cleaned up significantly if you move the calculations out to properties of their own. For instance:

posRadians: toRadians((200 * {this.custom.value} / 100 ) - 15)
outsideStopX: 150 - 150 * cos({this.custom.posRadians})
outsideStopY: 150 - 150 * sin({this.custom.posRadians})
insideStopX: 150 - 130 * cos({this.custom.posRadians})
insideStopY: 150 - 130 * sin({this.custom.posRadians})

Then the main expression looks like:

if({this.custom.value} = 100,
    //True Return default if value = 100
    "M0.7963 165.436 A150 150 0 1 1 299.2037 165.436 A10 10 0 1 1 " +
    "279.2567 164.4069 A130 130 0 1 0 20.7433 164.4039 A10 10 0 1 1 " +
    "0.7963 165.436",
    //False Return check if value = 0
    if( {this.custom.value} = 0,
        //True Return default if value = 0
        "M0.7963 165.436 A10 10 0 1 1 20.7433 164.4039 A10 10 0 1 1 0.7963 165.436",
        //False Return use calculated outside and inside stops
        toStr({this.custom.outsideStopX}) + " " +
        toStr({this.custom.outsideStopY}) + " A10 10 0 1 1 " +
        toStr({this.custom.insideStopX}) + " " +
        toStr({this.custom.insideStopY}) + " A130 130 0 " +
        if ( {this.custom.value} >= 95, "1 0 ", "0 0") +
        "20.7433 164.4039 A10 10 0 1 1 0.7963 165.436"
        )
    )

Note that you need to determine which direction to "close" the final arc dependent upon the value. In my case the break over threshold was 95% yours may be different.

6 Likes

Thank you! I read your previous statement. I think I'm starting to understand all of it :sweat_smile:

I'm beginning to realize the extent of the work involved in animating SVGs like this in Perspective. It's certainly no small task. Especially thinking with the fact that I want to create much more complicated designs and SVGs

Again, thank you for your time!

The SVG syntax is among some of the most cryptic there is (it's nearly as bad as regex). I happen to do well with it because I have a back ground in G-code for CNC Machines. Anyway, it isn't linked in the other post, but I use the Pocket Guide to Writing SVG as a primary resource. It's very well done.

Often when the SVG's get more complex then using groups and applying transforms to the entire group can make things significantly easier.

This is another example that is a bit more complex but the animation is actually not that complicated.

3 Likes

Thank you!

I had a question about about the animation that you linked to. This might be a really silly question, and probably show my inexperience with all of this, but for that JSON file. Do you create that file in the Property Editor of your container?

Not a silly question at all.

That is the JSON you get when you "Shift + Right Click" on the view and select Copy JSON.

To use it:

  1. Create a Temp View to hold the JSON
  2. Copy the JSON from the post.
  3. "Shift + Right Click on the temp view in the Project Browser
  4. Select Paste JSON
1 Like

I found a pretty nice SVG gauge on Ignition Exchange. You can find it here:

It's easy to re-apply it as it is built. It's pretty tricky to customize it to add dynamic capability. I modified a version of it to use styles to animate the alarm condition flashing because I prefer to handle that part in CSS rather than an expression binding off a timer.

Anyway, I think Ashwin M did a great job on this gauge and thought it might be useful to you.

4 Likes

Regarding Figma SVG export to Perspective SVG embed... any strategies to flatten the element structure? Figma will turn any group, component (embedded view equivalent), auto layout (flex container equivalent), etc into its own nested object, which can make it difficult to find specific elements in the SVG structure if you want to bind them. Yes, you can do bindings for particular items within the JSONified embedded SVG structure in Perspective properties.

I'm curious how things will change in 8.3. We've heard of an integrated drawing tool, but I'm even more curious if there's a chance to have something like embedded views but natively SVG. Imagine assembling SVGs rather than just views with embeds, flex repeaters, or a SVG canvas component like view canvas. Dreamy.

2 Likes

Definitely the way to go. And if you need it to be dynamic, you can use bindings to modify the styling as well. The last example I posted uses a CSS transform to rotate.

3 Likes

If you want to make this Exchange resource go from CSS styles instead of a blink animation, here you go.

I used these styles in advanced style sheet

.psc-stroke-gray
{
	stroke: #CCCCCC
}

.psc-stroke-blink-red
{
	animation: blinkStrokeRed 1.3s linear infinite;
}

@keyframes blinkStrokeRed{
	0%, 50%{
		stroke: #FF0000
	}
	50%, 100%{
		stroke: #CCCCCC
	}
}

.psc-opacity-off
{
	opacity: 0
}

.psc-opacity-blink
{
	animation: blinkOpacity 1.3s linear infinite;
}

@keyframes blinkOpacity{
	0%, 50%{
		opacity: 1
	}
	50%, 100%{
		opacity: 0
	}
}

Then you just add the "classes" property in the right style locations to add the 2 blinking things. Also you want to unbinds the bindings to the blinker property and remove the blinker property to get away from the gateway scoped timer expression. Here's screenshots:


I don't have time at the moment to help anyone troubleshoot this but this is probably enough to go on.

2 Likes

This is awesome, thank you!

1 Like

I would love to have an extension of Perspective with Figma! I just cant imagine the work that would need to be involved to do something like that.

But to be able to easily design in Figma and then have it easily plug into Ignition would be awesome.