Using Glows to Change Image Color in Flex

In a few cases in our application, we've been using buttons that consist primarily of an image that must then change colors for hover and down/selected states. For each of these, we initially embedded the images for the three states into the application and referred to them from there. The trouble is that this means a bit of bloat in the app itself. Not much for small buttons, more for larger buttons, but regardless if the space is unnecessary, then there's no need to have it there. Flex applications can get relatively large (the main module of our app is around 700K at the moment), and it's always nice to save some space. With that in mind, how can we include fewer assets in these cases?

In our case, we decided to use a Glow effect. The Glow effect provides a certain color tint to a given component. By default, it's an external glow, meaning there is some color emanating from the borders of that component. However, there are a few properties of the effect that we can use to our great advantage:
  • The inner property can be used to switch the glow from an effect that emanates from the borders outwards to one that emanates inwards and over the component.
  • The blurX and blurY properties can be used, at least on relatively small components, to make the glow fill the entire image with the color. Usually the glow color fades, but if these two properties are big enough then the color spreads into the entire image at the same intensity.

A downside of the filter is that, in order to actually modify the color it is glowing with, you have to remove it from the list of a component's filters and then add it back in. Simply changing the color property is not enough.

For all the discussion about rollovers above, the biggest win of using a glow to change colors is the ability to change an image with a color transition. In our case, we have a thumbs up image that changes colors when a user is rated up. The image has a color transition from green to white and then back. Usually, this would be relatively difficult to achieve; with the glow effect, however, we can change the color of the image without needing any additional embedded assets.

Here is and adaptation of that code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
           xmlns:effects="com.darronschall.effects.*"
           creationComplete="init()">
    <mx:Script>
        <![CDATA[
            import mx.events.EffectEvent;

            [Embed('assets/images/thumbs_up.png')]
            private static const HELPFUL_THUMBS_UP:Class;
            private static const THUMBS_UP_DEFAULT_COLOR:Number = 0xC3DA6E;

            [Bindable]
            public var helpfulImageColor:Number = THUMBS_UP_DEFAULT_COLOR;

            public function highlight(newColor:Number = NaN):void
            {
                if (! isNaN(newColor))
                {
                    helpfulImageColor = newColor;
                    reloadHelpfulImageGlow();
                    ratingLabel.setStyle("color", newColor);
                }
            }

            public function fadeHighlight():void
            {
                highlightFader.play();
            }

            /**
             * In order for a glow to have its color changed, it needs to be
             * removed and re-applied. This function does that for the helpful
             * image glow filter.
             */
            private function reloadHelpfulImageGlow():void
            {
                helpfulImage.filters = null;
                helpfulImage.filters = [helpfulImageGlow];
            }
        ]]>
    </mx:Script>
    <mx:Parallel id="highlightFader" duration="2000">
        <effects:AnimateColor target="{ratingLabel}"
                              property="color" isStyle="true"
                              toValue="{THUMBS_UP_DEFAULT_COLOR}" />
        <effects:AnimateColor target="{this}"
                              property="helpfulImageColor"
                              toValue="{THUMBS_UP_DEFAULT_COLOR}"
                              tweenUpdate="reloadHelpfulImageGlow();" />
    </mx:Parallel>

    <mx:GlowFilter id="helpfulImageGlow"
                   color="{helpfulImageColor}"
                   blurX="20" blurY="20"
                   alpha="1"
                   inner="true" />

    <mx:VBox styleName="ratingWrapper" height="24">
        <mx:Label id="ratingLabel" styleName="ratingLabel"
                  width="100%" height="8"
                  text="{data.rating}" />
        <mx:Image id="helpfulImage" styleName="helpfulImage"
                  height="10"
                  scaleContent="true"
                  source="{HELPFUL_THUMBS_UP}"
                  filters="{[helpfulImageGlow]}" />
    </mx:VBox>
</mx:Canvas>

Here, we given the color animation a tweenUpdate function that reloads the glow filter on the image. As I mentioned above, this happens because the filter is not reapplied unless you remove it and re-add it. Simply changing the color property of the filter, as the AnimateColor effect does, does not force that refresh. Other than that most of it is pretty self-explanatory. We have a bindable color that is updated continuously by the AnimateColor effect, and we have a parallel effect that updates the label that accompanies it. The component that contains this in our application changes its own background color at the same time, thus making this color change still result in a legible rating.

All in all, glow filters are more useful than simply applying a glow. This technique can be a real keeper for color transitions on images.