How to animate the color of a F7 icon efficiently, using opacity

I’ve gotten good help with these things on the forum before, so I try again.

I have a situation which I feel should be very simple, but that I just can’t get to work (even after following a lot of “AI” suggested red herrings). I’m working on trying to make the UI log viewer automatically reconnect if it loses the connection to OH for some reason (brief network outage for example). I wish to indicate that it is trying to reconnect while that is happening, and I want to do that without adding anything “extra” to the layout if possible.

I thus figured that I could make the “play button” in the log viewer blink until it managed to reconnect. This should normally be a rare occurrence, but it shows that “something is happening” - and you can abort the reconnect attempt by clicking the “stop button”.

This is all working nicely, except that I think that animating the icon color is a bit “jerky” at times. So, I read that animating color is very heavy for the browser, because it has to constantly repaint - and that by using opacity instead, the repainting can be avoided. Great, sound like the way to go.

The problem is that, to use opacity to change between colors, you must had to identical objects on top of each other, one with one color, the other with the other color, and then animate the opacity of the “top layer object”. And herin lies the problem.

Since the exact glyph used is dynamic, depending on the theme used, I don’t want to hard-code the character/glyph used. I’d also prefer to do as few changes as possible to the Vue template/HTML, so I was ideally seeking to use a CSS pseudo element (::after) to make the overlay. But, whatever I do, I can’t get this to work.

I only want the overlay to apply to the icon/glyph, not the surrounding background. I’ve tried everything I’ve found of blending and clipping, but it seems like it’s “impossible” to get the overlay to actually use the “text” of the icon to decide where to render.

This seems like it should be such a simple task that I can’t quite believe that I can’t get it to work. Still, I’m just not able to. It just shouldn’t be that hard to animate a font-based icon between two colors in a way that a browser can handle efficiently… what am I missing?

The filter property also doesn’t force a repaint. So, can you get your color switching by animating filter: hue-rotate(x) on a single glyph instead of wrestling with two?

That is absolutely worth a try if it doesn’t repaint :+1: I need it to alternate between white and a color, so hue probably won’t do much, but there must be some other filter that I can use. Perhaps I can get it working by combining hue(), brightness() and possibly saturate().

I’m still amazed that it seemingly is so hard to make a “cloned overlay object”.

Another question is how they managed to botch it so that you can change hue using the GPU, but changing color requires a repaint. I guess that is because there are so many “rules” applying to colors and how they are resolved, with functions, variables at all, that it has gotten to complicated to handle for the GPU. But, then they should at least give us a filter colorOverlay() or similar that works with simple RGB values so that the GPU can do it.

Argh, any combination I try of hue, brightness and saturation won’t let me “break out” of the even balance between the colors, so all I get is shades of gray. If I could just add “some color”, I could transform it further. I guess I will have to do something else, like setting the color for the icon itself, and then use brightness to turn it white.

I got it to work using

  animation color-pulse 1s cubic-bezier(0.25, 0, 0, 1) infinite alternate

  @keyframes color-pulse 
    0%
      filter: saturate(1) brightness(1)
    100%
      filter: saturate(0) brightness(15)

…only to realize that it’s a dead-end. Because the “normal” color of the icon is only white if the navbars are “filled”, if they aren’t, it’s orange. I don’t think I should make code that applies different animations based on the theme settings, it just gets too fragile.

A simple alternative might be to just animate the opacity of the icon, so that it fades back and forth to the background color. That should work regardless of theme…

What about just adding a ! or :counterclockwise_arrows_button: badge to the icon instead of changing the icon itself. There are already several places where MainUI does that so it’s consistent with the general theme. You could even make the badge blink with simple opacity without impacting the icon/button itself.

There are many possible things you can do, but I’m so tired of fighting with F7 (because in my experience, “everything” you do with CSS will break/collide with F7 under some circumstance) that I set out to not touch the layout itself. When you add something, other elements must move etc., which I’m sure F7 can find a way to break.

To add ! I must modify the “content” itself of the span/i itself, which I’m sure will bug, because F7 overrides this based on the theme.

Adding a badge to the icon I’m not sure how you do, do you mean to add it as a child/slot of the Icon? Or the F7-Link? How will it impact the layout?

If you want to have an icon in the badge then you have to add a f7-badge as a child of the link and you get into the layout issues it sounds like you are trying to avoid. However, just adding a badge with simple text only requires adding the badge attribute to the f7-link itself with the value of the attribute being what you want the text of the badge to be. Any extra layout that needs to be done will be out of your hands at that point (except for color which you can set with the badge-color attribute).

<f7-link
  icon-ios="f7:play_fill"
  icon-f7="play_fill"
  icon-md="material:play_arrow"
  :icon-color="stateConnected && stateProcessing ? 'gray' : ''"
  :badge="reconnecting ? '!' : ''"
  badge-color=yellow
...etc

The need for a color overlay filter is obvious, look what people have come up with as a “workaround”:

Using this, you can generate blue (#00f):

filter: brightness(0) saturate(100%) invert(10%) sepia(100%) saturate(7281%) hue-rotate(248deg) brightness(86%) contrast(144%);

You can “calculate” how to generate any color this way here:

It’s quite amazing how such glaring holes are allowed to exist in CSS. Imagine how much easier it would be to calculate filter: rgba(0, 0, ff, ff) than the above :head_shaking_horizontally:

That’s hilarious. It’s not even deterministic.

I think it is, because they transform to full black first. But, it doesn’t work to animate with the hue changes :laughing:

badge did nothing here, perhaps because it only contains an icon. But, icon-badge did “work”, but not in a way that I consider usable:

bilde

I have gone with animating the opacity, and I think that will do (the gif captures “normal condition” → connection lost → connection regained)

Connecting

ConnectingDark

This is a question because I don’t know: what’s wrong with using animated gifs?

Alternatively simply display a disconnected icon, so the status can be instantly glanced at without having to wait for the animation

I don’t know if it’s anything “wrong” with it - but I’m not sure I understand. The icon here is a glyph (a custom font character), not an image, so a GIF wouldn’t be an option without doing a lot of customization of F7-link and getting into all kind of layout issues that I’m trying to avoid. I’m pretty sure it would perform worse as well.

You don’t have to wait for the animation - the animation is only there to tell you that it’s trying to (re)connect. The fact that the connection is lost is visible immediately by the fact that the “log range” field turns red. There’s also a tooltip providing textual information.

This is the result of trying to cram functionality into a very limited (mobile-only) UI framework. It has to work on tiny mobile screens as well as on proper screens, so there’s not a lot of space available in the first place. Adding to that, F7 “assumes” that nobody wants to alter the layout it imposes, so it has almost no customization options - and if you override the CSS, you can expect it to do something that breaks it under different circumstances.

Is there ever a case where a reconnection is not attempted?

Yes, if you never connected in the first place. Or if you disconnected manually. In addition, I’m considering whether automatic reconnect should give up after some timeout or not.

How about color code the “log range”
Never connected / manually disconnected → grey
disconnected but retrying → yellow
disconnected - gave up trying → red
connected → green

It is already color coded in my PR, but the “connect procedure” (a timer that tries again and again) is separate from that. Yellow is already used for “paused”. But, it’s obviously possible to avoid animation if that’s a big goal.

To me, it never was - I think an animation is a good way to show that something is “in progress”, just like you have the typical spinning circle and whatnot while waiting for something to load.

In any case, this kind of discussion is probably better suited in the PR, this thread was only about how to achieve the effect, which has kind of been resolved: