scrolledAnimation: An Animated Effect Driven by the Scroll Position
This example implements a graphical effect not unlike Apple's "Cover flow" to a list of images in a way that doesn't sacrifice accessibility.
Design
I assume some familiarity with the "Cover flow" effect in the Apple OS X operating system or iTunes, but more information can be found on the Wikipedia page dedicated to it if needed.
Since a cover flow interface is basically a list of image previews, the HTML equivalent of that will be used as a starting point.
<ul> <li> <a href="?id=2"> <img alt="" src="scrolledAnimation2.jpg"/><br/> <em>Lorem Ipsum</em> </a> </li> <li> <a href="?id=3"> <img alt="" src="scrolledAnimation3.jpg"/><br/> <em>Lorem Ipsum</em> </a> </li> <li> <a href="?id=4"> <img alt="" src="scrolledAnimation4.jpg"/><br/> <em>Lorem Ipsum</em> </a> </li> </ul>Figure 1. This HTML example is a simple list of images within hyperlinks.
The above code would come out looking something like this:
By sticking to this simple structure it is ensured that the contents of the example will be accessible to even the most limited browsers, like those on mobile phones and very easily parsed by search engines.
Decoration
To shape this basic HTML into a horizontal scrolling list the individual elements are floated against each other in a container with an overflow property.
section.scrolledAnimation {
background : #000000 url(../images/scrolledAnimation0.jpg) no-repeat 0% 0%;
border : solid 1px #000000;
display : block;
margin : 0px 0px 1em 0px;
padding : 16px;
}
section.scrolledAnimation div {
height : 185px;
overflow : auto;
overflow-x : scroll;
overflow-y : hidden;
width : 100%;
}
section.scrolledAnimation div ul {
margin : 0px;
padding : 0px 45% 0px 45%;
width : 10000px;
}
section.scrolledAnimation div ul li {
float : left;
list-style-type : none;
padding : 16px;
text-align : center;
width : 100px;
}
Figure 3.
This clip of the CSS code shows how the images are floated together in a scrolling container.
Below is how the marked up list looks with all its styles applied. Please note that, even without scripting, this is a perfectly functional piece of layout.
To keep this example simple, the default scrollbar is used instead of a prettier fake one. Doing so would have made the example too complicated to follow. The browser's standard scrollbar is of course also the safest choice to make for accessibility, since it always works.
Dynamics
To add the required eye-candy to this example some scripting is required. jQuery was chosen because it allows the script to be applied in an unobtrusive way. To link the script to any cover flows on a page, the following jQuery statement was used.
// extend jQuery with this method
jQuery.fn.scrolledAnimation = function(){
return this.each(
function(){
jQuery.classBehaviours.handlers.scrolledAnimation.start(this);
}
);
};
// set the event handler for this jQuery method
$(document).ready(
function(){
$(".scrolledAnimation").scrolledAnimation();
}
);
Figure 5.
This jQuery statement links the cover flow script to all cover flow examples on a page with the same class-name.
The basic reasoning behind the script will be to adjust the appearance of the image as a function of the distance scrolled and the item's place in line. Below is the functionality required broken up into the simplest steps possible.
- Get the distance scrolled in the display.
- Get all items in the scrolling display.
- Get the width of the scrolling display.
-
For every item in the display.
- Measure the width of the icon.
- Calculate the icon's relative position to the distance scrolled.
- Calculate how near the icon is to the center of the display.
- Adjust for icons that are outside the display.
- Make a useful rounded number.
- Use this number to adjust the class-name of the icon.
With the program laid out in plain English, transferring it to javascript is the next step.
jQuery.classBehaviours.handlers.scrolledAnimation = {
start: function(node){
// get the scroller from this container
scroller = node.getElementsByTagName('DIV')[0];
// add the event handler
jQuery.classBehaviours.utilities.addEvent(scroller, 'scroll', this.adjustScroll);
},
adjustScroll: function(){
var san = jQuery.classBehaviours.handlers.scrolledAnimation;
// get the distance scrolled in the display
scrollPosition = (san.focusNode!=null) ? san.focusNode.scrollLeft : 0 ;
// get all items in the scrolling display
scrollerItems = scroller.getElementsByTagName('LI');
// get the width of the scrolling display
scrollViewWidth = scrollerItems[0].parentNode.parentNode.offsetWidth;
// for every item in the display
for(var a=0; a<scrollerItems.length; a++){
// measure the width of the icon
scrollItemWidth = scrollerItems[a].offsetWidth;
// calculate the icon's relative position to the distance scrolled
scrollRelativePosition = Math.abs(scrollItemWidth * a - scrollPosition);
// calculate how near the icon is to the center of the display
scrollCenterFraction = 10 * (1 - scrollRelativePosition / scrollViewWidth * 2);
// adjust for icons that are outside the display
scrollCenterFraction = (scrollCenterFraction<0) ? 0 : scrollCenterFraction ;
// make a useful rounded number
scrollCenterFraction = Math.round(scrollCenterFraction);
// use this number to adjust the classname of the icon
scrollerItems[a].className = 'step_' + scrollCenterFraction;
}
}
}
Figure 7.
The required javascript is added between the plain english descriptions of the functionality.
As can be seen in the last line of this code example, only the class-name of the images is adjusted and the appearance not directly. This was done purposefully, to keep the decorative effects separate from the logic of the code.
The actual changes to the appearance of the images are defined in the style sheet.
/* steps indicate the relative distance from the center on a scale of 1 to 10 */
section.scrolledAnimation div ul li.step_0 {
opacity : 0.00; -moz-opacity : 0.00; -khtml-opacity : 0.00; filter : alpha(opacity=0);
}
section.scrolledAnimation div ul li.step_1 {
opacity : 0.10; -moz-opacity : 0.10; -khtml-opacity : 0.10; filter : alpha(opacity=10);
}
section.scrolledAnimation div ul li.step_2 {
opacity : 0.20; -moz-opacity : 0.20; -khtml-opacity : 0.20; filter : alpha(opacity=20);
}
section.scrolledAnimation div ul li.step_3 {
opacity : 0.30; -moz-opacity : 0.30; -khtml-opacity : 0.30; filter : alpha(opacity=30);
}
section.scrolledAnimation div ul li.step_4 {
opacity : 0.40; -moz-opacity : 0.40; -khtml-opacity : 0.40; filter : alpha(opacity=40);
}
section.scrolledAnimation div ul li.step_5 {
opacity : 0.50; -moz-opacity : 0.50; -khtml-opacity : 0.50; filter : alpha(opacity=50);
}
section.scrolledAnimation div ul li.step_6 {
opacity : 0.60; -moz-opacity : 0.60; -khtml-opacity : 0.60; filter : alpha(opacity=60);
}
section.scrolledAnimation div ul li.step_7 {
opacity : 0.70; -moz-opacity : 0.70; -khtml-opacity : 0.70; filter : alpha(opacity=70);
}
section.scrolledAnimation div ul li.step_8 {
opacity : 0.80; -moz-opacity : 0.80; -khtml-opacity : 0.80; filter : alpha(opacity=80);
}
section.scrolledAnimation div ul li.step_9 {
opacity : 0.90; -moz-opacity : 0.90; -khtml-opacity : 0.90; filter : alpha(opacity=90);
}
section.scrolledAnimation div ul li.step_10 {
opacity : 1.00; -moz-opacity : 1.00; -khtml-opacity : 1.00; filter : alpha(opacity=100);
}
section.scrolledAnimation div ul li.step_0 a img {
height : 64px; margin : 16px; width : 64px;
}
section.scrolledAnimation div ul li.step_1 a img {
height : 68px; margin : 14px; width : 68px;
}
section.scrolledAnimation div ul li.step_2 a img {
height : 74px; margin : 11px; width : 74px;
}
section.scrolledAnimation div ul li.step_3 a img {
height : 82px; margin : 7px; width : 82px;
}
section.scrolledAnimation div ul li.step_4 a img {
height : 92px; margin : 2px; width : 92px;
}
section.scrolledAnimation div ul li.step_5 a img {
height : 96px; margin : 0px; width : 96px;
}
section.scrolledAnimation div ul li.step_6 a img {
height : 100px; margin : -2px; width : 100px;
}
section.scrolledAnimation div ul li.step_7 a img {
height : 110px; margin : -7px; width : 110px;
}
section.scrolledAnimation div ul li.step_8 a img {
height : 118px; margin : -11px; width : 118px;
}
section.scrolledAnimation div ul li.step_9 a img {
height : 124px; margin : -14px; width : 124px;
}
section.scrolledAnimation div ul li.step_10 a img {
height : 128px; margin : -16px; width : 128px;
}
Figure 8.
The steps defined in the style sheet determine the size and opacity of the images.
This makes it easy for anyone modifying the example to stay out of the script, if the thought of changing the logic is too daunting.
With the script cycling through the animation steps defined in the style sheet, the effect is complete. Every image is scaled and faded according to its position relative to the center of the display.
As an extra feature, clicking on the images, will open a larger version in a floating popup layer, commonly referred to as a "light-box". This effect is scripted similarly to the cover flow example and discussed elsewhere on this site in popUpLayer: A Light-box Styled Pop-Up Layer.
Required Files
-
jQuery Addon Script: jquery.classbehaviours.scrolledanimation.js
- Prerequisite Script: jquery.classbehaviours.js
- Example Markup: scrolledanimation.html
- Example Stylesheet: scrolledanimation.css
- Example XML: scrolledanimation.html
Comments on this Article












