Apple Music » Animating the album cover

Animating the album cover

When the music is playing the album art should be full-size, just like it is now, and when paused the album cover shrinks (and also loses most of its shadow).

FYI: The shadow in the original app is actually more of a blurred version of the album cover, but since our cover is black we’ll keep it simple and use a shadow.

To animate between these two states we’ll use, of course, States.

But first, we have to set up a few things.


Later on, we’ll show this same album cover very small on the mini-player… and have the whole “Now Playing” screen disappear. That’s why we should lift the album cover layer out of its parent, and put it directly in the ScrollComponent.

That’s easily done though, with just one line:

$.Album_Cover.parent = scroll_now_playing.content

It’s now still in the ScrollComponent, but independently, as a sibling of the “Now Playing” screen. (And we didn’t even have to correct its position.)

Next, we need to get rid of the existing (static) shadow. I made it a separate group in the Sketch document, so you can just make this $.Album_Cover_shadow layer invisible.

$.Album_Cover_shadow.visible = no

Creating the “playing” and “paused” states

We can now define the states.

When the music is playing, the album cover should look like this:

  • It’s shown at its full 311 x 311 points — so its scale is 1
  • The shadow’s color is 40% black — ”rgba(0,0,0,0.4)”
  • The shadow is projected downwards — shadowY is 20 points
  • … but also outwards in all directions — a shadowSpread of 10 points
  • (There’s no shadowX)
  • The shadow’s blur is also high — 50 points of Gaussian blur
How the album cover should look with music playing

And when the music is paused, it should look like this:

  • The album is 249 x 249 — which makes for a scale of 0.8
  • The shadow is very light: only 10% black — ”rgba(0,0,0,0.1)”
  • A shadowY of 19
  • No shadowSpread
  • A shadowBlur of 37 points

(The shadow will actually end up being 20% smaller because of the scale change.)

How the album cover should look when the music is paused

To keep it simple we’ll call our states ”playing” and "paused”. We can define both of them at the same time:

$.Album_Cover.states =
        scale: 1
        shadowType: "outer"
        shadowColor: "rgba(0,0,0,0.4)"
        shadowY: 20
        shadowSpread: 10
        shadowBlur: 50
        frame: $.Album_Cover.frame
            time: 0.8
            curve: Spring(damping: 0.60)
        scale: 0.8
        shadowType: "outer"
        shadowColor: "rgba(0,0,0,0.1)"
        shadowY: 19
        shadowSpread: 0
        shadowBlur: 37
        frame: $.Album_Cover.frame
            time: 0.5

I’ve also included the original frame of the layer in each state. This is because, later on, we’ll add a third state for the mini-player in which we’ll change its position.

And I’ve also included animationOptions:

  • Animating to the ”playing” state takes 0.8 seconds, but it seems faster because it ends with a soft (dampened) bounce.
  • There’s no bounce when animating back to ”paused” (we use the default Bezier.ease curve), and that animation’s duration is 0.5 seconds.

To test the animations, we can stateCycle() between them with a tap on the album cover:

$.Album_Cover.onTap ->
    this.stateCycle "paused", "playing"

(By including their names the ”default” state will be ignored.)

Testing the states animation of the album cover

That looks okay.

You can delete the onTap() event for now because we’ll trigger these state animations with the starting and stopping of the music.

With stateSwitch() we can switch a layer to a certain state without animating it. We use this function to make ”paused” the initial state.

$.Album_Cover.stateSwitch "paused"

Animating between the states when the music starts and stops

Now, we could use onTap events on the “Play” and “Pause” buttons to trigger these animations, like this…

$.Button_Play.onTap ->
    $.Album_Cover.animate "playing"

…but later we’ll have two more buttons: the ones on the mini-player.

So we’ll do it differently. We’re going to listen to the audio player’s playing and pause events.

The audio player has a player object, which is the HTML5 audio element that actually plays the music. And apparently, we can add functions to it that will be run when an event occurs. You do this by creating a function in player with an on in front of the event name.

So playing and pause become onplaying and onpause.

# When the music started playing
audio.player.onplaying = ->
    $.Album_Cover.animate "playing"
# When the music is paused
audio.player.onpause = ->
    $.Album_Cover.animate "paused"
Download Framer project

The state animations are now triggered by the music