Transitioning from the “Now Playing” screen to the mini-player
Everything looks okay. So we can hide the mini-player, for now.
Mini_Player.opacity = 0
We use opacity
because we’ll want to animate it.
But we also don’t want it to be tapped by accident when the user swipes down the “Now Playing” screen, so we also switch off its visible
.
Mini_Player.visible = no
Later we’ll need to know when the mini-player is in use (you’ll see why). We create a variable for that, miniPlayerActive
, which at this point will still be ‘no
’.
miniPlayerActive = no
Listening to the scroll movement
To know when the user has dragged down the “Now Playing” screen we’ll listen to its onScrollEnd
event. This event gets triggered the moment the user stops scrolling.
scroll_now_playing.onScrollEnd ->
Now we need to check if the user has scrolled down far enough. If they haven’t, we’ll just let the ScrollComponent bounce back.
In the original app, the user has to drag the “Now Playing” screen 121 points or more from the top of the screen to have it transition to the mini-player.
The “Now Playing” screen is already placed 33 points from the top, so we’ll start our animation when the user has scrolled down 88
points.
But since we’re scrolling down, actually beyond the edge of the content, we’re checking for a negative value of scrollY
, the scroll distance.
(You can put a print maxi_player.scrollY
in the event handler to test this.)
scroll_now_playing.onScrollEnd ->
if scroll_now_playing.scrollY < -88
Freezing the scroll position
Now, when the user has scrolled this far down we can start the transition, but we face a problem: the “Now Playing” screen will be in a “scrolled down” state.
We’ll solve this by quickly resetting the ScrollComponent to its initial state, like this:
Before starting the animations, we’ll move the ScrollComponent down while moving its content up. We do it instantly, without animation.
Okay, step by step:
# The ScrollComponent jumps to its content’s position
scroll_now_playing.y = scroll_now_playing.content.y + 33
(Note that we’re not using scrollY
here, but the content
layer’s y
position, which increases when scrolling down.)
So no matter how far the user has scrolled down, our ScrollComponent will always end up in the correct place.
Now we move the content back to the top:
# … and we reset the content to its initial position
scroll_now_playing.scrollToPoint
y: 0
no
The scrollToPoint()
function does what it says: it lets you scroll to a certain point. By setting its ‘animate’ argument to no
, this will happen instantly, without animation.
All together it should look like this:
scroll_now_playing.onScrollEnd ->
if scroll_now_playing.scrollY < -88
scroll_now_playing.y = scroll_now_playing.content.y + 33
scroll_now_playing.scrollToPoint
y: 0
no
Please try it. You can scroll up and down all you want, but once you pull down far enough, it will stay at the spot where you released it.
Now get ready. We’ll have nine animations, with different timings, all run at the same time.
First set of animations
The first set of six animations starts immediately, and the duration for all of them will be a third of a second.
# - First set of animations: a third of a second - #
firstSetDuration = 0.3
In 0.3
seconds we’ll:
- Show the mini-player (
opacity
) - Hide the transparent gray overlay behind it (
opacity
) - Reset the “Library” screen in the back (
scaleX
,y
,borderRadius
) … - … and do the same for the “For You” screen
- Make the Status Bar black again (
invert
) - And move the Tab Bar up (
y
)
Here we go.
Showing the mini-player: We make it visible
again and animate its opacity
back to 1
.
Mini_Player.visible = yes
Mini_Player.animate
opacity: 1
options:
time: firstSetDuration
Hide the transparent gray overlay by animating its opacity
to zero.
overlay.animate
opacity: 0
options:
time: firstSetDuration
Next, we move scroll_library
and scroll_for_you
back to the top of the screen, reset their horizontal scale, and remove their border radius.
scroll_library.animate
scaleX: 1
y: 0
borderRadius: 0
options:
time: firstSetDuration
scroll_for_you.animate
scaleX: 1
y: 0
borderRadius: 0
options:
time: firstSetDuration
(Initially, we only changed scroll_library
, but after use of the prototype either one of them might be in the background.)
Earlier we made the Status Bar white by changing its invert
; we now dial it back to the default value: 0
.
$.Status_Bar.animate
invert: 0
options:
time: firstSetDuration
By setting the Tab Bar’s bottom, maxY
, to the bottom of the screen, it will slide back up.
$.Tabs.animate
maxY: Screen.height
options:
time: firstSetDuration
Because we used a variable, firstSetDuration
, to set the duration for these animations, we can slow all of them down to better observe what’s happening.
Like having them animate with a duration of 3 seconds:
# - First set of animations: a third of a second - #
firstSetDuration = 0.3 * 10
Second set of animations
The next two animations also start immediately but are slower, and they have a subtle bounce.
# - Second set of animations: 0.7 seconds - #
secondSetDuration = 0.7
In 0.7
seconds we’ll:
- Move the whole “Now Playing” screen (which includes the mini-player) downwards (
y
,borderRadius
) - Make the album cover fit on the mini-player (a state animation)
We don’t want to animate everything off-screen because the mini-player should still be visible, so we move the top of the “Now Playing” screen to the height of the Tab Bar + the height of the mini-player.
scroll_now_playing.animate
y: Screen.height - $.Tabs.height - Mini_Player.height + 1
borderRadius:
topLeft: 0
topRight: 0
options:
time: secondSetDuration
curve: Spring(damping: 0.77)
(Apparently, we need to add 1
extra point to not have a small gap appear.)
We also get rid of the border radius because otherwise, we would have a mini-player with rounded corners.
The added Spring
curve is only slightly bouncy, with a 0.77
damping
instead of the default 0.5.
And we use this same spring curve when shrinking $.Album_Cover
to its ”mini”
state:
$.Album_Cover.animate "mini",
time: secondSetDuration
curve: Spring(damping: 0.77)
We did not include animation options when creating ”mini”
(as we did for the ”playing”
and ”paused”
states), but we can add the desired duration and curve here.
Here’s a video of all eight animations at a tenth of their speed:
Last animation: Hiding the “Now Playing” screen
This last animation starts 0.5
seconds later because we want to be sure that the mini-player is in place before fading out the screen underneath it.
$.Now_Playing.animate
opacity: 0
options:
delay: 0.5
time: 0.5
(Here we’re animating the opacity of the $.Now_Playing
Sketch layer that’s inside our ScrollComponent.)
Background Blur
Now that you can see the mini-player’s transparency you’ll notice something is missing: Background Blur. Whatever is underneath the mini-player should be blurred.
Go back to Design, select the mini-player, give it a Blur of 25
, and then change this blur from Layer to Background.
Ah, and now that we switched to the mini-player we can also “flip the switch”:
# The mini-player is now active
miniPlayerActive = yes