On Scroll Effect Layout

An on scroll effect template that animates the sides of sections once they are in the viewport.

Blueprint ScrollEffect

View demo Download source

This Blueprint is a template for a on scroll effect layout. We’ve had some requests on how to achieve this effect of animating elements once they are in the viewport and decided to create a Blueprint for it. It works with adding a class for animating the two sides of a section. There is an example effect defined and also some media queries for dealing with smaller screens.

On devices that support touch, we will not apply any effects due to the scrolling logic on touch devices. Please read more about this issue in this article by TJ VanToll: onscroll Event Issues on Mobile Browsers.

Resource credit: iPad PSD Flat Mockup by Pixeden.

The HTML

<div id="cbp-so-scroller" class="cbp-so-scroller">

	<section class="cbp-so-section">
		<article class="cbp-so-side cbp-so-side-left">
			<h2>Lemon drops</h2>
			<p>Fruitcake toffee jujubes. Topping biscuit sesame snaps jelly caramels jujubes tiramisu fruitcake. Marzipan tart lemon drops chocolate sesame snaps jelly beans.</p>
		</article>
		<figure class="cbp-so-side cbp-so-side-right">
			<img src="images/1.png" alt="img01">
		</figure>
	</section>

	<section class="cbp-so-section">
		<figure class="cbp-so-side cbp-so-side-left">
			<img src="images/2.png" alt="img01">
		</figure>
		<article class="cbp-so-side cbp-so-side-right">
			<h2>Plum caramels</h2>
			<p>Lollipop powder danish sugar plum caramels liquorice sweet cookie. Gummi bears caramels gummi bears candy canes cheesecake sweet roll icing dragée. Gummies jelly-o tart. Cheesecake unerdwear.com candy canes apple pie halvah chocolate tiramisu.</p>
		</article>
	</section>

	<section>
		<!-- ... -->
	</section>
	<!-- ... -->
	
</div>

The CSS

.cbp-so-scroller {
	margin-top: 50px;
	overflow: hidden;
}

.cbp-so-section {
	margin-bottom: 15em;
	position: relative;
}

/* Clear floats of children */
.cbp-so-section:before,
.cbp-so-section:after {
	content: " ";
	display: table;
}

.cbp-so-section:after {
	clear: both;
}

/* Text styling */
.cbp-so-section h2 {
	font-size: 5em;
	font-weight: 300;
	line-height: 1;
}

.cbp-so-section p {
	font-size: 2em;
	font-weight: 300;
}

/* Sides */
.cbp-so-side {
	width: 50%;
	float: left;
	margin: 0;
	padding: 3em 4%;
	overflow: hidden;
	min-height: 12em;
}

/* Clear floats of children */
.cbp-so-side:before,
.cbp-so-side:after {
	content: " ";
	display: table;
}

.cbp-so-side:after {
	clear: both;
}

.cbp-so-side-right {
	text-align: left;
}

.cbp-so-side-left {
	text-align: right;
}

.cbp-so-side-right img {
	float: left;
}

.cbp-so-side-left img {
	float: right;
}

/* Initial state (hidden or anything else) */
.cbp-so-init .cbp-so-side {
	opacity: 0;
	-webkit-transition: none;
	-moz-transition: none;
	transition: none;
}

.cbp-so-init .cbp-so-side-left {
	-webkit-transform: translateX(-80px);
	-moz-transform: translateX(-80px);
	transform: translateX(-80px);
}

.cbp-so-init .cbp-so-side-right {
	-webkit-transform: translateX(80px);
	-moz-transform: translateX(80px);
	transform: translateX(80px);
}

/* Animated state */
/* add you final states (transition) or your effects (animations) for each side */
.cbp-so-section.cbp-so-animate .cbp-so-side-left,
.cbp-so-section.cbp-so-animate .cbp-so-side-right {
	-webkit-transition: -webkit-transform 0.5s, opacity 0.5s;
	-moz-transition: -moz-transform 0.5s, opacity 0.5s;
	transition: transform 0.5s, opacity 0.5s;
	-webkit-transform: translateX(0px);
	-moz-transform: translateX(0px);
	transform: translateX(0px);
	opacity: 1;
}

/* For example, add a delay for the right side:
.cbp-so-section.cbp-so-animate .cbp-so-side-right {
	-webkit-transition-delay: 0.2s;
	-moz-transition-delay: 0.2s;
	transition-delay: 0.2s;
}
*/

/* Example media queries */

@media screen and (max-width: 73.5em) {
	.cbp-so-scroller {
		font-size: 65%;
	}

	.cbp-so-section h2 {
		margin: 0;
	}

	.cbp-so-side img {
		max-width: 120%;
	}
}

@media screen and (max-width: 41.125em) {
	.cbp-so-side {
		float: none;
		width: 100%;
	}

	.cbp-so-side img {
		max-width: 100%;
	}
}

The JavaScript

/**
 * cbpScroller.js v1.0.0
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 * 
 * Copyright 2013, Codrops
 * http://www.codrops.com
 */
;( function( window ) {
	
	'use strict';

	var docElem = window.document.documentElement;

	function getViewportH() {
		var client = docElem['clientHeight'],
			inner = window['innerHeight'];
		
		if( client < inner )
			return inner;
		else
			return client;
	}

	function scrollY() {
		return window.pageYOffset || docElem.scrollTop;
	}

	// http://stackoverflow.com/a/5598797/989439
	function getOffset( el ) {
		var offsetTop = 0, offsetLeft = 0;
		do {
			if ( !isNaN( el.offsetTop ) ) {
				offsetTop += el.offsetTop;
			}
			if ( !isNaN( el.offsetLeft ) ) {
				offsetLeft += el.offsetLeft;
			}
		} while( el = el.offsetParent )

		return {
			top : offsetTop,
			left : offsetLeft
		}
	}

	function inViewport( el, h ) {
		var elH = el.offsetHeight,
			scrolled = scrollY(),
			viewed = scrolled + getViewportH(),
			elTop = getOffset(el).top,
			elBottom = elTop + elH,
			// if 0, the element is considered in the viewport as soon as it enters.
			// if 1, the element is considered in the viewport only when it's fully inside
			// value in percentage (1 >= h >= 0)
			h = h || 0;

		return (elTop + elH * h) <= viewed && (elBottom) >= scrolled;
	}

	function extend( a, b ) {
		for( var key in b ) { 
			if( b.hasOwnProperty( key ) ) {
				a[key] = b[key];
			}
		}
		return a;
	}

	function cbpScroller( el, options ) {	
		this.el = el;
		this.options = extend( this.defaults, options );
		this._init();
	}

	cbpScroller.prototype = {
		defaults : {
			// The viewportFactor defines how much of the appearing item has to be visible in order to trigger the animation
			// if we'd use a value of 0, this would mean that it would add the animation class as soon as the item is in the viewport. 
			// If we were to use the value of 1, the animation would only be triggered when we see all of the item in the viewport (100% of it)
			viewportFactor : 0.2
		},
		_init : function() {
			if( Modernizr.touch ) return;
			this.sections = Array.prototype.slice.call( this.el.querySelectorAll( '.cbp-so-section' ) );
			this.didScroll = false;

			var self = this;
			// the sections already shown...
			this.sections.forEach( function( el, i ) {
				if( !inViewport( el ) ) {
					classie.add( el, 'cbp-so-init' );
				}
			} );

			var scrollHandler = function() {
					if( !self.didScroll ) {
						self.didScroll = true;
						setTimeout( function() { self._scrollPage(); }, 60 );
					}
				},
				resizeHandler = function() {
					function delayed() {
						self._scrollPage();
						self.resizeTimeout = null;
					}
					if ( self.resizeTimeout ) {
						clearTimeout( self.resizeTimeout );
					}
					self.resizeTimeout = setTimeout( delayed, 200 );
				};

			window.addEventListener( 'scroll', scrollHandler, false );
			window.addEventListener( 'resize', resizeHandler, false );
		},
		_scrollPage : function() {
			var self = this;

			this.sections.forEach( function( el, i ) {
				if( inViewport( el, self.options.viewportFactor ) ) {
					classie.add( el, 'cbp-so-animate' );
				}
				else {
					// this add class init if it doesn´t have it. This will ensure that the items initially in the viewport will also animate on scroll
					classie.add( el, 'cbp-so-init' );
					
					classie.remove( el, 'cbp-so-animate' );
				}
			});
			this.didScroll = false;
		}
	}

	// add to global namespace
	window.cbpScroller = cbpScroller;

} )( window );

View demo Download source

Previous:
Next:

Tagged with:

ML is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

View all contributions by

Website: http://www.codrops.com

Related Articles

CSS Reference

Learn about all important CSS properties from the basics with our extensive and easy-to-read CSS Reference.

It doesn't matter if you are a beginner or intermediate, start leanirng CSS now.

Feedback 57

Comments are closed.
  1. 2

    Thanks Mary. This is a really nice article. I’ve wanted to know more about this effect since I first saw it but never had the time. I can’t wait to play with your demo and see what effects I can add to it.

  2. 3

    What a great timing! :) I just finished my wireframe with this effect, thank you!

  3. 7

    Wow, great! Only a thing, in Chrome (28.0.1500.71) the document flicker when you scroll down. It’s all ok in Safari…

    • 8

      I’m seeing this flicker in Chrome as well. I’d be interested to see the underlying cause and how to fix that up.

  4. 12

    Since long, i was waiting for tutorial like this. Many many thanks.

  5. 13

    Excelente articulo MARY LOU, justo lo que necesita para el rediseño de mi web.

  6. 15

    I’m so surprised to see this blueprint working properly in IE10 and not working at all in Chrome… what’s wrong with my browsers ?!!

    Anyway, Mary, another great work from you, thanks!

  7. 17

    Lovely like always Mary.

    I have one small question that I hope you can answer for me.
    In all of your tutorials on codrops, your javascript is wrapped with this:

    ;( function( window ) {
    ...
    } )( window );

    What does this do exactly? Is it a DOM ready function? Or are you passing the window object?

    I am trying to move away from my reliance on jQuery towards a lighter JavaScript skill-set.

    Cheers
    Dean

    • 18

      The “;” on the first line prevents some nasty errors that can occur while minimizing/concatenating js files.

      ( function (window) { ... })(window);

      This creates an annonymous and self-executing function to create an isolated scope, so you won’t pollute global namespace with your variables. It takes one argument — the window object. It is just a convenient way to create an alias for window object if you use it a lot. I usually see a slightly more advanced example of such a function:

      ( function (w, d, undefined) { ... })(window, document);

      This aliases window as w, and document as d, and also makes sure undefined is undefined (that’s older js legacy, probably useless now).

    • 19

      Thanks for the reply Maciek.

      You couldn’t have explained that better!

      Cheers.

  8. 21

    I hope you don’t mind such an ignorant question. I am taking online classes to improve my understanding of css, html and java, I just haven’t gotten to this particular part yet and online research has proved fruitless.

    In WordPress, where specifically would you add the the html and javascript? I understand the css would go into the stylesheet/CSS, correct? I just haven’t been able to figure out the html and javascript part.

    Again, I apologize for asking such a primary question. I guess I’m not working the search inquiries correctly to get the answer.

    Your site has been a great resource in learning the basics of coding and seeing it work in an actual code situation. Thank you.

    • 22

      Hye,

      The javascript can be written in footer.php i guess (since its included in every page).. And for HTML one of the relevant php files, though you should be careful while editing them.

      Hope it helps.

  9. 23

    Excellent Mary Lou!

    As a friendly suggestion, consider doing the blueprint for a three column adaptive – responsive website (see the new Mashable) next.

Comments are closed.