From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
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).
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 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.
So amazing
This is awesome..that cursor is epic.Nicely done.
Really Nice work! 🙂
That’s perfect!
Guys, review the code examples, I opened on safara browser and http://cl.ly/image/0l3F1j09323p
Thanks for letting us know Antonio, it’s fixed now 🙂
Long live Mr.Spock you are our greatest hero!
Have you considered a visual indicator for when the threshold for removing the element has been reached? E.g. sometimes when swiping on iPhone, the element is not removed because I didn’t swipe far enough.
That’s a great idea and definitely useful. I’ll consider adding that to a future update. Thanks for your feedback. Cheers, ML.
It’s amazing work!!!!
Beautiful effect! Congratulations!
Awesome, very inspiring 🙂
Manoela,
that AWESOME ….
You make HTML CSS JS look AMAZING
Thank you Amazing post
I couldn´t believe my eyes! Really awesome.I have a lot to learn
Love it! This makes me wanna learn code
So Amazing – Thanks !
Hi, I’m trying to implement your blog demos for wordpress site, but adding script via src=” or enquiing script in functions.php don’t work properly
Is there a easy way to make your demos work on wordpress?
Best Regards,
Gabrielle
Thank you very much
Hi Mary Lou,
This effect is excellent, congratulations.
I have searched information in the Internet about the property “data-morph-next” but I didn’t find documentation for this property, I also visited the “http://snapsvg.io/” site but I found no information.
Can you please explain how it work, or you can help me with some sites or books where I can search.
I appreciate your help.
Excuse me by me English I’m Colombian and I speak spanish. 😛
Thank you! Excelent!
HI, Thanks for sharing such a great work, i would like to know is there a way by which we can restore the previous image by dragging up.
> Hey Mary Im a big fan of your Work.
I have 1 Question . If I want to ADD or REMOVE a picture in this wheel. How would I do that?
I tried to add 3 other pictures like in
…
etc but that didnt work.
How should the HTML be tweaked to do that?
Love,
Stephanie