A little playful content navigation effect that uses the draggable Elastic Stack and Snap.svg for animating a background shape like a trampoline. The demos are a tribute to Leonard Nimoy.

Today we want to share a fun little effect with you. The idea is to simulate the bounciness of a trampoline when navigating the image stack. For the draggable image stack we are using the Elastic Stack. The SVG animations are done with the help of Snap.svg. We will also use CSS Transitions for all the effects.

The demos and the Dribbble shots used in them are a little tribute to Leonard Nimoy who passed away last week. Rest in peace, Mr. Spock.

Have a look at the tutorial on the Elastic Stack to understand how it works. The Elastic Stack uses Draggabilly by David DeSandro.

The first element in the body will be the SVG shape that we’ll morph from a rectangle to a squeezed rectangle whenever we navigate.

<div id="morph-shape" class="morph-shape" data-morph-next="M301,301c0,0-83.8-21-151-21C71.8,280-1,301-1,301s21-65.7,21-151C20,79.936-1-1-1-1s73,11,151,11c85 0,151-11,151-11s-21,66.43-21,151C280,229.646,301,301,301,301z"> <svg width="100%" height="100%" viewBox="0 0 300 300" preserveAspectRatio="none"> <path d="M301,301c0,0-83.8,0-151,0c-78.2,0-151,0-151,0s0-65.7,0-151C-1,79.936-1-1-1-1s73,0,151,0c85,0,151,0,151,0s0,66.43,0,151 C301,229.646,301,301,301,301z" /> </svg> </div>

The initial rectangular shape is the one defined in the path of the SVG, and the squeezed shape we morph to is saved in the data-morph-next .

The markup for the stack is the following:

<div class="stack"> <ul id="elasticstack" class="stack__images"> <li><img src="img/1.jpg" alt="01"/></li> <li><img src="img/2.png" alt="02"/></li> <li><img src="img/3.jpg" alt="03"/></li> <li><img src="img/4.jpg" alt="04"/></li> <li><img src="img/5.png" alt="05"/></li> <li><img src="img/6.png" alt="06"/></li> </ul> <button id="stack-next" class="stack__next"><i class="icon icon-down"></i><span>Next</span></button> <ul id="stack-titles" class="stack__titles"> <li class="current"> <blockquote> <p>"Once you have eliminated the impossible, whatever remains, however improbable, must be the truth."</p> <footer><a href="http://drbl.in/nTZA">#RIPLeonardNimoy</a> by James Olstein</footer> </blockquote> </li> <li> <blockquote> <p>"The needs of the many outweigh the needs of the few, or the one."</p> <footer><a href="http://drbl.in/nUqE">Mr. Spock</a> by Mustafa Kural</footer> </blockquote> </li> <li> <blockquote> <p>"Insufficient facts always invite danger."</p> <footer><a href="http://drbl.in/nUhf">LLAP</a> by Sarah McKay</footer> </blockquote> </li> <li> <blockquote> <p>"Without followers, evil cannot spread."</p> <footer><a href="http://drbl.in/nTTO">RIP Leonard Nimoy</a> by derric</footer> </blockquote> </li> <li> <blockquote> <p>"Logic is the beginning of wisdom, not the end."</p> <footer><a href="http://drbl.in/nUcJ">#Goodnight, Spock</a> by Helen Tseng</footer> </blockquote> </li> <li> <blockquote> <p>"Change is the essential process of all existence."</p> <footer><a href="http://drbl.in/nTYY">RIP Spock</a> by Sahirul Iman</footer> </blockquote> </li> </ul> </div><!-- /stack -->

We have the image stack, a navigation button for showing the next image and a list for the descriptions of each image. We’ve adjusted the Elastic Stack in order to separate the titles from the images so that we can apply a different effect to them (and not the effect we see on the image).

The morph shape needs to be stretched over all the page:

.morph-shape { position: absolute; width: 100%; height: 100%; top: 0; left: 0; } .morph-shape svg { position: absolute; margin: 0; pointer-events: none; }

The styles for the stack are an adapted version of the original styles of the Elastic Stack. We use perspective in the ul for the images, so that we can position them in a 3D space. The two last classes in this block are added dynamically when we animate and drag the stack.

.stack ul { position: relative; margin: 0 auto; padding: 0; list-style: none; } .stack ul li { position: absolute; width: 100%; opacity: 0; } ul.stack__images { width: 400px; height: 300px; z-index: 10; perspective: 1000px; perspective-origin: 50% -50%; } @media screen and (max-height: 530px), screen and (max-width: 400px) { ul.stack__images { width: 260px; height: 195px; } } .stack__images li { top: 0; z-index: 1; transform: translate3d(0, 0, -180px); transform-style: preserve-3d; } .stack__images li img { display: block; max-width: 100%; pointer-events: none; } .stack__images li:hover { cursor: url(../img/cursor_vulcan.png), auto; } .stack__images li:active { cursor: -webkit-grabbing; cursor: grabbing; } .stack__images li.animate { transition: all 0.3s ease-out; } .stack__images li.move-back { transition-timing-function: cubic-bezier(0.175, 0.885, 0.470, 1.515); }

The other elements, like the navigation button and the titles have the following styles:

.stack__next { border: none; background: none; display: block; padding: 0; overflow: hidden; width: 36px; height: 36px; margin: 10px auto 0; font-size: 30px; position: relative; cursor: pointer; color: #067ba7; } .stack__next:hover { color: #fff; } .stack__next:focus { outline: none; } .stack__next span { position: absolute; top: 200%; } ul.stack__titles { height: 18vh; max-width: 560px; width: 95%; } .stack__titles blockquote { margin: 0; text-align: center; font-size: 1.4em; } .stack__titles blockquote footer { font-size: 50%; padding-bottom: 1em; font-family: 'Montserrat', Arial, sans-serif; } .stack__titles li { pointer-events: none; transition: opacity 0.45s ease; } .stack__titles li.current { opacity: 1; pointer-events: auto; }

The title items will fade in and fade out when we navigate.

Note that you might have a problem seeing the custom cursor if you are on Yosemite (Mac).

The last thing we do in our style sheet is to define some transitions. The SVG will animate its fill when we do the “trampoline effect” while the main container of the page will be pushed on the Z-axis using a 3D transform, making it appear smaller. You will find the styles for the container in the base.css style sheet.

The class navigate-next is added to the body when we navigate. In demo 2 we do a slight variation, where we also rotate the container by 10deg on the Z-axis and -5deg on the X-axis.

.morph-shape svg { fill: #01AEF0; transition: fill 0.1s ease-out; } .navigate-next .morph-shape svg { fill: #01a0dc; transition-duration: 0.45s; } .container { transition: transform 0.1s cubic-bezier(0.6, 0, 0.5, 1); } .demo-1.navigate-next .container { transition-duration: 0.45s; transform: translate3d(0, 0, -600px); } .demo-2.navigate-next .container { transition-duration: 0.45s; transform: rotate3d(-0.5, 0, 1, -6deg) translate3d(0, 0, -600px); } .demo-2 .morph-shape svg { fill: #A2CD72; } .demo-2.navigate-next .morph-shape svg { fill: #95C264; }

For the JavaScript part, we need to include Snap, a custom Modernizr (open the file and see the features needed in the second line of the script), Draggabilly and the script for the Elastic Stack and then we define our script that will take care of the morphing between the SVG shapes, the navigation and the title appearances:

(function() { var body = document.body, titles = [].slice.call( document.querySelectorAll( '#stack-titles > li' ) ), totalTitles = titles.length, stack = new ElastiStack( document.getElementById( 'elasticstack' ), { onUpdateStack : function( idx ) { classie.remove( titles[idx === 0 ? totalTitles - 1 : idx - 1], 'current' ); classie.add( titles[idx], 'current' ); } } ), shapeEl = document.getElementById( 'morph-shape' ), s = Snap( shapeEl.querySelector( 'svg' ) ), pathEl = s.select( 'path' ), paths = { reset : pathEl.attr( 'd' ), next : shapeEl.getAttribute( 'data-morph-next' ) }; document.getElementById( 'stack-next' ).addEventListener( 'mousedown', nextItem ); function nextItem() { classie.add( body, 'animating' ); classie.add( body, 'navigate-next' ); pathEl.stop().animate( { 'path' : paths.next }, 450, mina.easeinout, function() { classie.remove( body, 'navigate-next' ); stack.nextItem( { transform : 'translate3d(0,-60px,400px)' } ); pathEl.stop().animate( { 'path' : paths.reset }, 100, mina.easeout, function() { classie.remove( body, 'animating' ); } ); } ); } })();

And that’s it! We hope you enjoyed this little effect and find it inspiring.