Multiple Background Images & Animation

Multiple background images allow one to build up complex layered visual effects using one element. In the past such effects would require each background image rendered inside its own element, these being either nested or stacked on top of each other with positioning. When I was working on creating the progress bar example, I ran to a problem where I couldn’t seem to get a specific layer to animate. I could figure out what was going on. Recently, after looking at the code and fiddling with it, I found out what the problem was. Animation of background images works a little differently from other CSS properties. Because multiple backgrounds consists of multiple definitions, it works more like other properties with multiple property values: margins, borders, padding. For example, you can animate all borders on an element, otherwise you can animate a single border. Unfortunately, with multiple backgrounds there is no easy way to single out an individual background image for animation as you can with a border, margin or padding property.

Now the thing to bare in mind when dealing with multiple background images is the stacking order. The first image defined is the topmost image, and the last image defined is the bottommost. Same thing with animations for multiple background images. So, in the case of the animated progress bar, I wanted to animate the bottommost gradient image which consisted of white slanted bars. The rest of the progress bar was fine stable. Well, except that I used a single animation which would instead affect the topmost layer, making it impossible for me to get the stacking layer they way I wanted. Here is the gradient:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
background-color: rgb(56,138,213);
background-image:
    -webkit-gradient(linear, 18 0, 0 10,
        color-stop(0.23, rgba(255,255,255,0)),
        color-stop(0.3, rgba(255,255,255,0.8)),
        color-stop(0.3, rgba(255,255,255,1)),
        color-stop(0.7, rgba(255,255,255,1)),
        color-stop(0.7, rgba(255,255,255,0.8)),
        color-stop(0.77, rgba(255,255,255,0))),
    -webkit-gradient(linear, 0 0, 0 100%,
        color-stop(0, rgba(255,255,255,.8)),
        color-stop(0.45, rgba(255,255,255,.05)),
        color-stop(0.55, rgba(0,0,0,.05)),
        color-stop(0.85, rgba(0,0,0,.2)),
        color-stop(0.98, rgba(0,0,0,.5))),
    -webkit-gradient(linear, 0 0, 0 100%,
        color-stop(0.20, transparent),
        color-stop(0.20, rgba(255,255,255,.5)),
        color-stop(0.32, rgba(255,255,255,.5)),
        color-stop(0.32, transparent));

To animate this, I was using a key-frame animation that moved the x axis of the background gradient across the element from 0 to 100% in a loop:

1
2
3
4
@-webkit-keyframes progressBarAnim {
    0% { background-position-x:  0%; }
    100% { background-position-x: 100%; }
}

If you ran my previous example of the progress bar, it animates as you would expect, except one problem. The topmost gradient layer is the part that should be on the bottom. Yet every time I positioned it to where I wanted it, it wouldn’t animate. In my mind I was thinking that the key frame animation defined above would be animating all the background images. Actually, it doesn’t. It only animates the topmost image background, which is why I had to have the part I wanted to animate on top. Unfortunately I didn’t figure this out until recently. After a lot of fiddling and digging into what was going on with animation of multiple background images, I finally sorted it all out. When defining animations you have to start from the top and work you way down. You don’t have to include an animation for every background image, only down to the layer you need to. But you do have to include all the layers above the animated one up to the topmost one. So here’s my new stacking order for the CSS the way I actually wanted it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
background-color: rgb(56,138,213);
background-image:
    -webkit-gradient(linear, 0 0, 0 100%,
        color-stop(0, rgba(255,255,255,.8)),
        color-stop(0.45, rgba(255,255,255,.05)),
        color-stop(0.55, rgba(0,0,0,.05)),
        color-stop(0.85, rgba(0,0,0,.2)),
        color-stop(0.98, rgba(0,0,0,.5))),
    -webkit-gradient(linear, 0 0, 0 100%,
        color-stop(0.20, transparent),
        color-stop(0.20, rgba(255,255,255,.5)),
        color-stop(0.32, rgba(255,255,255,.5)),
        color-stop(0.32, transparent)),
    -webkit-gradient(linear, 18 0, 0 10,
        color-stop(0.23, rgba(255,255,255,0)),
        color-stop(0.3, rgba(255,255,255,0.8)),
        color-stop(0.3, rgba(255,255,255,1)),
        color-stop(0.7, rgba(255,255,255,1)),
        color-stop(0.7, rgba(255,255,255,0.8)),
        color-stop(0.77, rgba(255,255,255,0)));

And here’s the key frame animation, notice the extra animations for the upper layers:

1
2
3
4
@-webkit-keyframes progressBarAnim {
    0% { background-position-x:  0%, 0%, 0%; }
    100% { background-position-x: 0%, 0%, 100%; }
}

So remember, in the above key frame animation the first value if for the topmost layer and the last value is for the bottom most layer.

To show the difference in the stacking order, I changed the white stripe that goes horizontally across the progress bar to black. In the wrong stacking order version you can see that its actually behind the white stripes. The corrected version with multiple background animations has it correctly on top:

 

progress bar with incorrect stacking order

progress bar with incorrect stacking order

 

progress bar with correct stacking order

progress bar with correct stacking order

 

Of course, the final product would not be a black streak, it would be white. This was just to show how the stacking order was previously wrong.

Advertisements

Leave a comment