From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
After getting the request, we are going to show you how to create a “slide-in on scroll” effect. You’ve probably seen this cool effect on some websites, like on Nizo or in the portfolio section of brilliantly designed La Moulade. The main idea is to laterally slide in elements depending on the scroll position of the document. Dividing the page into a left and right side, we want to move the elements from “outside” of the page to the center when they are in the viewport. We will also add the option to move the elements in 3D space.
The theme of today’s tutorial is going to be a timeline where we will have some circular elements on one side and the descriptions on the other. We’ll be alternating the sides to create a random look.
So, let’s start!
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
The Markup
The HTML structure is going to consist of a main container and some rows that we’ll use to place the left and the right elements. The left and right elements will either contain a h2 heading, a circular link with a background image or a description in form of a h3 element with a link and a span:
<div id="ss-container" class="ss-container"> <div class="ss-row"> <div class="ss-left"> <h2 id="november">November</h2> </div> <div class="ss-right"> <h2>2011</h2> </div> </div> <div class="ss-row ss-medium"> <div class="ss-left"> <a href="#" class="ss-circle ss-circle-1">Some title</a> </div> <div class="ss-right"> <h3> <span>November 28, 2011</span> <a href="#">Some Title</a> </h3> </div> </div> <!-- more rows... --> </div>
For the circles we’ll have three different sizes and we’ll indicate that but giving the respective row the class of “ss-small”, “ss-medium” or “ss-large”.
Let’s look at the style.
The CSS
The container will occupy all the width and we’ll set the overflow to hidden because we don’t want a scrollbar to appear when we move the left and right elements out of if:
.ss-container{ width: 100%; position: relative; text-align: left; float: left; overflow: hidden; padding-bottom: 500px; }
To create the middle line throughout the container, we’ll use a pseudo element that we’ll position in the middle of the container:
.ss-container:before{ position: absolute; width: 4px; background: rgba(17,17,22,0.8); top: 0px; left: 50%; margin-left: -2px; content: ''; height: 100%; }
The row will serve as a wrapper for the left and right elements:
.ss-row{ width: 100%; clear: both; float: left; position: relative; padding: 30px 0; }
The two lateral elements will occupy half of the width:
.ss-left, .ss-right{ float: left; width: 48%; position: relative; } .ss-right{ padding-left: 2%; } .ss-left{ text-align: right; float: left; padding-right: 2%; }
The headings will have the following style:
.ss-container h2{ font-size: 40px; text-transform: uppercase; color: rgba(78,84,123,0.2); text-shadow: 0px 1px 1px #fff; padding: 20px 0px; } .ss-container h3{ margin-top: 34px; padding: 10px 15px; background: rgba(26, 27, 33, 0.6); text-shadow: 1px 1px 1px rgba(26, 27, 33, 0.8) }
To create a circle, we’ll set the border radius of the anchor to 50% and we’ll add some neat box shadow:
.ss-circle{ border-radius: 50%; overflow: hidden; display: block; text-indent: -9000px; text-align: left; box-shadow: 0px 2px 5px rgba(0,0,0,0.7) inset, 0px 0px 0px 12px rgba(61,64,85,0.3); background-size: cover; background-color: #f0f0f0; background-repeat: no-repeat; background-position: center center; }
We’ll have three different circle sizes and depending on which side we are we’ll make the circle float either left or right:
.ss-small .ss-circle{ width: 100px; height: 100px; } .ss-medium .ss-circle{ width: 200px; height: 200px; } .ss-large .ss-circle{ width: 300px; height: 300px; } .ss-left .ss-circle{ float: right; margin-right: 30%; } .ss-right .ss-circle{ float: left; margin-left: 30%; }
We’ll use the pseudo element :before and :after in order to create the line and the arrow that will point to the middle line. The width will be defined as a percentage so that it adjust to the screen size. We’ll also center it by setting the top to 50% and correct the position by setting the margin-top to -3px. Depending on where we are (left or right side) we want the position to be different:
.ss-circle-deco:before{ width: 29%; height: 0px; border-bottom: 5px dotted #ddd; border-bottom: 5px dotted rgba(17, 17, 22, 0.3); box-shadow: 0px 1px 1px #fff; position: absolute; top: 50%; content: ''; margin-top: -3px; } .ss-left .ss-circle-deco:before{ right: 2%; } .ss-right .ss-circle-deco:before{ left: 2%; }
The little arrow will be created by the border style and depending on if it’s a child of the left or right side, we’ll set the according border and position:
.ss-circle-deco:after{ width: 0px; height: 0px; border-top: 10px solid transparent; border-bottom: 10px solid transparent; content: ''; position: absolute; top: 50%; margin-top: -10px; } .ss-left .ss-circle-deco:after{ right: 0; border-right: 10px solid rgba(17,17,22,0.8); } .ss-right .ss-circle-deco:after{ left: 0; border-left: 10px solid rgba(17,17,22,0.8); }
Because of the different circle sizes, we’ll need to adjust the position of the headings on the other side. We want them to be at the height of the arrow, so we’ll set the margins differently (the one for ss-small is already set in the circle itself):
.ss-container .ss-medium h3{ margin-top: 82px; } .ss-container .ss-large h3{ margin-top: 133px; } .ss-container .ss-left h3{ border-right: 5px solid rgba(164,166,181,0.8); } .ss-container .ss-right h3{ border-left: 5px solid rgba(164,166,181,0.8); }
The style for the description:
.ss-container h3 span{ color: rgba(255,255,255,0.8); font-size: 13px; display: block; padding-bottom: 5px; } .ss-container h3 a{ font-size: 28px; color: rgba(255,255,255,0.9); display: block; } .ss-container h3 a:hover{ color: rgba(255,255,255,1); }
Each circle is going to have a different background image:
.ss-circle-1{ background-image:url(../images/1.jpg); } .ss-circle-2{ background-image: url(../images/2.jpg); } .ss-circle-3{ background-image: url(../images/3.jpg); } /* and so on... */
And that’s all the style! Let’s take a look at the JavaScript.
The JavaScript
The main idea is to initially show all the side elements that are visible on the viewport. All the other elements will be hidden by setting their left or right value to -50%. If the perspective option is set to true, then these elements will be translated and rotated on the y-axis, with the opacity set to 0. On scroll, we want to show the lateral elements by sliding them to position 0 (left or right, respectively), or in case of perspective, translate / rotate them accordingly once they are in the viewport.
Let’s start by defining some variables:
// the row elements var $rows = $('#ss-container > div.ss-row'), // we will cache the inviewport // rows and the outside viewport rows $rowsViewport, $rowsOutViewport, // navigation menu links $links = $('#ss-links > a'), // the window element $win = $(window), // we will store the window sizes here winSize = {}, // used in the scroll setTimeout function anim = false, // page scroll speed scollPageSpeed = 2000 , // page scroll easing scollPageEasing = 'easeInOutExpo', // perspective? hasPerspective = true, perspective = hasPerspective && Modernizr.csstransforms3d,
We’ll have the following functions:
// initialize function init = function() { // get window sizes getWinSize(); // initialize events initEvents(); // define the inviewport selector defineViewport(); // gets the elements that match the previous selector setViewportRows(); // if perspective add css if( perspective ) { $rows.css({ '-webkit-perspective' : 600, '-webkit-perspective-origin' : '50% 0%' }); } // show the pointers for the inviewport rows $rowsViewport.find('a.ss-circle').addClass('ss-circle-deco'); // set positions for each row placeRows(); },
defineViewport defines a selector that gathers the row elements that are initially visible. An element is visible if its top is less than the window’s height. These elements will not be affected when scrolling the page:
defineViewport = function() { $.extend( $.expr[':'], { inviewport : function ( el ) { if ( $(el).offset().top < winSize.height ) { return true; } return false; } }); },
setViewportRows checks which rows are initially visible:
setViewportRows = function() { $rowsViewport = $rows.filter(':inviewport'); $rowsOutViewport = $rows.not( $rowsViewport ) },
getWinSize gets the window width and height:
getWinSize = function() { winSize.width = $win.width(); winSize.height = $win.height(); },
And now let's initialize some events:
initEvents = function() { // navigation menu links. // scroll to the respective section. $links.on( 'click.Scrolling', function( event ) { // scroll to the element that has id = menu's href $('html, body').stop().animate({ scrollTop: $( $(this).attr('href') ).offset().top }, scollPageSpeed, scollPageEasing ); return false; }); $(window).on({ // on window resize we need to redefine // which rows are initially visible // (this ones we will not animate). 'resize.Scrolling' : function( event ) { // get the window sizes again getWinSize(); // redefine which rows are initially // visible (:inviewport) setViewportRows(); // remove pointers for every row $rows.find('a.ss-circle').removeClass('ss-circle-deco'); // show inviewport rows and respective pointers $rowsViewport.each( function() { $(this).find('div.ss-left') .css({ left : '0%' }) .end() .find('div.ss-right') .css({ right : '0%' }) .end() .find('a.ss-circle') .addClass('ss-circle-deco'); }); }, // when scrolling the page change // the position of each row 'scroll.Scrolling' : function( event ) { // set a timeout to avoid that the // placeRows function gets called on // every scroll trigger if( anim ) return false; anim = true; setTimeout( function() { placeRows(); anim = false; }, 10 ); } }); },
placeRows sets the position of the rows (left and right row elements). Both of these elements will start with -50% for the left/right (not visible). This value should be 0% (final position) when the element is in the center of the window.
placeRows = function() { // how much we scrolled so far var winscroll = $win.scrollTop(), // the y value for the center of the screen winCenter = winSize.height / 2 + winscroll; // for every row that is not inviewport $rowsOutViewport.each( function(i) { var $row = $(this), // the left side element $rowL = $row.find('div.ss-left'), // the right side element $rowR = $row.find('div.ss-right'), // top value rowT = $row.offset().top; // hide the row if it is under the viewport if( rowT > winSize.height + winscroll ) { if( perspective ) { $rowL.css({ '-webkit-transform' : 'translate3d(-75%, 0, 0) rotateY(-90deg) translate3d(-75%, 0, 0)', 'opacity' : 0 }); $rowR.css({ '-webkit-transform' : 'translate3d(75%, 0, 0) rotateY(90deg) translate3d(75%, 0, 0)', 'opacity' : 0 }); } else { $rowL.css({ left : '-50%' }); $rowR.css({ right : '-50%' }); } } // if not, the row should become visible // (0% of left/right) as it gets closer to // the center of the screen. else { // row's height var rowH = $row.height(), // the value on each scrolling step // will be proporcional to the distance // from the center of the screen to its height factor = ( ( ( rowT + rowH / 2 ) - winCenter ) / ( winSize.height / 2 + rowH / 2 ) ), // value for the left / right of each side of the row. // 0% is the limit val = Math.max( factor * 50, 0 ); if( val <= 0 ) { // when 0% is reached show the pointer for that row if( !$row.data('pointer') ) { $row.data( 'pointer', true ); $row.find('.ss-circle').addClass('ss-circle-deco'); } } else { // the pointer should not be shown if( $row.data('pointer') ) { $row.data( 'pointer', false ); $row.find('.ss-circle').removeClass('ss-circle-deco'); } } // set calculated values if( perspective ) { var t = Math.max( factor * 75, 0 ), r = Math.max( factor * 90, 0 ), o = Math.min( Math.abs( factor - 1 ), 1 ); $rowL.css({ '-webkit-transform' : 'translate3d(-' + t + '%, 0, 0) rotateY(-' + r + 'deg) translate3d(-' + t + '%, 0, 0)', 'opacity' : o }); $rowR.css({ '-webkit-transform' : 'translate3d(' + t + '%, 0, 0) rotateY(' + r + 'deg) translate3d(' + t + '%, 0, 0)', 'opacity' : o }); } else { $rowL.css({ left : - val + '%' }); $rowR.css({ right : - val + '%' }); } } }); }; return { init : init };
Demos
Check out the two demos, one with the "normal" slide in and one with the 3D sliding:
And that's it! I hope you enjoyed this tutorial and find it useful!
Wow…Mary Mary. Thanks for this. I’ve seen this effect before. Thank you for explaining how to do it.
Again an awesome tutorial. thank you very much.
This looks nice, but it’s so bad for the UX, the user gets very confused about how the site works and the info is not so readable…
AN EPIC TUTORIAL MARY !
you’ve presented an awesome effect on more time!
I didn’t know about Lamoulade.com .. great website too…
thanks for share this piece of creative code !
bye
hadeuh…can’t say nothing ah…zzz’
Mary Lou, you´re SO GREAT !!
Love to see how great programming is
better than going to the cinema
Go on !!
Einfach SAGENHAFT
Also,besser als ins Kino gehen
vielen, vielen Dank für die Info, es ist echt toll sowas gutes zu sehen und es noch dazu nachvollziehen zu können
DANKE !
MERCI!
OBRIGADO!
THANK YOU!
GRACIAS!
Must get this married with WordPress.
This is AWESOME !!
Mega Useful thanks
was waiting for this – or would have soon started to extract this technique from some website… thanks a lot for posting… cheers!
will get back after understanding it…
Perfect.
You are amazing, girl 😀
Gracias mil, te pasaste!… sigue asi!!!!!
LIKE ! as always great work
as always its simply great………Thanks
Mary Lou you are officially my goddess. where do you come up with all this ideas. the jquery goddess
Awesome! How long did it take to put this together?
Wow, seriously? You honestly make the COOLEST CSS3/HTML5 plugins EVER
Exelent! Fantastic! Wonderful! Unbelievable! You are the best.
http://www.kinderueberraschung.de
Absolutely awesome !!!
Thank you so much for your creativity 😉
Good! =)
Congratulations. very crazy…..
Wow, that is nice. Thanks 🙂
seems like a lengthy way to accomplish such a simple task…
Why not just go about it in a general way devoid of specifics?
http://jsfiddle.net/filever10/HKaC3/
@FiLeVer10 I like your tutorial and the effect you create. It is very clean. I’m not familiar with navigating jsfiddle though. Do you have any external scripts associated with this or is all the code in the editor?
Obrigado Mary.
Seu esforço em contribuir com novas mídias me ajuda bastante em meus projetos bem como para outros designers
tks a lot
looks really nice, cheers for this
Great work Man I like each and every thing about Jquery
BTW, this works like crap in IE, and it’s unacceptably choppy everywhere else.
I’ve fixed most of the issues in my jsfiddle link above. Or you can see it fullscreen here: http://jsfiddle.net/filever10/HKaC3/embedded/result/
Firefox doesn’t update until scrolling has stopped, so I added animate for that case; but it works much better than this in every browser (even IE6), and if you use the scrollbar in anything but Firefox, it moves in real time.
The perspective view doesn’t work in webkit, it seems to only work in safari (not the same thing)
Hi Could you please teach me how to use your tutorial? Email or message me here pls!
Once again, an amazing Tutorial !
Thanks Mary
Girl you rocks! Very creative conception.
I’ve added perspective views for Safari AND Chrome now. Check it out in the jsfiddle links above.
Most of the stuff you do are things I’ve never seen before so kudos on that Mary Lou!
This one is very lovely as well. Thank you very much 🙂
Hi, i have a problem with the with accentuation in the Spanish language, i need help, thanks
Is there anyway to have elements stay in the middle after scroll down and not slide back out of the viewport?
Be nice if you can add these effects
http://eee.asus.com/eeepad/transformer-prime/features/
Another one of those awesome tutorial
thank you again for another brilliant and gorgeous tutorial!
Beautiful marriage of web design and scripting.
thank you man i love your all work thank you my website will be launch soon !
thanks good and nice tutorial
Can we use this is for Blogger service?
Nice!.. This is very nice with video sequence: http://www.360langstrasse.sf.tv/page/
And tutorial page: http://www.netmagazine.com/tutorials/create-interactive-street-view-jquery
hey mary lou,
is there a way to prevent the rows from retracting when scrolling back upwards?
thanks!
hi mary lou,
is there a way to prevent the rows from retracting when scrolling back upwards?
Can someone please explain me how to integrate fancy box on this ? I’ve added some images and as well fancy box script , but nothing happens.
Thank you!
Sorry for my english.
Whoo!
Amazing !
Thanks a lot!!
Oh my god, this is amazing!
Thank you so much for creating these tutorials so people like me, who are continually learning, learn how to create and use these effects.
*ninja bow*
Thanks a lot! amazing work
los mejore tutoriales… 🙂
VERY WONDERFUL !!! \(^o^)/