How to add animation by HTML/CSS scripts in Perspective?

Hi, i am currently having trouble with adding a animated script to my Ignition project. I want to try to add this html script from openbridge of an Azimuth propeller indicator. To my HMI/GUI on ignition, how do i do this please help.

image

<html lang="en" theme="day"> <!-- set theme="day for day pallett -->
<head>
    <script src="https://unpkg.com/openbridge-web-components@0.2.1"></script> <!--import web components-->
    <link rel="stylesheet" href="https://unpkg.com/openbridge-css@0.2.1/dist/css/openbridge.css"> <!--import openbridge css-->
</head>
<body style="background-color: var(--container-background-color);">
    <ob-azimuth-large target="90" value = "80" angle = "45" targetAngle="60" id="azimuth"/>
<script>
    const azimuth = document.getElementById("azimuth"); <!-- Get the azimuth element -->
    setInterval(() => {
        const t = new Date().getTime() / 1000;
        azimuth.value = Math.sin(t*(2*Math.PI)* .1)*90; <!-- Set the power -->
        azimuth.angle = t*10 % 360; <!-- Set the angle -->
    }, 10)

</script>
</body>
</html>

you can do this sort of animations just in css alone too...

but if you want to import the component then you can try the markdown component. This is very suboptimal though, if you plan on making a screen with a lot of these indicators, then you should do something different. (like creating a custom perspective component)

i've made some posts about js injection.
this one might be interesting

some more links in here

2 Likes

I would recommend this approach. In the current Ignition Ecosystem this is going to be easier to support, and easier for others to follow.

This could be done easily with an SVG and some expression bindings.

3 Likes

Okay thanks for the input, i will try my best! pretty new to CSS, tried doing something to my project and ruined the whole thing.. how do rollback to get an older version of my project. I saw on an old forum that you could click on File>Rollback but i think this was an older version of Ignition. Any ideas?

If you have saved the view, there is no way that I am aware of to get it back, unless you happen to have a backup from before the changes were made.

This post might be interesting to you. Illustrates how you might go about making this type of thing. There are some links to other resources at the top of the thread as well.

1 Like

How would i add a SVG and some expression bindings, the same way as you replied earlier? The "Perspective, faster page"

I'd recommend setting up automatic gateway backups for this very situation. You can set it up from the gw webpage. I set it to every hr with 48 files for dev systems

1 Like

Thanks! Where in the gateway webpage?

Config, Gateway settings from memory (one of the top nav items). You can check in the user manual

1 Like

Do you have any idea of how you would add the SVG components from Openbridge from above?

I would simply import it as an SVG and bind to the rotation and whatever else to expressions/tags, as others have mentioned. You'll regret trying to inject js, and others who work on it will say bad words :laughing:

2 Likes

That post details all of the steps required to import and bind an SVG.

With simple SVGs I will import a blank and then add the elements as I need them. For more complicated I develop the different parts in a third party software such as ink-scape and then import them.

I’m riding in a car today, I’ll see what I can come up with.

1 Like

Use Git. Commit early and commit often. Commit chunks of work. Give your commits meaningful names. Make your experiments separate commits. Even better - make them separate branches.

There are no other reasonable answers to this question.

Ignition and Git is getting there... couldn't have made this suggestion not too long ago.

2 Likes

Okay, so it took me longer than I wanted (my laptop died), but I finally did get around to completing this, and I have to say I'm pretty pleased with the result. This was a fun exercise and I learned something good along the way. I will give you a disclaimer though, other than a rough idea of what an Azimuth Propeller is, I don't know much else. So for instance, I have no idea what the yellow triangle is supposed to indicate and so I didn't add it. I made some assumptions on how I think the rest should work.

I am glad to help you work out any kinks as needed, though my response may be delayed as I am on vacation.

There are bindings so that gauge rotates with the angle, and the blue section will expand with the power.

I added two custom properties to the svg to hold the power and angle. I also provided text labels for these inside of the svg, but that is not really needed, you could easily use labels if you wanted, but this way the text will scale with the gauge.

This was all done inside of Ignition as the shapes are (IMO) simple enough.

Originally when I set out to do this, I had a complex script transform on a path element that calculated the points needed to draw the gauge needle, and that worked well, but depending on one's proficiency with trigonometry could be hard to follow, especially considering the inverted y-axis of the graphic space. Any way I discovered a CSS trick that makes this much, much, simpler.

What is needed is to insure that any elements of the svg that need to be rotated are nested inside of a group, then you can apply a CSS transform to the group and viola all the crazy calculations are done for you! (This must be what @victordcq feels like. :wink: )

Preformatted Style

transform-box: fill-box
transform-origin: center
transform: rotate(45deg)

image

I had to provide a different origin due to the center of the group being different than the center I wanted to rotate around.

The binding on the transform is simply a property binding with an expression transform that inserts the angle into the rotate().

Here is the JSON for the view, I did all of this inside of a coordinate container but it will work in any type of container. You may just need to paste this into a test view first and then copy the SVG.

JSON
{
  "custom": {},
  "params": {},
  "props": {},
  "root": {
    "children": [
      {
        "custom": {
          "angle": 23,
          "power": 60
        },
        "meta": {
          "name": "blank"
        },
        "position": {
          "height": 300,
          "width": 300,
          "x": 62,
          "y": 91
        },
        "propConfig": {
          "props.elements[0].elements[2].elements[1].elements[1].d": {
            "binding": {
              "config": {
                "path": "this.custom.power"
              },
              "transforms": [
                {
                  "code": "\tpower \u003d value/100.0 * 180\n\treturn \"M200,240 l0,-{0:.3f} a180,180 0 0,1 100,0 l0,{0:3f}z\".format(power)",
                  "type": "script"
                }
              ],
              "type": "property"
            }
          },
          "props.elements[0].elements[2].elements[1].elements[1].fill.paint": {
            "binding": {
              "config": {
                "path": "this.props.elements[0].elements[2].elements[1].elements[0].fill.paint"
              },
              "type": "property"
            }
          },
          "props.elements[0].elements[2].style.transform": {
            "binding": {
              "config": {
                "path": "this.custom.angle"
              },
              "transforms": [
                {
                  "expression": "\"rotate(\" + toString({value}) + \"deg)\" ",
                  "type": "expression"
                }
              ],
              "type": "property"
            }
          },
          "props.elements[0].elements[3].elements[1].text": {
            "binding": {
              "config": {
                "path": "this.custom.power"
              },
              "transforms": [
                {
                  "expression": "{value} + \u0027%\u0027",
                  "type": "expression"
                }
              ],
              "type": "property"
            }
          },
          "props.elements[0].elements[3].elements[3].text": {
            "binding": {
              "config": {
                "path": "this.custom.angle"
              },
              "transforms": [
                {
                  "formatType": "numeric",
                  "formatValue": "#°",
                  "type": "format"
                }
              ],
              "type": "property"
            }
          }
        },
        "props": {
          "elements": [
            {
              "elements": [
                {
                  "cx": 250,
                  "cy": 250,
                  "fill": {
                    "opacity": 0,
                    "paint": "#000000"
                  },
                  "r": 200,
                  "stroke": {
                    "paint": "#A1A1A1",
                    "width": 4
                  },
                  "type": "circle"
                },
                {
                  "elements": [
                    {
                      "stroke": {
                        "paint": "#a1a1a1"
                      },
                      "type": "line",
                      "x1": 250,
                      "x2": 250,
                      "y1": 50,
                      "y2": 120
                    },
                    {
                      "stroke": {
                        "paint": "#a1a1a1"
                      },
                      "type": "line",
                      "x1": 450,
                      "x2": 370,
                      "y1": 250,
                      "y2": 250
                    },
                    {
                      "stroke": {
                        "paint": "#a1a1a1"
                      },
                      "type": "line",
                      "x1": 250,
                      "x2": 250,
                      "y1": 450,
                      "y2": 370
                    },
                    {
                      "stroke": {
                        "paint": "#a1a1a1"
                      },
                      "type": "line",
                      "x1": 50,
                      "x2": 120,
                      "y1": 250,
                      "y2": 250
                    },
                    {
                      "stroke": {
                        "paint": "#a1a1a1"
                      },
                      "type": "line",
                      "x1": 391.4213331294189,
                      "x2": 334.8527998776513,
                      "y1": 108.57854994415476,
                      "y2": 165.14717239288217
                    },
                    {
                      "stroke": {
                        "paint": "#a1a1a1"
                      },
                      "type": "line",
                      "x1": 108.578549944,
                      "x2": 165.147172393,
                      "y1": 391.421333129,
                      "y2": 334.852799878
                    },
                    {
                      "stroke": {
                        "paint": "#a1a1a1"
                      },
                      "type": "line",
                      "x1": 108.578549944,
                      "x2": 165.147172393,
                      "y1": 108.57854994415476,
                      "y2": 165.147172393
                    },
                    {
                      "stroke": {
                        "paint": "#a1a1a1"
                      },
                      "type": "line",
                      "x1": 391.4213331294189,
                      "x2": 334.8527998776513,
                      "y1": 391.421333129,
                      "y2": 334.852799878
                    }
                  ],
                  "type": "group"
                },
                {
                  "elements": [
                    {
                      "d": "M250,250 m-50.0,3.06161699787e-15 l0.0,-180.0 a180,180 0 0,1 100.0,6.12323399574e-15 l-0.0,360.0 a180,180 0 0,1 -100.0,6.12323399574e-15z",
                      "fill": {
                        "paint": "#868686"
                      },
                      "type": "path"
                    },
                    {
                      "elements": [
                        {
                          "fill": {
                            "paint": "#0013FF"
                          },
                          "height": 20,
                          "type": "rect",
                          "width": 120,
                          "x": 190,
                          "y": 240
                        },
                        {
                          "fill": {},
                          "type": "path"
                        }
                      ],
                      "type": "group"
                    },
                    {
                      "fill": {
                        "paint": "#0013FF"
                      },
                      "points": "250,10 265,45 235,45",
                      "type": "polygon"
                    }
                  ],
                  "style": {
                    "classes": "",
                    "transform-box": "fill-box",
                    "transform-origin": "60px 240px"
                  },
                  "type": "group"
                },
                {
                  "elements": [
                    {
                      "fill": {
                        "paint": "#868686"
                      },
                      "style": {
                        "classes": "",
                        "fontSize": 35
                      },
                      "text": "Power:",
                      "type": "text",
                      "x": 15,
                      "y": 480
                    },
                    {
                      "fill": {
                        "paint": "#0013FF"
                      },
                      "style": {
                        "classes": "",
                        "fontSize": 35
                      },
                      "type": "text",
                      "x": 135,
                      "y": 482
                    },
                    {
                      "fill": {
                        "paint": "#868686"
                      },
                      "style": {
                        "classes": "",
                        "fontSize": 35
                      },
                      "text": "Angle:",
                      "type": "text",
                      "x": 318,
                      "y": 480
                    },
                    {
                      "fill": {
                        "paint": "#0013FF"
                      },
                      "style": {
                        "classes": "",
                        "fontSize": 35
                      },
                      "type": "text",
                      "x": 424,
                      "y": 482
                    }
                  ],
                  "type": "group"
                }
              ],
              "type": "group"
            }
          ],
          "viewBox": "0 0 500 500"
        },
        "type": "ia.shapes.svg"
      }
    ],
    "meta": {
      "name": "root"
    },
    "type": "ia.container.coord"
  }
}
12 Likes

Amazing! I think the yellow triangle indicates the desired setpoint. Is there a simple step by step guide on how to add the JSON script to the project? Tried copying and pasting but that did not work.

i think this is a shift + right click on the view paste json thing

image

3 Likes

Nice! the component is kind of laggy, is there any way to get the JSON component to animate smoothly?

Was not laggy for me, when I get to my computer I will post a clip of what it looks like for me.

1 Like

Okay, still not what I would call laggy, but I did find a bug and correct it.

The script transform on the binding on the Power indicator was too vague. I have modified it from what it was to the following:

def transform(self, value, quality, timestamp):
	power = value/100.0 * 170
	shape = 1
	if value < 0:
		shape = 0
		return "M200,260 l0,{0:.3f} a180,180 0 0,{1} 100,0 l0,{2:3f}z".format(-power,shape,power)
	elif value > 0:
		shape = 1
		return "M200,240 l0,{0:.3f} a180,180 0 0,{1} 100,0 l0,{2:3f}z".format(-power,shape,power)
	return ""

Here are two clips, the first is of the component in the designer, the second is the component in a browser.

InDesigner

InClient

5 Likes

Very nice!, I am using a real lever and Ignition as a gui to see the position and OPC UA as communication. I think it has to do with the "tag throughput rate (Scans/Second)" on my OPC UA tags that are not as fast as i want them to be making the component "laggy" and delayed. Trying to figure out if it has something to do with the Gateway or something. My tags are updating slower than a friend of mine who has approx. 15 scans/second, while i have 4. (Do not know if this is the reason.) So now the question is how to get the tags to update faster in realtime.