Having trouble applying multiple animated style classes to one object

I'm having trouble getting multiple animated style classes to apply properly to a single object (specifically tested on a simple rectangle svg graphic object and even just an empty flex container).

I have a few simple style classes for testing purposes. For the svg object I have one that changes the stroke color and another one for fill color. For the empty flex container, I have one for background color and one for border color.

I've tested this and the same behavior happened on both the svg object and the empty flex container. When both the style classes applied to an object are set to "Animated", one would always seem to override the other (even though each style class is manupulating a different style property). When I change one style class to non-animated and keep the other animated, it works fine and applies both properly (one static and one with animation), but when both are animated, the one earlier in the list doesn't get visually applied.

Is this a known limitation of animated style classes? I've gotten around this in some cases by using separate nested containers/objects which I applied individual style classes to separately, but this isn't ideal.

Thanks for any help and input!

1 Like

Does this vary by browser?

To add to this, are you testing this in the designer or the browser?

@David_Stone @pturmel
I just tried in designer, chrome browser, and edge browser. All have the same behavior.

.psc-TestBackgroundStyle {
  background-color: #FF0000;
}

@keyframes psc-TestBackgroundStyle_Animated-anim {
  0% {
    background-color: #FF0000;
  }
  100% {
  }
}
.psc-TestBackgroundStyle_Animated {
  animation-name: psc-TestBackgroundStyle_Animated-anim;
  animation-delay: 0s;
  animation-direction: alternate;
  animation-duration: 1s;
  animation-fill-mode: both;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

.psc-TestBorderStyle {
  border-color: #FFFF00;
}

@keyframes psc-TestBorderStyle_Animated-anim {
  0% {
    border-color: #FFFF00;
  }
  100% {
  }
}
.psc-TestBorderStyle_Animated {
  animation-name: psc-TestBorderStyle_Animated-anim;
  animation-delay: 0s;
  animation-direction: alternate;
  animation-duration: 1s;
  animation-fill-mode: both;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

[
  {
    "type": "ia.container.flex",
    "version": 0,
    "props": {
      "direction": "column",
      "style": {
        "classes": "TestBackgroundStyle_Animated TestBorderStyle",
        "borderStyle": "solid",
        "borderWidth": 10
      }
    },
    "meta": {
      "name": "FlexContainer_1"
    },
    "position": {
      "grow": 1,
      "shrink": 0,
      "basis": "10px"
    },
    "custom": {},
    "children": [
      {
        "type": "ia.display.label",
        "version": 0,
        "props": {
          "text": "Animated Background",
          "style": {
            "textAlign": "center"
          }
        },
        "meta": {
          "name": "Label"
        },
        "position": {
          "grow": 1,
          "shrink": 0
        },
        "custom": {}
      },
      {
        "type": "ia.display.label",
        "version": 0,
        "props": {
          "text": "Static Border",
          "style": {
            "textAlign": "center"
          }
        },
        "meta": {
          "name": "Label_0"
        },
        "position": {
          "grow": 1,
          "shrink": 0
        },
        "custom": {}
      }
    ]
  },
  {
    "type": "ia.container.flex",
    "version": 0,
    "props": {
      "direction": "column",
      "style": {
        "classes": "TestBackgroundStyle TestBorderStyle_Animated",
        "borderStyle": "solid",
        "borderWidth": 10
      }
    },
    "meta": {
      "name": "FlexContainer_2"
    },
    "position": {
      "grow": 1,
      "shrink": 0,
      "basis": "10px"
    },
    "custom": {},
    "children": [
      {
        "type": "ia.display.label",
        "version": 0,
        "props": {
          "text": "Static Background",
          "style": {
            "textAlign": "center"
          }
        },
        "meta": {
          "name": "Label_0"
        },
        "position": {
          "grow": 1,
          "shrink": 0
        },
        "custom": {}
      },
      {
        "type": "ia.display.label",
        "version": 0,
        "props": {
          "text": "Animated Border",
          "style": {
            "textAlign": "center"
          }
        },
        "meta": {
          "name": "Label"
        },
        "position": {
          "grow": 1,
          "shrink": 0
        },
        "custom": {}
      }
    ]
  },
  {
    "type": "ia.container.flex",
    "version": 0,
    "props": {
      "direction": "column",
      "style": {
        "classes": "TestBackgroundStyle_Animated TestBorderStyle_Animated",
        "borderStyle": "solid",
        "borderWidth": 10
      }
    },
    "meta": {
      "name": "FlexContainer_0"
    },
    "position": {
      "grow": 1,
      "shrink": 0,
      "basis": "10px"
    },
    "custom": {},
    "children": [
      {
        "type": "ia.display.label",
        "version": 0,
        "props": {
          "text": "Animated Background",
          "style": {
            "textAlign": "center"
          }
        },
        "meta": {
          "name": "Label_1"
        },
        "position": {
          "grow": 1,
          "shrink": 0
        },
        "custom": {}
      },
      {
        "type": "ia.display.label",
        "version": 0,
        "props": {
          "text": "Animated Border",
          "style": {
            "textAlign": "center"
          }
        },
        "meta": {
          "name": "Label"
        },
        "position": {
          "grow": 1,
          "shrink": 0
        },
        "custom": {}
      }
    ]
  }
]

Here is the code for my Style classes as well as the objects I was using to test this (just flex containers in this case). Two objects have one animated and one static style class applied to each where both get applied properly. The third object has both animated style classes and only the latter style class gets applied visually.

I don't believe that CSS allows you to apply multiple animation classes to the same element. You can however animate multiple properties of an element with a single class.

.psc-TestBackgroundStyle {
  background-color: #FF0000;
}

@keyframes psc-TestBackgroundStyle_Animated-anim {
  0% {
    background-color: #FF0000;
  }
  100% {
  }
}
.psc-TestBackgroundStyle_Animated {
  animation-name: psc-TestBackgroundStyle_Animated-anim;
  animation-delay: 0s;
  animation-direction: alternate;
  animation-duration: 1s;
  animation-fill-mode: both;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

.psc-TestBorderStyle {
  border-color: #FFFF00;
}

@keyframes psc-TestBorderStyle_Animated-anim {
  0% {
    border-color: #FFFF00;
  }
  100% {
  }
}
.psc-TestBorderStyle_Animated {
  animation-name: psc-TestBorderStyle_Animated-anim;
  animation-delay: 0s;
  animation-direction: alternate;
  animation-duration: 1s;
  animation-fill-mode: both;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}


.psc-TestCombinedStyle_Animated {
	animation-name: psc-TestBackgroundStyle_Animated-anim, psc-TestBorderStyle_Animated-anim;
	animation-delay: 0s;
	animation-direction: alternate;
	animation-duration: 1s;
	animation-fill-mode: both;
	animation-iteration-count: infinite;
	animation-timing-function: linear;
}

animation

You can even do multiple durations, if you want to. (Don't use this, its highly inappropriate even for the Non-HPHMI Evangelist, this is only for a high contrast example.)

.psc-TestCombinedStyle_Animated {
	animation-name: psc-TestBackgroundStyle_Animated-anim, psc-TestBorderStyle_Animated-anim;
	animation-delay: 0s;
	animation-direction: alternate;
	animation-duration: 0.1s, 1s;
	animation-fill-mode: both;
	animation-iteration-count: infinite;
	animation-timing-function: linear;
}

animationDur

Thanks for the input. This limitation is what I was afraid of. My application is for a conveyor system where I've got SVG objects for different conveyor beds (straight sections, curves, spurs, etc.). The border/stroke color of these shapes indicates the HOA status and the fill color indicates the running/faulted status of that conveyor. In Auto, the border/stroke is black. When Off or in Hand, the border would flash between black and Grey/Light Blue, respectively. This flash is to give a clear/eye-catching indication that a conveyor is not in Auto. For the Fill, conveyors would be Grey if Idle, Green if Running Forward, Yellow if Running Reverse, and Flashing Red if Faulted (once again, to be eye-catching).

I can always make separate style classes for each combination of animations, but it would obviously be more convenient to just be able to keep them separate.

I would keep them separate as far as the keyframes are concerned since that is the part that is actually needed to be dynamic.

I would then use a combination of the binEnc() and case() expressions in conjunction with custom properties and indirect tag bindings, to apply the animation styles on a 'per component' basis.

I know that sentence is a mouth full, however, consider a custom property, which is just a copy of the needed "style" object. You could write an expression along the lines of:

case(
    binEnc({Status.Hand},{Status.Off},{Status.Faulted}),
    // integer value, resultant style
    1, "FultedAnimation", // Idle and faulted condition
    2, "OffAnimation", // Off condtion
    3, "OffAnimation, FaultedAnimation", // Off and faulted condition
    4, "HandAnimation", // Hand Condtion
    5, "HandAnimation, FaultedAnimation", // Hand and faulted condition
    "default"
)

Obviously this can be expanded to other conditions with the addition of additional "bits".

Status would be a custom property with an expression structure to collect into a single object the status of the conveyors. There would be other custom properties which would be indirectly bound to the tags needed. This set up would allow for these properties to be copied and pasted across multiple components and the by modifying only a single property indicating the path to the appropriate conveyor ("presumably a UDT") have a working animation.

The only duplicated step for each component, would be to set up the style binding to the custom property which holds your animation definition.

Hopefully that was clear.

Forgive me for getting a little lost in your answer. Tbh, I'm really not familiar with CSS or keyframes or any of the stuff that runs these animations/styles behind the style classes gui.

Here's my understandings/lack thereof so far:
1: I can't apply multiple animated style classes to a single component. I believe you confirmed this

I don't believe that CSS allows you to apply multiple animation classes to the same element.

2: You say you can do multiple animations within the same style class. Is this possible within the Edit Style Class GUI? I can assign different properties in each stop of one animation, but I don't see how I can add a second animation with its own stops, duration, etc. Also, to clarify, is each of these "animations" within a single style class a uniquely defined "keystroke"? Or is my understanding of what a Keystroke is wrong?


If you can't use the GUI to define a style class with multiple keystrokes/animations, do you have to use the "Advanced StyleSheet" or do you do it some other way?

3: If I'm understanding you so far, you want me to make different keystrokes for each animation I need; one for off (flashing grey border), one for manual (flashing blue border), one for faulted (flashing red background). But how do you want me to apply those keystrokes to the object? The only way I understand right now is to create different style classes for each combination of those keystrokes I might need. The keystrokes can be somewhat global(?) (advanced stylesheet?) and used by the different style classes (off, manual, off_and_faulted, manual_and_faulted, faulted). Then I just decipher which of those 5 style classes I need within my expression binding on my component's style class property? Or is there another way to apply those keystrokes to my object individually outside of the style class binding?

I hope that all makes sense. There's definitely a lack of knowledge somewhere that's preventing me from fully understanding how you're suggesting I accomplish this because I feel like doing the following:

case(
    binEnc({Status.Hand},{Status.Off},{Status.Faulted}),
    // integer value, resultant style
    1, "FultedAnimation", // Idle and faulted condition
    2, "OffAnimation", // Off condtion
    3, "OffAnimation, FaultedAnimation", // Off and faulted condition
    4, "HandAnimation", // Hand Condtion
    5, "HandAnimation, FaultedAnimation", // Hand and faulted condition
    "default"
)

within the style class binding (essentially just applying multiple animated style classes) just does the same thing I did in my testing that didn't actually work.

Thanks for any and all help and clarification!

No problem let me see if I can expand on this to help your understanding.

That is correct, because of the way CSS works. If I have two classes:

.class1 {
  someProerty = someValue
}

.class2 {
  someProperty = someOtherValue
}

Because (unless you do something to denote it) the last class always take precedence, any time you have the same property defined in multiple classes, then that property will always take on the definition in the last defined class.

This is what is happening when you apply multiple animated style classes, they each define a different set of keyframes and so the last classes keyframes is what is used.

To my knowledge a style class can only have a single set of keyframes, so the only way to accomplish a single class which defines multiple sets of keyframes is with the advanced stylesheet.

The disconnect here, is that you are putting the binding inside of the style class (so basically a dynamic style definition). This allows you to dynamically choose which set of keyframes to use.

Here is an example of what I am suggesting.

  1. In your stylesheet define the set of keyframes that you need.
  @keyframes psc-TestBackgroundStyle_Manual {
  0% {
    background-color: #FF0000;
  }
  100% {
  }
}

@keyframes psc-TestBorderStyle_On {
  0% {
    border-color: #FFFF00;
  }
  100% {
  }
}

@keyframes psc-TestBorderStyle_Off {
	0% {
		border-color: #FFFFFF;
	}
	100% {
	}
}
  1. Create a custom object property that defines the properties you need for your animation and style.

  2. Create a custom object property to hold the current status of the device, typically this would mean having multiple tag bindings, but you're not forced into that.

  3. Create a binding on the custom style prop that will change the animation according to the needed logic. Here is the binding I used:

case(
	binEnc({this.custom.Status.Manual},{this.custom.Status.on},{this.custom.Status.off}),
	4, "psc-TestBackgroundStyle_Manual",
	5, "psc-TestBackgroundStyle_Manual, psc-TestBorderStyle_On",
	"psc-TestBackgroundStyle_Manual,psc-TestBorderStyle_Off"
)
  1. Bind the style property of your component to the custom property.

Result:
animation

Hopefully that helps explain a little how to accomplish your goal.

2 Likes