Parallax Backgrounds with CSS Transitions

This may look very similar to the full JavaScript version, but works very differently. Also, for simplicity I've removed the bouncing red ball and concentrate in this example on the parallax effect.

So again... just a DIV. No canvas, SVG or Flash:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quam lacus, consequat eget sodales non, ultrices vitae arcu. Vivamus quis felis sit amet mi malesuada gravida vitae rhoncus diam.


The first class in the CSS is always applied to our DIV and sets the basic multiple background parameters.

When the page loads, .multibackstart has also been applied, setting the original start positions for all backgrounds. Once the page is done loading a script is being called and replaces .multibackstart with .multibackprocess, which kicks off the parallax scrolling. The parallax effect results from the different target positions for the different layers while the duration is kept the same - some layers have to move faster than others.

Note: Currently (December 2010) Opera does not yet support transitions on background positions.

.multiback {
	width: 400px;
	height: 180px;
	padding: 10px 10px 10px 10px;
	background-color: #a0e0ff;
	background-image: url(images/grass1.png), url(images/tree1.png), url(images/clouds1.png), url(images/bluesky.png);
	background-repeat: repeat-x, repeat-x, repeat-x, repeat-x;

.multibackstart {
	background-position: 0 0, 0 0, 0 0, 0 0;

.multibackprocess {
	-moz-transition-property: background-position;  
	-moz-transition-duration: 30s;
	-moz-transition-timing-function: linear;
	-webkit-transition-property: background-position;  
	-webkit-transition-duration: 30s;  
	-webkit-transition-timing-function: linear;
	-o-transition-property: background-position;  
	-o-transition-duration: 30s;  
	-o-transition-timing-function: linear;
	transition-property: background-position;  
	transition-duration: 30s;  
	transition-timing-function: linear;
	background-position: -5400px 0, -4600px 0, -3800px 0, -3000px 0;


In the script we first define some basic global variables. target_base sets the general speed (further is faster) and target_difference influences the parallax effect (more is... more). These initial parameters match the target positions in the .multibackprocess class above.

start_animation() is being called on page load and calls set_animation() which will kick off the CSS transition and returns the DIV object that the transition has been applied to. We then listen to the transitionend event (webkitTransitionEnd for webkit) and when the event fires after 30 seconds (transition duration set in the CSS), we fire reset_animation().

When reset_animation() is being called, we know that the transition has moved the background layers to their respective target positions. We increment the multiplier and move the target for the transition further down along the x-axis by the same distance as the initial transition. This forces the transition to continue at the same speed.

What makes this method incredibly attractive is that we can handle relatively complex animations and only have to dedicate a few script cycles every thirty seconds (in this example) to control the animation. Currently, the resulting animation is a little bit less smooth than the full JavaScript controlled animation, but CSS transitions are still relatively new in the browsers and things will certainly improve over the next few months.

<script type="text/javascript">
var multiplier = 1;
var target_difference = 800;
var target_base = 3000;

function start_animation() {
	var func_el = set_animation();
	func_el.addEventListener('transitionend', reset_animation, true);
	func_el.addEventListener('webkitTransitionEnd', reset_animation, true);

function set_animation() {
	var el = document.querySelector('div.multiback');
	el.className = 'multiback multibackprocess';
	return el;

function reset_animation() {
	var el = document.querySelector('div.multiback'); = '-'+((target_base + target_difference*3)*multiplier)+'px 0, -'+((target_base + target_difference*2)*multiplier)+'px 0, -'+((target_base + target_difference)*multiplier)+'px 0, -'+(target_base*multiplier)+'px 0';


Name *:        Email *:        URL:
no comments yet...