From our sponsor: Chromatic - Visual testing for Storybook, Playwright & Cypress. Catch UI bugs before your users do.
In the following tutorial we will create an asymmetrical image slider with a little twist: when sliding the pictures we will slightly rotate them and delay the sliding of each element. The unusual shape of the slider is created by some elements placement and the use of thick borders. We will also add an autoplay option and the mousewheel functionality.
We’ll use the jQuery 2D Transformation Plugin for rotating the images and the jQuery Mousewheel Plugin by Brandon Aaron for the mousewheel control.
The beautiful photos are by Andrew and Lili and you can see their Behance profile here:
http://www.behance.net/AndrewLili
The images are licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
Allrighty! Let’s start with the HTML!
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
First, we will wrap all our slider elements in a wrapper with the class “rm_wrapper”:
<div class="rm_wrapper"> ... </div>
Inside of that wrapper we will have a container for the actual slider list, some mask and corner elements, the heading and a hidden div that will contain all the image sets:
<div id="rm_container" class="rm_container"> <ul> <li data-images="rm_container_1" data-rotation="-15"> <img src="images/1.jpg"/> </li> <li data-images="rm_container_2" data-rotation="-5"> <img src="images/2.jpg"/> </li> <li data-images="rm_container_3" data-rotation="5"> <img src="images/3.jpg"/> </li> <li data-images="rm_container_4" data-rotation="15"> <img src="images/4.jpg"/> </li> </ul> <div id="rm_mask_left" class="rm_mask_left"></div> <div id="rm_mask_right" class="rm_mask_right"></div> <div id="rm_corner_left" class="rm_corner_left"></div> <div id="rm_corner_right" class="rm_corner_right"></div> <h2>Fashion Explosion 2012</h2> <div style="display:none;"> <div id="rm_container_1"> <img src="images/1.jpg"/> <img src="images/5.jpg"/> <img src="images/6.jpg"/> <img src="images/7.jpg"/> </div> <div id="rm_container_2"> <img src="images/2.jpg"/> <img src="images/8.jpg"/> <img src="images/9.jpg"/> <img src="images/10.jpg"/> </div> <div id="rm_container_3"> <img src="images/3.jpg"/> <img src="images/11.jpg"/> <img src="images/12.jpg"/> <img src="images/13.jpg"/> </div> <div id="rm_container_4"> <img src="images/4.jpg"/> <img src="images/14.jpg"/> <img src="images/15.jpg"/> <img src="images/16.jpg"/> </div> </div> </div>
So the unordered lists will have the first set of four images where each list element has some data attributes for the image sets and the rotation degree. We will use that data to know which images come next and how much each image should be rotated.
The mask and corner divs will be absolute elements that we will place on top of the slider, slightly rotated in order to cover some areas. Since we will use the same background color for these elements like the body’s background color, we will create the illusion of the images being shaped in a certain way.
Then we’ll add the elements for the navigation and the autoplay controls:
<div class="rm_nav"> <a id="rm_next" href="#" class="rm_next"></a> <a id="rm_prev" href="#" class="rm_prev"></a> </div> <div class="rm_controls"> <a id="rm_play" href="#" class="rm_play">Play</a> <a id="rm_pause" href="#" class="rm_pause">Pause</a> </div>
Let’s take a look at the CSS.
The CSS
First, we’ll reset some styles and define the properties for the body. (Remember, if we would have another background color, we would want to change the background and border colors of some of the elements in our slider, too.)
@import url('reset.css'); body{ background:#f0f0f0; color:#000; font-family: 'PT Sans Narrow', Arial, sans-serif; font-size:16px; } a{ color:#000; text-decoration:none; } h1{ padding:10px; margin:20px; font-size:40px; text-transform:uppercase; text-shadow:0px 0px 1px #fff; color:#333; background:transparent url(../images/line.png) repeat-x bottom left; }
The main wrapper will have the following style:
.rm_wrapper{ width:1160px; margin:0 auto; position:relative; }
The container for the slider will have any overflow hidden, which will help shaping our slider since we will cut off the outer sides of it:
.rm_container{ width:1050px; overflow:hidden; position:relative; height:530px; margin:0 auto; }
The heading will have the following style:
.rm_container h2{ background:transparent url(../images/lines.png) repeat top left; padding:10px 30px; position:absolute; bottom:170px; right:0px; color:#000; font-size:36px; text-transform:uppercase; text-shadow:1px 0px 1px #fff; }
Let’s define the width for the ul to be bigger than the container since we want to make the list element float next to each other:
.rm_container ul{ width:1170px; }
By giving a negative left margin and a thick border to the list element, we will overlap the images and cut off the right sides so that we create our asymmetrical shapes by rotating the elements then. The border color will be the same like the background color of the body (or the container).
.rm_container ul li{ float:left; margin-left:-80px; position:relative; overflow:hidden; width:310px; height:465px; border:30px solid #f0f0f0; border-width:50px 30px 0px 30px; background-color:#f0f0f0; }
We’ll position the images absolutely:
.rm_container ul li img{ position:absolute; top:0px; left:0px; }
In the following we will style the mask and the corner elements. They will be all positioned absolutely and we’ll give them the grey background color. By rotating them, we’ll make the images to appear as being “shaped”:
.rm_mask_right, .rm_mask_left{ position: absolute; height: 110px; background: #f0f0f0; width: 530px; bottom: -30px; left: 0px; -moz-transform:rotate(-3deg); -webkit-transform:rotate(-3deg); transform:rotate(-3deg); } .rm_mask_right{ left:auto; right:0px; -moz-transform:rotate(3deg); -webkit-transform:rotate(3deg); transform:rotate(3deg); } .rm_corner_right, .rm_corner_left{ background: #f0f0f0; position:absolute; width:200px; height:100px; bottom:0px; left:-65px; -moz-transform:rotate(45deg); -webkit-transform:rotate(45deg); transform:rotate(45deg); } .rm_corner_right{ left:auto; right:-65px; -moz-transform:rotate(-45deg); -webkit-transform:rotate(-45deg); transform:rotate(-45deg); }
The navigation elements will be placed to the left and right of the main container:
.rm_nav a{ position:absolute; top:200px; width:38px; height:87px; cursor:pointer; opacity:0.7; } .rm_nav a:hover{ opacity:1.0; } .rm_nav a.rm_next{ background:transparent url(../images/next.png) no-repeat top left; right:0px; } .rm_nav a.rm_prev{ background:transparent url(../images/prev.png) no-repeat top left; left:0px; }
The pause/play control will be placed to the top left of the main container:
.rm_controls{ position:absolute; top:0px; left:-40px; height:20px; } .rm_controls a{ cursor:pointer; opacity:0.7; padding-left:24px; font-size:16px; text-transform:uppercase; height:20px; float:left; line-height:20px; } .rm_controls a:hover{ opacity:1.0; } .rm_controls a.rm_play{ display:none; background:transparent url(../images/play.png) no-repeat center left; } .rm_controls a.rm_pause{ background:transparent url(../images/pause.png) no-repeat center left; }
And that’s all the style! Let add the spice!
The JavaScript
The main idea for the slider functionality is to add another image before the current one with a slightly increased rotation degree than the current item. Then we will animate the rotation and make the new images appear.
So let’s start by caching some elements and checking if we are dealing with a special needs browser in order to deal with some issues:
//our 4 items var $listItems = $('#rm_container > ul > li'), totalItems = $listItems.length, //the controls $rm_next = $('#rm_next'), $rm_prev = $('#rm_prev'), $rm_play = $('#rm_play'), $rm_pause = $('#rm_pause'), //the masks and corners of the slider $rm_mask_left = $('#rm_mask_left'), $rm_mask_right = $('#rm_mask_right'), $rm_corner_left = $('#rm_corner_left'), $rm_corner_right= $('#rm_corner_right'), //check if the browser is <= IE8 ieLte8 = ($.browser.msie && parseInt($.browser.version) <= 8),
Then we will define our main function:
RotateImageMenu = (function() { ... })(); RotateImageMenu.init();
And then we define the following in our function:
//difference of animation time between the items var timeDiff = 300, //time between each image animation (slideshow) slideshowTime = 3000, slideshowInterval, //checks if the images are rotating isRotating = false, //how many images completed each slideshow iteration completed = 0, /* all our images have 310 of width and 465 of height. this could / should be dynamically calculated if we would have different image sizes. we will set the rotation origin at x = width/2 and y = height*2 */ origin = ['155px', '930px'], init = function() { configure(); initEventsHandler(); }, //initialize some events initEventsHandler = function() { /* next and previous arrows: we will stop the slideshow if active, and rotate each items images. 1 rotate right -1 rotate left */ $rm_next.bind('click', function(e) { stopSlideshow(); rotateImages(1); return false; }); $rm_prev.bind('click', function(e) { stopSlideshow(); rotateImages(-1); return false; }); /* start and stop the slideshow */ $rm_play.bind('click', function(e) { startSlideshow(); return false; }); $rm_pause.bind('click', function(e) { stopSlideshow(); return false; }); /* adds events to the mouse and left / right keys */ $(document).bind('mousewheel', function(e, delta) { if(delta > 0) { stopSlideshow(); rotateImages(0); } else { stopSlideshow(); rotateImages(1); } return false; }).keydown(function(e){ switch(e.which){ case 37: stopSlideshow(); rotateImages(0); break; case 39: stopSlideshow(); rotateImages(1); break; } }); }, /* rotates each items images. we set a delay between each item animation */ rotateImages = function(dir) { //if the animation is in progress return if(isRotating) return false; isRotating = true; $listItems.each(function(i) { var $item = $(this), /* the delay calculation. if rotation is to the right, then the first item to rotate is the first one, otherwise the last one */ interval = (dir === 1) ? i * timeDiff : (totalItems - 1 - i) * timeDiff; setTimeout(function() { //the images associated to this item var $otherImages = $('#' + $item.data('images')).children('img'), totalOtherImages = $otherImages.length; //the current one $img = $item.children('img:last'), //keep track of each items current image current = $item.data('current'); //out of bounds if(current > totalOtherImages - 1) current = 0; else if(current < 0) current = totalOtherImages - 1; //the next image to show and its //initial rotation (depends on dir) var otherRotation = (dir === 1) ? '-30deg' : '30deg', $other = $otherImages.eq(current).clone(); //for IE <= 8 we will not rotate, //but fade out / fade in ... //better than nothing :) if(!ieLte8) $other.css({ rotate : otherRotation, origin : origin }); (dir === 1) ? ++current : --current; //prepend the next image to the <li> $item.data('current', current).prepend($other); //the final rotation for the current image var rotateTo = (dir === 1) ? '80deg' : '-80deg'; if(!ieLte8) { $img.animate({ rotate : rotateTo }, 1200, function(){ $(this).remove(); ++completed; if(completed === 4) { completed = 0; isRotating = false; } }); $other.animate({ rotate : '0deg' }, 600); } else { $img.fadeOut(1200, function(){ $(this).remove(); ++completed; if(completed === 4) { completed = 0; isRotating = false; } }); } }, interval ); }); }, //set initial rotations configure = function() { if($.browser.msie && !ieLte8) rotateMaskCorners(); else if(ieLte8) hideMaskCorners(); $listItems.each(function(i) { //the initial current is 1 //since we already showing the first image var $item = $(this).data('current', 1); if(!ieLte8) $item.transform({rotate: $item.data('rotation') + 'deg'}) .find('img') .transform({origin: origin}); }); }, //rotates the masks and corners rotateMaskCorners = function() { $rm_mask_left.transform({rotate: '-3deg'}); $rm_mask_right.transform({rotate: '3deg'}); $rm_corner_left.transform({rotate: '45deg'}); $rm_corner_right.transform({rotate: '-45deg'}); }, //hides the masks and corners hideMaskCorners = function() { $rm_mask_left.hide(); $rm_mask_right.hide(); $rm_corner_left.hide(); $rm_corner_right.hide(); }, startSlideshow = function() { clearInterval(slideshowInterval); rotateImages(1); slideshowInterval = setInterval(function() { rotateImages(1); }, slideshowTime); //show the pause button and hide the play button $rm_play.hide(); $rm_pause.show(); }, stopSlideshow = function() { clearInterval(slideshowInterval); //show the play button and hide the pause button $rm_pause.hide(); $rm_play.show(); }; return {init : init};
As you noticed, we will treat older browsers a bit differently so that the slider works properly.
And that’s it! We really hope you enjoyed the tutorial and like the result!
Realy realy awesome! 😀
Awesome as always! Congrats Mary 🙂
Becoming a fan of yours, Mary 🙂 You are one jQuery Ninja 🙂
MaryLou… you are god. How do you come up with, and do these so fast, while working on your own paid projects?!?!?
Don’t work on Chromium 12 🙁
Hi, thanks for the post.
Buts it does not work for me in google chrome, the images aren´t displayed.
So nice 🙂 I’m was wondering what kind of slideshow I could use for my website first page….
Awesome MaryLou, another great tut!
I’ve just noticed that the controls for next / previous work only AFTER the animation is finished, if I click more than once, it does not go twice, only one, he should not trigger the second once the first animation was over?
Great job Mary thanks for share 😉
very nice 😉
Now that is nice, thanks man!
I mean ma’am 😉
You ROCK MARY !
Hey this was great, even though that in google chrome the lines are “pixelized”
That’s really a different and great slider.
nice tutorial and awesome effects…
thanks for sharing
…. Awesome
Thanks
Nice slider. Not the normal effect.
This is nice but somehow I’m not a fan of it. The code is great!
I ‘ll implement it on my wordpress blog
Very nice. Thanks for sharing. I’ve already got an idea for this one!
Awesome + Amazing + Nice!!!
Like This ^_^
I Love U Mary =¤
Waaoouuu!
Very nice and cool!!!!
you never fail to amaze me with your talent.
good job!
best website to my knowledge
for me it’s works only after downloading in local browsing.
Wow! Awesome effect!
Very nice and very clever!
The javascript feels a bit long, but that doesn’t necessarily mean it’s “bloated.” I’ll look it over and possibly give it a try. Thanks much.
Also (sorry for multiple posts), the Chrome ad-block extension causes a number of issues, chief of those being that it blocks the images themselves. Works fine under Firefox’s ABP.
Becoming a fan of yours.., Mary
we…e…e…e can be like that…
WOW!!!
download link doesn’t work, why?
Gianluca, can you tell me which browser you are using, and what error message you get? If you click on “download”, it should work. Thank you, ML
i’m on FF 4 and give me a 403 message
Thank you Gianluca! Can you try it again now? ML
You are such a Hero!!
This is an amazing slider, i now can’t wait to see what you will be doing next!
Ok, now it work correctly.
Thanks.
This doesn’t work on Chrome ?
Hi Tony! Is that a question or a statement? 🙂 Which version are you talking about and what exactly does not work? Cheers, ML
Thanks
Every time I check your tutorials, I ask myself, ‘Hmm what other ways can I put this to good good use?’. Thanks ML.
In IE 8 this script don’t work proper in both normal view & compatible view also. It is not showing effects of the images – it shows just plain square images. please advice. Thanks.
you never cease to amaze me. this one is ‘simple’ yet super-awesome
How do you get it to autoplay onload?
Mary Lou…. You are amazzzing!!! Love all your work. : )
Hy, i cannot manage to get links to pictures. I saw this problem in many of your jquery based galleries. Can it be fixed? Thnks, Alex
hola, soy de habla hispana y agradezco a quienes trabajan esta información, me agrada bastante
quisiera pedir un favor, como puedo yo añadir esto a mi blog de blogger de google, de que forma lo puedo instalar para que me funcione igual?
Gracias por su ayuda!
hi,
I like this slideshow and i would like start it automatically when the website opens. What should I change to get it
thank
You do the most amazing tutorial in the web.
Hi..
Any body here will help me as this Jquery is not working properly in IE 8.
In IE 8 this script don’t work proper in both normal view & compatible view also. It is not showing effects of the images – it shows just plain square images. please advice. Thanks.