The cool factor of Cover Flow is in its fluid animation of the covers in the 3-D space. If you haven’t seen it before, you miss a lot! To get the feeling of the effect, use your browser (preferably on a smartphone or a tablet) to visit ariya.github.io/kinetic/5. Swipe left and right to scroll through the cover images of some audiobooks.
My first encounter to this Cover Flow effect was 6 years ago as I implemented this with pure software in C/C++. It ran quite well even on a lowly hardware (such as this 200 MHz HTC Touch). Of course, with GPU-accelerated CSS animation, there is no need to write an optimized special renderer for this. For this example, we manipulate the transformation matrix directly using CSS Transform feature. To follow along, check the main code in coverflow.js.
There are several key elements in this demo. First, we will need a smooth scrolling with the right acceleration and deceleration using the exponential decay technique which was covered in Part 2. Also, stopping the scrolling at the right position is necessary (Part 3, see the snap-to-grid code) as we need to have all the cover images in the perfect arrangement, not halfway and not being stuck somewhere.
When you look at the code, the scrolling via mouse or touch events impacts the value of the variable
In fact, the code for doing that is line-by-line equivalent to what has
been covered in the previous parts. On a normal circumstances, this
offset is always an integer multiply of 200 (the cover image size, in
pixels). When the user makes a touch gesture to drag the cover or when
it is decelerating by itself, then the value could be anything. This is
where we need to apply tweening to give the right transformation matrix for each cover image.
Such a decoupling results in a modular implementation. All the events
(touch, mouse, keyboard, timer) only deal with the change in offset, it
has no knowledge as to how the value will be useful. The main renderer,
which is the
scroll() function, knows nothing on how that
offset value is computed. Its responsibility is very limited: given the
offset, compute the right matrix for every single cover. This is carried
out by going a loop through the stack of the covers, from the
front-most image to the ones in the far background, as illustrated in
this diagram. Note how the loop also serves to set the proper z-index so that the center image becomes the front cover and so on.
Every image is initially centered on the screen. After that, the right translation is in the X axis is computed based on the relative position of the image. For a depth perception, there will be an additional translation in the Z axis, making the cover going farther from the screen. Finally the image is rotated (in the Y axis) with a positive angle and a negative angle for the left and right side, respectively. The 3-D visual comes from the perspective of the main div element which is the parent of every single image element.
scroll() is about 40 lines. If the
explanation above is still confusing, it is always fun to step through
the code and watch the variables as it goes through one cover to
another. The code is not crazily optimized, it is deliberately chosen to
be as clear as possible without revealing too much. Having said that,
with the most recent iOS or Android phones, the effect should be pretty
smooth and there is not any problem achieving over 30 fps most of the
time. You will get some minor extra frames (particularly useful for
older devices) if the reflection effect is removed.
Since this demo is intended to be educational, I left a few exercises
for the brave readers. For example, the tweening causes only one cover to move at a time. If you are up for a challenge, see if you can move two
covers, one that is going to the front and one that is going to the
back. In addition, the perspective here is rather a cheat since it
applies on the parent DOM element (as you can witness, these transformed
images have the same vanishing point). Applying an individual
perspective and Y-rotation requires wrapping every
element with its container. Of course, the 3-D effect does not have to
be like Cover Flow as you could always tweak the matrix logic so that it
resembles e.g. MontageJS Popcorn instead.
Once you leave the flatland, there is no turning back!