Quantcast
Channel: slideshow – Codrops
Viewing all 53 articles
Browse latest View live

Audio Slideshow with jPlayer

$
0
0

Audio Slideshow with jPlayer

View demo Download source

Today we’ll share an audio slideshow with you. Using the open source audio framework jPlayer the slideshow will show images and play sound, changing the images at specified moments of the song/audio. While we’ll demonstrate a photo slideshow here, it is not limited to photos. It could also use div tags containing any kind of markup whatsoever.

The photos used in this tutorial are from the Library of Congress Flickr set. The mp3 is a Kurt Vile song that was downloaded from the Free Music Archive, a wonderful resource for Creative Commons Licensed songs.

So let’s take a look at how it’s done!

The Markup

We will require a container div, the class in the example is ‘audio-slideshow’, however it can be whatever you’d like. This tag has two HTML5 data attributes:

  1. data-audio: This is the path to the audio file
  2. data-audio-duration: This is the length of the audio file in seconds. This is required so we can position the markers on the timeline without having to load the audio file into the browser
<div class="audio-slideshow" data-audio="audio.mp3" data-audio-duration="161">
	<div class="audio-slides">
		<img src="image.jpg" data-thumbnail="thumbnail.jpg" data-slide-time="0">
		<img src="image.jpg" data-thumbnail="thumbnail.jpg" data-slide-time="1">
	</div>
	<div class="audio-control-interface">
		<div class="play-pause-container">
			<a href="javascript:;" class="audio-play" tabindex="1">Play</a>
			<a href="javascript:;" class="audio-pause" tabindex="1">Pause</a>
		</div>
		<div class="time-container">
			<span class="play-time"></span> / <span class="total-time"></span>
		</div>

		<div class="timeline">
			<div class="timeline-controls"></div>
			<div class="playhead"></div>
		</div>

	 	<div class="jplayer"></div>
	</div>
</div>

Within the .audio-slideshow container is a div with a class of .audio-slides. The child tags within this div can be anything you want. In this particular example, they are images. However, they could be divs that contain text. They can be whatever tag you want, as long as they have two HTML5 data attributes:

  1. data-thumbnail: This is the thumbnail that will show up when someone mouses over the marker
  2. data-slide-time: This is the time, in seconds, when this slide should be displayed.

The other tags represent the other items within the player: The play and pause button, the playhead and timeline…etc. The plugin only needs to know their class or id names.

The CSS

/* Component style */

.audio-slideshow {
	width: 640px;
	height: 520px;
	position: relative;
	margin: 0 auto;
}

.audio-slideshow .audio-slides {
	position: relative;
}

.audio-slideshow .audio-slides img {
	display: block;
	position: absolute;
	top: 0; left: 0;
}

.audio-slideshow .audio-control-interface {
	position: absolute;
	bottom: 0;
	left: 0;
	width: 100%;
	height: 48px;
}

.audio-slideshow .play-pause-container, .audio-slideshow .time-container {
	position: absolute;
	bottom: 25px;
	height: 18px;
	font-weight: bold;
	color: #777;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.1);
}

.audio-slideshow .play-pause-container a {
	outline: none;
	text-indent: -99999px;
	width: 16px;
	height: 16px;
	position: absolute;
}
.audio-slideshow .play-pause-container a.audio-play {
	background: transparent url(../images/play.png) no-repeat center center;
}

.audio-slideshow .play-pause-container a.audio-pause {
	background: transparent url(../images/pause.png) no-repeat center center;
}

.audio-slideshow .audio-control-interface .time-container {
	right: 3px;
}

.audio-slideshow .timeline {
	position: absolute;
	width: 100%;
	background-color: #fff;
	height: 20px;
	bottom: 0;
	left: 0;
	box-shadow: 0 1px 2px rgba(0,0,0,0.2);
}

.audio-slideshow .timeline .playhead {
	position: absolute;
	height: 20px;
	background: #333;
	width: 0;
}

.marker {
	width: 10px;
	height: 10px;
	border-radius: 5px;
	box-shadow: 1px 1px 1px rgba(0,0,0,0.4) inset;
	position: absolute;
	background: #B8BAC6;
	top: 5px;
}

.marker span {
	padding: 5px;
	position: absolute;
	bottom: 20px;
	opacity: 0;
	left: -50px;
	z-index: -1;
	box-shadow: 1px 1px 4px rgba(0,0,0,0.5);
	background: linear-gradient(top, #f5f6f6 0%,#dbdce2 21%,#b8bac6 49%,#dddfe3 80%,#f5f6f6 100%);
	transition: all 0.3s ease-in-out;
}

.marker span img {
	display: block;
}

.marker:hover span {
	opacity: 1;
	z-index: 100;
}

As you can see from the CSS, pretty much everything can be customized. The timeline could be larger, smaller, above or even on top of the slides. The slides could be scaled to the size of the browser and the markers could be graphics instead of CSS circles.

The Javascript

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js?84cd58"></script>
<script>window.jQuery || document.write('>script src="/lib/js/jquery-1.7.1.min.js?84cd58"><\/script>')</script>
<script src="jplayer/jquery.jplayer.js"></script>
<script src="js/jquery.audioslideshow.js"></script>
<script>
	$(document).ready(function() {
		$('.audio-slideshow').audioSlideshow();
	});
</script>

You will need three scripts to set this up:

  1. jQuery (although I recommend utilizing Google’s CDN as shown above)
  2. jPlayer
  3. the AudioSlideshow plugin

Once those scripts are added to the page, you just need to call audioSlideshow on any tag that you want.

If you’ve changed any of the tag selectors, you can let the plugin know this by setting any of the following settings:

<script>
	$(document).ready(function() {
		$('.audio-slideshow').audioSlideshow(
			{
				jPlayerPath: "/lib/swf",
				suppliedFileType: "mp3",
				playSelector: ".audio-play",
				pauseSelector: ".audio-pause",
				currentTimeSelector: ".play-time",
				durationSelector: ".total-time",
				playheadSelector: ".playhead",
				timelineSelector: ".timeline"
			}
		);
	});
</script>

Audio Slideshow with jPlayer on Github.

View demo Download source


Fluid CSS3 Slideshow with Parallax Effect

$
0
0

Fluid CSS3 Slideshow with Parallax Effect

View demo Download source

In this tutorial, we are going to create a slideshow with a parallax effect with the help of some CSS3 properties. We’ll use radio buttons and sibling combinators for controlling which slide is shown. There will be two backgrounds and the idea is to change the background positions and the position of the slider with transitions in order to create a slight parallax effect.

The graphics used in the demo are by: 5Milli (Global Vector Map) and by WeGraphics (Free Vector Infographic Kit).

Please note: the result of this tutorial will only work as intended in browsers that support the respective CSS properties.

The Markup

We will “connect” the input elements to the division with the class sp-content by using the general sibling combinator. For that we will leave the inputs on the same level like the sp-content div. When we click on an input we will change the background color and background position of it (grid pattern) and also the background-position of the sp-parallax-bg div (the world map) with transitions. The respective slide will be shown by moving the sp-slider ul to the right position. The markup looks as follows:

<div class="sp-slideshow">
			
	<input id="button-1" type="radio" name="radio-set" class="sp-selector-1" checked="checked" />
	<label for="button-1" class="button-label-1"></label>
	
	<input id="button-2" type="radio" name="radio-set" class="sp-selector-2" />
	<label for="button-2" class="button-label-2"></label>
	
	<input id="button-3" type="radio" name="radio-set" class="sp-selector-3" />
	<label for="button-3" class="button-label-3"></label>
	
	<input id="button-4" type="radio" name="radio-set" class="sp-selector-4" />
	<label for="button-4" class="button-label-4"></label>
	
	<input id="button-5" type="radio" name="radio-set" class="sp-selector-5" />
	<label for="button-5" class="button-label-5"></label>
	
	<label for="button-1" class="sp-arrow sp-a1"></label>
	<label for="button-2" class="sp-arrow sp-a2"></label>
	<label for="button-3" class="sp-arrow sp-a3"></label>
	<label for="button-4" class="sp-arrow sp-a4"></label>
	<label for="button-5" class="sp-arrow sp-a5"></label>
	
	<div class="sp-content">
		<div class="sp-parallax-bg"></div>
		<ul class="sp-slider clearfix">
			<li><img src="images/image1.png" alt="image01" /></li>
			<li><img src="images/image2.png" alt="image02" /></li>
			<li><img src="images/image3.png" alt="image03" /></li>
			<li><img src="images/image4.png" alt="image04" /></li>
			<li><img src="images/image5.png" alt="image05" /></li>
		</ul>
	</div><!-- sp-content -->
	
</div><!-- sp-slideshow -->

The list elements are the wrappers for each slide and although we are using simply images here, you can use any kind of content.

The CSS

We’ll set the width of the main container to 80% and set the width of the divisions with class sp-content and class sp-parallax-bg to 100%. The sp-content div will have a background color and a background image (grid) that we will move whenever we slide the slider ul. The sp-parallax-bg div will have a map as background image and we will also move the background position.

.sp-slideshow {
    position: relative;
	margin: 10px auto;
	width: 80%;
	max-width: 1000px;
	min-width: 260px;
	height: 460px;
	border: 10px solid #fff;
	border: 10px solid rgba(255,255,255,0.9);
    box-shadow: 0 2px 6px rgba(0,0,0,0.2);
}

.sp-content {
    background: #7d7f72 url(../images/grid.png) repeat scroll 0 0;
	position: relative;
	width: 100%;
	height: 100%;
	overflow: hidden;
}

.sp-parallax-bg {
    background: url(../images/map.png) repeat-x scroll 0 0;
    background-size: cover;
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	overflow: hidden;
}

The styles of the inputs and the labels:

.sp-slideshow input {
    position: absolute;
	bottom: 15px;
	left: 50%;
	width: 9px;
	height: 9px;
	z-index: 1001;
	cursor: pointer;
    opacity: 0;
}

.sp-slideshow input + label {
    position: absolute;
    bottom: 15px;
	left: 50%;
    width: 6px;
	height: 6px;
	display: block;
	z-index: 1000;
	border: 3px solid #fff;
	border: 3px solid rgba(255,255,255,0.9);
    border-radius: 50%;
    transition: background-color linear 0.1s;
}
.sp-slideshow input:checked + label {
	background-color: #fff;
    background-color: rgba(255,255,255,0.9);
}

.sp-selector-1, .button-label-1 {
    margin-left: -36px;
}

.sp-selector-2, .button-label-2 {
    margin-left: -18px;
}

.sp-selector-4, .button-label-4 {
    margin-left: 18px;
}

.sp-selector-5, .button-label-5 {
    margin-left: 36px;
}

We’ve set the opacity of the inputs to 0 so that they are not visible. The labels are under the radio button and we will make it look like a little circle. All the inputs and labels will be positioned absolutely and we will place them next to each other by applying a left margin.

Next, we will style the arrows which are simply labels with the respective for attribute. Note, that clicking on a label to active an associated input might not work in mobile browsers. But anyway, you can navigate using the dots since we are actually clicking on the inputs.

The arrow labels have the following style:

.sp-arrow {
    position: absolute;
	top: 50%;
	width: 28px;
	height: 38px;
	margin-top: -19px;
	display: none;
	opacity: 0.8;
	cursor: pointer;
	z-index: 1000;
	background: transparent url(../images/arrows.png) no-repeat;
    transition: opacity linear 0.3s;
}
.sp-arrow:hover{
	opacity: 1;
}
.sp-arrow:active{
	margin-top: -18px;
}

Now, let’s define when each arrow is shown. On the first slide we, for example, don’t want to show the left arrow. And on the last slide we don’t want to show the right arrow:

.sp-selector-1:checked ~ .sp-arrow.sp-a2,
.sp-selector-2:checked ~ .sp-arrow.sp-a3,
.sp-selector-3:checked ~ .sp-arrow.sp-a4,
.sp-selector-4:checked ~ .sp-arrow.sp-a5 {
    right: 15px;
	display: block;
	background-position: top right;
}
.sp-selector-2:checked ~ .sp-arrow.sp-a1,
.sp-selector-3:checked ~ .sp-arrow.sp-a2,
.sp-selector-4:checked ~ .sp-arrow.sp-a3,
.sp-selector-5:checked ~ .sp-arrow.sp-a4 {
    left: 15px;
	display: block;
	background-position: top left;
}

When an input is selected, the sp-content div will have a transition for the background-position and the background-color. The second transition is going to take a bit longer:

.sp-slideshow input:checked ~ .sp-content {
    transition: background-position linear 0.6s, background-color linear 0.8s;
}

The div with the world map (sp-parallax-bg) will also have a transition for the background-position:

.sp-slideshow input:checked ~ .sp-content .sp-parallax-bg {
    transition: background-position linear 0.7s;
}

In this way we can add a background parallax effect.

Let’s define the changes to color and background-position for the sp-content div:

input.sp-selector-1:checked ~ .sp-content {
    background-position: 0 0;
	background-color: #727b7f;
}

input.sp-selector-2:checked ~ .sp-content {
    background-position: -100px 0;
	background-color: #7f7276;
}

input.sp-selector-3:checked ~ .sp-content {
    background-position: -200px 0;
	background-color: #737f72;
}

input.sp-selector-4:checked ~ .sp-content {
    background-position: -300px 0;
	background-color: #79727f;
}

input.sp-selector-5:checked ~ .sp-content {
    background-position: -400px 0;
	background-color: #7d7f72;
}

… and the sp-parallax-bg div:

input.sp-selector-1:checked ~ .sp-content .sp-parallax-bg {
    background-position: 0 0;
}

input.sp-selector-2:checked ~ .sp-content .sp-parallax-bg {
    background-position: -200px 0;
}

input.sp-selector-3:checked ~ .sp-content .sp-parallax-bg {
    background-position: -400px 0;
}

input.sp-selector-4:checked ~ .sp-content .sp-parallax-bg {
    background-position: -600px 0;
}

input.sp-selector-5:checked ~ .sp-content .sp-parallax-bg {
    background-position: -800px 0;
}

The unordered list with the class sp-slider will have a width of 500%. This is because we have 5 slides. It will have a transition for the left value, that we will change depening on the input that is checked:

.sp-slider {
    position: relative;
	left: 0;
    width: 500%;
	height: 100%;
	list-style: none;
    margin: 0;
	padding: 0;
    transition: left ease-in 0.8s; 
}

Each list element is a slide and it will also have a transition for the opacity. We will give both, the slide and the image inside the box-sizing property of “border-box”. This will allow us to set a padding but also use 100% values for heights and widths and not worry about any overflow:

.sp-slider > li {
	color: #fff;
	width: 20%;
	box-sizing: border-box;
	height: 100%;
	padding: 0 60px;
    float: left;
	text-align: center;
	opacity: 0.4;
    transition: opacity ease-in 0.4s 0.8s; 
}
.sp-slider > li img{
	box-sizing: border-box;
	display: block;
	margin: 0 auto;
	padding: 40px 0 50px 0;
	max-height: 100%;
	max-width: 100%;
}

Now we need to set the correct left values for each selected slide:

input.sp-selector-1:checked ~ .sp-content .sp-slider {
    left: 0;
}

input.sp-selector-2:checked ~ .sp-content .sp-slider {
    left: -100%;
}

input.sp-selector-3:checked ~ .sp-content .sp-slider {
    left: -200%;
}

input.sp-selector-4:checked ~ .sp-content .sp-slider {
    left: -300%;
}

input.sp-selector-5:checked ~ .sp-content .sp-slider {
    left: -400%;
}

Each current slide will then get opacity 1:

input.sp-selector-1:checked ~ .sp-content .sp-slider > li:first-child,
input.sp-selector-2:checked ~ .sp-content .sp-slider > li:nth-child(2),
input.sp-selector-3:checked ~ .sp-content .sp-slider > li:nth-child(3),
input.sp-selector-4:checked ~ .sp-content .sp-slider > li:nth-child(4),
input.sp-selector-5:checked ~ .sp-content .sp-slider > li:nth-child(5){
	opacity: 1;
}

That’s all, hope you like it!

View demo Download source

How to Create a Fast Hover Slideshow with CSS3

$
0
0

How to Create a Fast Hover Slideshow with CSS3

View demo Download source

Today I want to share a little hover slideshow with you. The main idea is to run a super-fast image slideshow on hover and show the current image when mousing out. It’s just a fancy effect to play with and I got the idea from Contain.r. We’ll be using CSS animations for the slideshow and control the pausing and running with animation-play-state.

Please note: this is just experimental and will only work as intended in browsers that support the respective CSS properties.

This is our simple structure:

<div class="hs-wrapper">

	<img src="images/1.jpg" alt="image01"/>
	<img src="images/2.jpg" alt="image02"/>
	<img src="images/3.jpg" alt="image03"/>
	<img src="images/4.jpg" alt="image04"/>
	<img src="images/5.jpg" alt="image05"/>
	<img src="images/6.jpg" alt="image06"/>
	<img src="images/7.jpg" alt="image07"/>
	<img src="images/8.jpg" alt="image08"/>
	
	<div class="hs-overlay">
		<span>Summer <strong>2012</strong></span>
	</div>
	
</div>

We’ll show and hide the images using an animation:

.hs-wrapper img{
	top: 0px;
	left: 0px;
	position: absolute;
	animation: showMe 0.8s linear infinite 0s forwards;
	animation-play-state: paused;	
}

The animation will be paused and we’ll just run it on hover:

.hs-wrapper:hover img{
	animation-play-state: running;
}

The animation will simply make an image visible and put it on top of the “stack”:

@keyframes showMe {
    0% { visibility: visible; z-index: 100; }
	12.5% { visibility: visible; z-index: 100; }
	25% { visibility: hidden; z-index: 0; }
    100% { visibility: hidden; z-index: 0; }
}

As you can see, each image will have the same animation but we will start the animation for each image with a delay. We also want to reverse the stacking order of the images by setting the z-index accordingly. Since we will run the whole animation for 0.8 seconds, we’ll delay the start for each image with 0.1 second (except the first one):

.hs-wrapper img:nth-child(1){
	z-index: 9;
}
.hs-wrapper img:nth-child(2){
	animation-delay: 0.1s;
	z-index: 8;
}
.hs-wrapper img:nth-child(3){
	animation-delay: 0.2s;
	z-index: 7;
}
.hs-wrapper img:nth-child(4){
	animation-delay: 0.3s;
	z-index: 6;
}

The percentages of the animation are calculated as follows: take the 100% of the animation timespan and distribute 8 images over it. Each one runs 0.1 seconds which means that at 12.5% we want the second image to show. The second image that will show, will be on top of the current one (although they have the same z-index) because it follows in the HTML structure. Having tried some other possibilities (i.e. not messing around with the z-index, setting 12.6% as the next step, etc.) it seems that this variant performs the smoothest. Nonetheless, sometimes there seems to be a bit of a lag.

animation

The overlay, which will be shown on hover, will have an absolute position and since we want to fade it in and animate the box shadow, we’ll add a transition to it:

.hs-overlay{
	position: absolute;
	width: 100%;
	height: 100%;
	opacity: 0;
	z-index: 500;
	background: rgba(0,0,0,0.6);
	box-shadow: 0 0 0 0 rgba(255,255,255,0.3) inset;
	pointer-events: none;
	transition: all 0.3s linear;
}

On hover, when the slideshow starts running, we’ll show the overlay:

.hs-wrapper:hover .hs-overlay{
	opacity: 1;
	box-shadow: 0 0 0 5px rgba(255,255,255,0.3) inset;
}

You can increase the opacity level of the background color of the overlay in order to make the effect more subtle. Note, that the images have the same background which makes this effect less seizure-inducing than if you would use a different color for each one. Black and white images with a not too transparent overlay like in Contain.r are a good fit for this effect.

It’s as well important to note that it should be made sure that the images are of course loaded and apply something similar to what Chris Coyier suggests here.

And that’s it! I hope you find it inspiring.

View demo Download source

Fullscreen Slit Slider with jQuery and CSS3

$
0
0

Fullscreen Slit Slider with jQuery and CSS3

View demo Download source

In this tutorial we’ll create a fullscreen slideshow with a twist: we’ll slice the current slide open in order to reveal the next or previous slide. Using different data-attributes, we’ll define the type, rotation angle and scale of a slide’s parts, giving us the possibility to create unique effects of each slide transition.

We’ll be using jQuery cond, jQuery plugin by Ben Alman for chainable “if-then-else” statements.

The animal icon font that we’ll be using is by Alan Carr and you can find it here.

The images in the second demo are by Majownik and they are licensed under a Creative Commons Attribution License.

Please note: the result of this tutorial will only work as intended in browsers that support the respective CSS properties.

Let’s start with the HTML.

The Markup

Our initial markup will consist of a main container with the class and id sl-slide-wrapper which will hold the slider and all the slides, each one having the class sl-slide. Then, we will add two types of navigation, one will be the arrows to go to the previous and next slide and the other will be the dots for navigating directly to a certain slide. We’ll also add a background class to each slide which will control the background color of every slide.

<div id="slider" class="sl-slider-wrapper">

	<div class="sl-slider">
	
		<div class="sl-slide bg-1">
			<div class="sl-slide-inner">
				<div class="deco" data-icon="H"></div>
				<h2>A bene placito</h2>
				<blockquote>
					<p>You have just dined...</p>
					<cite>Ralph Waldo Emerson</cite>
				</blockquote>
			</div>
		</div>
		
		<div class="sl-slide bg-2">
			<div class="sl-slide-inner">
				<div class="deco" data-icon="q"></div>
				<h2>Regula aurea</h2>
				<blockquote>
					<p>Until he extends the circle...</p>
					<cite>Albert Schweitzer</cite>
				</blockquote>
			</div>
		</div>

		<div class="sl-slide bg-2">
			<!-- ... -->
		</div>

		<!-- ... -->

	</div>

	<nav id="nav-arrows" class="nav-arrows">
		<span class="nav-arrow-prev">Previous</span>
		<span class="nav-arrow-next">Next</span>
	</nav>

	<nav id="nav-dots" class="nav-dots">
		<span class="nav-dot-current"></span>
		<span></span>
		<span></span>
		<span></span>
		<span></span>
	</nav>

</div>

Every slide will also have some data-attributes that we will use in order to control the effect for each slide. The data attributes that we want are the following:

data-orientation
data-slice1-rotation
data-slice2-rotation
data-slice1-scale 
data-slice2-scale

The first one, data-orientation should be either “vertical” or “horizontal”. This we need in order to know where to “slice” the slide. It will be either slice horizontally or vertically. The data-slice1-rotation and data-slice2-rotation value will be the rotation degree for each one of the slices and the data-slice1-scale and data-slice2-scale value will be the scale value.

So, our first slide will have something like this:

<div class="sl-slide" data-orientation="horizontal" data-slice1-rotation="-25" data-slice2-rotation="-25" data-slice1-scale="2" data-slice2-scale="2">

Our structure is a “base structure”. We will build upon that structure using JavaScript in order to be able to create the effects. So, we will want to transform it into this (each slide will also have the data attributes):

<div id="slider" class="sl-slider-wrapper">

	<div class="sl-slider">
			
		<div class="sl-slides-wrapper">
		
			<div class="sl-slide bg-1 sl-slide-horizontal">
				<div class="sl-content-wrapper">
					<div class="sl-content">
						<!-- the content -->
					</div>
				</div>
			</div>
			
			<!-- ... -->
			
		</div>

	</div>

	<!-- navs -->
		
</div>

The content will basically be our “sl-slide-inner” and everything that is inside of it.

In the moment that we navigate to the next or previous slide we will take the current slide and duplicate its content wrapper, creating the “slices”:

<div class="sl-slide sl-slide-horizontal" >

	<div class="sl-content-slice">
		<div class="sl-content-wrapper">
			<div class="sl-content">
				<!-- ... -->
			</div>
		</div>
	</div>
	
	<div class="sl-content-slice">
		<div class="sl-content-wrapper">
			<div class="sl-content">
				<!-- ... -->
			</div>
		</div>
	</div>
	
</div>
					

Now, let’s style it!

The CSS

Note, that we will omit vendor prefixes here.

Since we will make a reusable plugin out of this we don’t want the slider to be fullscreen by default. So, we won’t set the default style of the wrapper to 100% width and height but to this:

.sl-slider-wrapper {
	width: 800px;
	height: 400px;
	margin: 0 auto;
	position: relative;
	overflow: hidden;
}

The slider will be of position absolute and we’ll set the top and left to zero. The width and height will be set dynamically with JavaScript.
The slides, the dyamic wrapper and the inner part will all need absolute positioning and they’ll have a width and height of 100%:


.sl-slider {
	position: absolute;
	top: 0;
	left: 0;
}

.sl-slide,
.sl-slides-wrapper,
.sl-slide-inner {
	position: absolute;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
} 

Each slide should have a z-index of 1; we’ll control the appearance and the stacking of the slides with JavaScript:

.sl-slide {
	z-index: 1;
}

The content “slices” will be positioned absolutely and their common style is the following:

/* The duplicate parts/slices */

.sl-content-slice {
	overflow: hidden;
	position: absolute;
	box-sizing: content-box;
	background: #fff;
}

We use box-sizing: content-box here because by default we use border-box .

The content “slices” will be horizontal or vertical, meaning that either the height or the width will be half of the screensize. In order to avoid seeing the edges of a slice when we rotate it, we’ll add some paddings.

/* Horizontal slice */

.sl-slide-horizontal .sl-content-slice {
	width: 100%;
	height: 50%;
	left: -200px;
}

.sl-slide-horizontal .sl-content-slice:first-child {
	top: -200px;
	padding: 200px 200px 0px 200px;
}

.sl-slide-horizontal .sl-content-slice:nth-child(2) {
	top: 50%;
	padding: 0px 200px 200px 200px;
}

/* Vertical slice */

.sl-slide-vertical .sl-content-slice {
	width: 50%;
	height: 100%;
	top: -200px;
}

.sl-slide-vertical .sl-content-slice:first-child {
	left: -200px;
	padding: 200px 0px 200px 200px;
}

.sl-slide-vertical .sl-content-slice:nth-child(2) {
	left: 50%;
	padding: 200px 200px 200px 0px;
}

We use negative position values in order to “pull” the divisions into place.

Let’s style the content wrapper and the content division:

/* Content wrapper */
/* Width and height is set dynamically */

.sl-content-wrapper {
	position: absolute;
}

.sl-content {
	width: 100%;
	height: 100%;
	background: #fff;
}

The division with the class sl-content-wrapper will get a height and width dynamically. If, for example, the slide is horizontal, the wrapper will have a width of 100% of the parent’s container width and 50% of the parent’s container height. The wrapper of the second slice will also have a negative top (horizontal) or left (vertical) margin in order to “pull” the duplicated content up or to the left.

This is all the styling for the slider to work by default. Let’s take a look at some custom elements, like the nav items and the content elements that we’ll animate when we navigate through the slides.

For the navigation arrows we will use this image-less technique. We’ll simply have a little box and rotate it 45 degrees. Then we’ll add some border to the sides and voilà, we made ourselves some neat arrows:

/* Custom navigation arrows */

.nav-arrows span {
	position: absolute;
	z-index: 2000;
	top: 50%;
	width: 40px;
	height: 40px;
	border: 8px solid #ddd;
	border: 8px solid rgba(150,150,150,0.4);
	text-indent: -90000px;
	margin-top: -40px;
	cursor: pointer;
	transform: rotate(45deg);
}

.nav-arrows span:hover {
	border-color: rgba(150,150,150,0.9);
}

.nav-arrows span.nav-arrow-prev {
	left: 5%;
	border-right: none;
	border-top: none;
}

.nav-arrows span.nav-arrow-next {
	right: 5%;
	border-left: none;
	border-bottom: none;
}

Now, let’s style the navigation dots. We’ll position them absolutely and make the span display as inline-blocks so that we can somply center them. They will look like little dots because we’ll apply a border radius of 50% to them. With some box shadows, we’ll make them look inset. The currently active navigation dot will have a pseudo-element (white dot) which will be placed on top of it:

.nav-dots {
	text-align: center;
	position: absolute;
	bottom: 2%;
	height: 30px;
	width: 100%;
	left: 0;
	z-index: 1000;
}

.nav-dots span {
	display: inline-block;
	position: relative;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	margin: 3px;
	background: #ddd;
	background: rgba(150,150,150,0.4);
	cursor: pointer;
	box-shadow: 
		0 1px 1px rgba(255,255,255,0.4), 
		inset 0 1px 1px rgba(0,0,0,0.1);
}

.nav-dots span.nav-dot-current:after {
	content: "";
	position: absolute;
	width: 10px;
	height: 10px;
	top: 3px;
	left: 3px;
	border-radius: 50%;
	background: rgba(255,255,255,0.8);
}

The elements that we’ll use in the content will be a decorative element (the animal with the circles), a headline and a blockquote. We’ll use a font to give us some cute animal “icons” that we’ll place as a pseudo element of the decorative div.

The division with the class deco, just like all the other content elements, will have an absolute position. We’ll center it horizontally and give it a bottom value of 50%:

.deco {
	width: 260px;
	height: 260px;
	border: 2px dashed #ddd;
	border: 2px dashed rgba(150,150,150,0.4);
	border-radius: 50%;
	position: absolute;
	bottom: 50%;
	left: 50%;
	margin: 0 0 0 -130px;
}

We use a data attribute “data-icon” in the decorative element and we’ll style the pseudo-element :after to contain the letter from the animal icon font as its content:

[data-icon]:after {
    content: attr(data-icon);
    font-family: 'AnimalsNormal';
	color: #999;
	text-shadow: 0 0 1px #999;
	position: absolute;
	width: 220px;
	height: 220px;
	line-height: 220px;
	text-align: center;
	font-size: 100px;
	top: 50%;
	left: 50%;
	margin: -110px 0 0 -110px;
	box-shadow: inset 0 0 0 10px #f7f7f7;
	border-radius: 50%;
}

The box shadow will create a “fake” inset border.

The headline will also be positioned absolutely and we’ll give it the same bottom value like we gave to the decorative element, which is 50%. We then add a negative bottom margin in order to place it under the other element. Like that we can use the decorative element as a reference point and position the other elements relatively to it using a negative bottom margin:

.sl-slide h2 {
	color: #000;
	text-shadow: 0 0 1px #000;
	padding: 20px;
	position: absolute;
	font-size: 34px;
	font-weight: 700;
	letter-spacing: 13px;
	text-transform: uppercase;
	width: 80%;
	left: 10%;
	text-align: center;
	line-height: 50px;
	bottom: 50%;
	margin: 0 0 -120px 0;
}

The blockquote will be of 100% width and we will center the paragraph inside which will have a maximum width of 400 pixel:

.sl-slide blockquote {
	position: absolute;
	width: 100%;
	text-align: center;
	left: 0;
	font-weight: 400;
	font-size: 14px;
	line-height: 20px;
	height: 70px;
	color: #8b8b8b;
	z-index: 2;
	bottom: 50%;
	margin: 0 0 -200px 0;
	padding: 0;
}

.sl-slide blockquote p{
	margin: 0 auto;
	width: 60%;
	max-width: 400px;
	position: relative;
}

Let’s add a quotation mark to the blockquote. Using the pseudo-class :before, we’ll add a over-sized quotation mark behind the blockquote:

.sl-slide blockquote p:before {
	color: #f0f0f0;
	color: rgba(244,244,244,0.65);
	font-family: "Bookman Old Style", Bookman, Garamond, serif;
	position: absolute;
	line-height: 60px;
	width: 75px;
	height: 75px;
	font-size: 200px;
	z-index: -1;
	left: -80px;
	top: 35px;
	content: '\201C';
}

And the cite will have a different look:

.sl-slide blockquote cite {
	font-size: 10px;
	padding-top: 10px;
	display: inline-block;
	font-style: normal;
	text-transform: uppercase;
	letter-spacing: 4px;
}

Next, we’ll define some classes for controling the colors of the slides. When we give this color class to the slide, we want the background color and the color of the elements to be different. By default, our slides are white/gray and the content elements are black and gray.

We also need to give the same background color to the dynamic slice element, the one that will have a large padding:

/* First Slide */
.bg-1 .sl-slide-inner,
.bg-1 .sl-content-slice {
	background: #fff;
}

/* Second Slide */
.bg-2 .sl-slide-inner,
.bg-2 .sl-content-slice {
	background: #000;
}

.bg-2 [data-icon]:after,
.bg-2 h2 {
	color: #fff;
}

.bg-2 blockquote:before {
	color: #222;
}

/* Third Slide */
.bg-3 .sl-slide-inner,
.bg-3 .sl-content-slice {
	background: #db84ad;
}

.bg-3 .deco {
	border-color: #fff;
	border-color: rgba(255,255,255,0.5);
}

.bg-3 [data-icon]:after {
	color: #fff;
	text-shadow: 0 0 1px #fff;
	box-shadow: inset 0 0 0 10px #b55381;
}

.bg-3 h2,
.bg-3 blockquote{
	color: #fff;
	text-shadow: 0px 1px 1px rgba(0,0,0,0.3);
}

.bg-3 blockquote:before {
	color: #c46c96;
}

/* Forth Slide */
.bg-4 .sl-slide-inner,
.bg-4 .sl-content-slice {
	background: #5bc2ce;
}

.bg-4 .deco {
	border-color: #379eaa;
}

.bg-4 [data-icon]:after {
	text-shadow: 0 0 1px #277d87;
	color: #277d87;
}

.bg-4 h2,
.bg-4 blockquote{
	color: #fff;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.2);
}

.bg-4 blockquote:before {
	color: #379eaa;
}

/* Fifth Slide */
.bg-5 .sl-slide-inner,
.bg-5 .sl-content-slice {
	background: #ffeb41;
}

.bg-5 .deco {
	border-color: #ECD82C;
}

.bg-5 .deco:after {
	color: #000;
	text-shadow: 0 0 1px #000;
}

.bg-5 h2,
.bg-5 blockquote{
	color: #000;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.1);
}

.bg-5 blockquote:before {
	color: #ecd82c;
}

And now, let’s add some motion to the content elements! When we navigate the slides, we want the content elements to do something fun, so we will add a class to the next slide whenever we navigate to the “right” (or “in” since this almost looks like as if we are moving further). That class will “trigger” an animation for each one of the content elements:


/* Animations for elements */

.sl-trans-elems .deco{
	animation: roll 1s ease-out both;
}

.sl-trans-elems h2{
	animation: moveUp 1s ease-in-out both;
}

.sl-trans-elems blockquote{
	animation: fadeIn 0.5s linear 0.5s both;
}

@keyframes roll{
	0% {transform: translateX(500px) rotate(360deg); opacity: 0;}
	100% {transform: translateX(0px) rotate(0deg); opacity: 1;}
}

@keyframes moveUp{
	0% {transform: translateY(40px);}
	100% {transform: translateY(0px);}
}

@keyframes fadeIn{
	0% {opacity: 0;}
	100% {opacity: 1;}
}

The decorative element will “roll in” from the right side, the heading will move up and the blockquote will simply fade in.

Now, when we navigate back (or “out”), we want to see the reverse happening:

.sl-trans-back-elems .deco{
	animation: scaleDown 1s ease-in-out both;
}

.sl-trans-back-elems h2{
	animation: fadeOut 1s ease-in-out both;
}

.sl-trans-back-elems blockquote{
	animation: fadeOut 1s linear both;
}

@keyframes scaleDown{
	0% {transform: scale(1);}
	100% {transform: scale(0.5);}
}

@keyframes fadeOut{
	0% {opacity: 1;}
	100% {opacity: 0;}
}

Here we will scale down the decorative element and simply fade out the rest.
And that’s all the style! Let’s take a look at the JavaScript.

The JavaScript

Let’s first take a look at our plugin options:

$.Slitslider.defaults 	= {
	// transitions speed
	speed : 800,
	// if true the item's slices will also animate the opacity value
	optOpacity : false,
	// amount (%) to translate both slices - adjust as necessary
	translateFactor : 230,
	// maximum possible angle
	maxAngle : 25,
	// maximum possible scale
	maxScale : 2,
	// slideshow on / off
	autoplay : false,
	// keyboard navigation
	keyboard : true,
	// time between transitions
	interval : 4000,
	// callbacks
	onBeforeChange : function( slide, idx ) { return false; },
	onAfterChange : function( slide, idx ) { return false; }
};

We can set the speed of the transitions, set the slideshow to play automatically with a specific interval and also make the slide’s slices opacity change during the transition.

The translateFactor option is the amount in percentage for translating both slices. You can adjust this value as necessary as you change the slide’s scale and angle data attributes and the maxAngle and maxScale values.

We will start by executing the _init function.


_init				: function( options ) {
			
	// options
	this.options = $.extend( true, {}, $.Slitslider.defaults, options );

	// https://github.com/twitter/bootstrap/issues/2870
	this.transEndEventNames = {
		'WebkitTransition' : 'webkitTransitionEnd',
		'MozTransition' : 'transitionend',
		'OTransition' : 'oTransitionEnd',
		'msTransition' : 'MSTransitionEnd',
		'transition' : 'transitionend'
	};
	this.transEndEventName = this.transEndEventNames[ Modernizr.prefixed( 'transition' ) ];
	// suport for css 3d transforms and css transitions
	this.support = Modernizr.csstransitions && Modernizr.csstransforms3d;
	// the slider
	this.$el = this.$elWrapper.children( '.sl-slider' );
	// the slides
	this.$slides = this.$el.children( '.sl-slide' ).hide();
	// total slides
	this.slidesCount = this.$slides.length;
	// current slide
	this.current = 0;
	// control if it's animating
	this.isAnimating = false;
	// get container size
	this._getSize();
	// layout
	this._layout();
	// load some events
	this._loadEvents();
	// slideshow
	if( this.options.autoplay ) {
	
		this._startSlideshow();
	
	}
	
}

Let’s take a look at the _layout function:


_layout				: function() {
			
	this.$slideWrapper = $( '<div class="sl-slides-wrapper" />' );
			
	// wrap the slides
	this.$slides.wrapAll( this.$slideWrapper ).each( function( i ) {
		
		var $slide = $( this ),
			// vertical || horizontal
			orientation = $slide.data( 'orientation' );
			
		$slide.addClass( 'sl-slide-' + orientation )
			  .children()
			  .wrapAll( '<div class="sl-content-wrapper" />' )
			  .wrapAll( '<div class="sl-content" />' );
	
	} );
	
	// set the right size of the slider/slides for the current window size
	this._setSize();
	// show first slide
	this.$slides.eq( this.current ).show();
	
}

We are wrapping the slides into a division with the class “sl-slides-wrapper”. As we’ve mentioned before, each slide’s content will also be wrapped by two divisions, one with the class sl-content and one with the class sl-content-wrapper.

We also add the respective orientation class to the slide (sl-slide-vertical or sl-slide-horizontal).

The slider and its sl-content-wrapper division need to have the main container’s width and height. That’s what we do in the _setSize function.

Finally, we’ll show the current/first slide.

In the _loadEvents function we will bind the click events for the keyboard navigation and the resize (smartresize) event to the window:


_loadEvents			: function() {
			
	var self = this;
			
	$window.on( 'debouncedresize.slitslider', function( event ) {
		
		// update size values
		self._getSize();
		// set the sizes again
		self._setSize();
		
	} );

	if ( this.options.keyboard ) {
		
		$document.on( 'keydown.slitslider', function(e) {

			var keyCode = e.keyCode || e.which,
				arrow = {
					left: 37,
					up: 38,
					right: 39,
					down: 40
				};

			switch (keyCode) {
				
				case arrow.left :

					self._stopSlideshow();
					self._navigate( 'prev' );
					break;
				
				case arrow.right :
					
					self._stopSlideshow();
					self._navigate( 'next' );
					break;

			}

		} );

	}

}

Let’s see how we “slice” the slides and move to the next one:


_navigate : function( dir, pos ) {
	
	if( this.isAnimating || this.slidesCount < 2 ) {
	
		return false;
	
	}

	this.isAnimating = true;

	var self = this,
		$currentSlide = this.$slides.eq( this.current );

	// if position is passed
	if( pos !== undefined ) {

		this.current = pos;

	}
	// if not check the boundaries
	else if( dir === 'next' ) {

		this.current = this.current < this.slidesCount - 1 ? ++this.current : 0;

	}
	else if( dir === 'prev' ) {

		this.current = this.current > 0 ? --this.current : this.slidesCount - 1;

	}

	this.options.onBeforeChange( $currentSlide, this.current );
	
	// next slide to be shown
	var $nextSlide = this.$slides.eq( this.current ),
		// the slide we want to cut and animate
		$movingSlide = ( dir === 'next' ) ? $currentSlide : $nextSlide,
		
		// the following are the data attrs set for each slide
		configData = $movingSlide.data(),
		config = {};
	
	config.orientation = configData.orientation || 'horizontal',
	config.slice1angle = configData.slice1Rotation || 0,
	config.slice1scale = configData.slice1Scale || 1,
	config.slice2angle = configData.slice2Rotation || 0,
	config.slice2scale = configData.slice2Scale || 1;
		
	this._validateValues( config );
	
	var cssStyle = config.orientation === 'horizontal' ? {
			marginTop : -this.size.height / 2
		} : {
			marginLeft : -this.size.width / 2
		},
		// default slide's slices style
		resetStyle = {
			'transform' : 'translate(0%,0%) rotate(0deg) scale(1)',
			opacity : 1 
		},
		// slice1 style
		slice1Style	= config.orientation === 'horizontal' ? {
			'transform' : 'translateY(-' + this.options.translateFactor + '%) rotate(' + config.slice1angle + 'deg) scale(' + config.slice1scale + ')'
		} : {
			'transform' : 'translateX(-' + this.options.translateFactor + '%) rotate(' + config.slice1angle + 'deg) scale(' + config.slice1scale + ')'
		},
		// slice2 style
		slice2Style	= config.orientation === 'horizontal' ? {
			'transform' : 'translateY(' + this.options.translateFactor + '%) rotate(' + config.slice2angle + 'deg) scale(' + config.slice2scale + ')'
		} : {
			'transform' : 'translateX(' + this.options.translateFactor + '%) rotate(' + config.slice2angle + 'deg) scale(' + config.slice2scale + ')'
		};
	
	if( this.options.optOpacity ) {
	
		slice1Style.opacity = 0;
		slice2Style.opacity = 0;
	
	}
	
	// we are adding the classes sl-trans-elems and sl-trans-back-elems to the slide that is either coming "next"
	// or going "prev" according to the direction.
	// the idea is to make it more interesting by giving some animations to the respective slide's elements
	//( dir === 'next' ) ? $nextSlide.addClass( 'sl-trans-elems' ) : $currentSlide.addClass( 'sl-trans-back-elems' );
	
	$currentSlide.removeClass( 'sl-trans-elems' );

	var transitionProp = {
		'transition' : 'all ' + this.options.speed + 'ms ease-in-out'
	};

	// add the 2 slices and animate them
	$movingSlide.css( 'z-index', this.slidesCount )
				.find( 'div.sl-content-wrapper' )
				.wrap( $( '<div class="sl-content-slice" />' ).css( transitionProp ) )
				.parent()
				.cond(
					dir === 'prev', 
					function() {
					
						var slice = this;
						this.css( slice1Style );
						setTimeout( function() {
							
							slice.css( resetStyle );

						}, 50 );
								 
					}, 
					function() {
						
						var slice = this;
						setTimeout( function() {
							
							slice.css( slice1Style );

						}, 50 );
				
					}
				)
				.clone()
				.appendTo( $movingSlide )
				.cond(
					dir === 'prev', 
					function() {
						
						var slice = this;
						this.css( slice2Style );
						setTimeout( function() {

							$currentSlide.addClass( 'sl-trans-back-elems' );

							if( self.support ) {

								slice.css( resetStyle ).on( self.transEndEventName, function() {

									self._onEndNavigate( slice, $currentSlide, dir );

								} );

							}
							else {

								self._onEndNavigate( slice, $currentSlide, dir );

							}

						}, 50 );
				
					},
					function() {
						
						var slice = this;
						setTimeout( function() {

							$nextSlide.addClass( 'sl-trans-elems' );
							
							if( self.support ) {

								slice.css( slice2Style ).on( self.transEndEventName, function() {

									self._onEndNavigate( slice, $currentSlide, dir );

								} );

							}
							else {

								self._onEndNavigate( slice, $currentSlide, dir );

							}

						}, 50 );
						
					}
				)
				.find( 'div.sl-content-wrapper' )
				.css( cssStyle );
	
	$nextSlide.show();
	
}

So, the trick is to duplicate the slide’s content into the divisions with the class sl-content-slice and to set the second one’s margin-left or margin-top to half the container’s width or height. That will make everything look “normal” and we won’t see any separation.

Then, according to the values defined in the element’s data attributes, we’ll animate the slide’s slices.

According to the direction, we will either slice the current slide and show the next one, or we will slice the previous one (not shown), and put together its slices on top of the current one.

We are adding the classes sl-trans-elems and sl-trans-back-elems to the slide that is next (when we navigate “next”) or the current one (when we navigate “prev”). Like we have seen before in the CSS part, adding those classes will make the content elements of the respective slide animate in a specific way.

Once the transition ends, we call the _onEndNavigate function where we will unwrap the content of the current slide, thus removing the two sl-content-slice divisions:


_onEndNavigate : function( $slice, $oldSlide, dir ) {
			
	// reset previous slide's style after next slide is shown
	var $slide = $slice.parent(),
		removeClasses = 'sl-trans-elems sl-trans-back-elems';
	
	// remove second slide's slice
	$slice.remove();
	// unwrap..
	$slide.css( 'z-index', 1 )
		  .find( 'div.sl-content-wrapper' )
		  .unwrap();
	
	// hide previous current slide
	$oldSlide.hide().removeClass( removeClasses );
	$slide.removeClass( removeClasses );
	// now we can navigate again..
	this.isAnimating = false;
	this.options.onAfterChange( $slide, this.current );
	
}

Finally, the plugin offers several public methods which one can call to trigger specific actions (navigation, pause the slideshow etc..)


// public method: adds more slides to the slider
add : function( $slides, callback ) {

	this.$slides = this.$slides.add( $slides );

	var self = this;
	
	
	$slides.each( function( i ) {

		var $slide = $( this ),
			// vertical || horizontal
			orientation = $slide.data( 'orientation' );

		$slide.hide().addClass( 'sl-slide-' + orientation )
			  .children()
			  .wrapAll( '<div class="sl-content-wrapper" />' )
			  .wrapAll( '<div class="sl-content" />' )
			  .end()
			  .appendTo( self.$el.find( 'div.sl-slides-wrapper' ) );

	} );

	this._setSize();

	this.slidesCount = this.$slides.length;
	
	if ( callback ) {

		callback.call( $items );

	}

},
// public method: shows next slide
next : function() {

	this._stopSlideshow();
	this._navigate( 'next' );

},
// public method: shows previous slide
previous : function() {

	this._stopSlideshow();
	this._navigate( 'prev' );

},
// public method: goes to a specific slide
jump : function( pos ) {

	pos -= 1;

	if( pos === this.current || pos >= this.slidesCount || pos < 0 ) {

		return false;

	}

	this._stopSlideshow();
	this._navigate( pos > this.current ? 'next' : 'prev', pos );

},
// public method: starts the slideshow
// any call to next(), previous() or jump() will stop the slideshow
play : function() {

	if( !this.isPlaying ) {

		this.isPlaying = true;

		this._navigate( 'next' );
		this.options.autoplay = true;
		this._startSlideshow();

	}

},
// public method: pauses the slideshow
pause : function() {

	if( this.isPlaying ) {

		this._stopSlideshow();

	}

},
// public method: check if isAnimating is true
isActive : function() {

	return this.isAnimating;

},
// publicc methos: destroys the slicebox instance
destroy : function( callback ) {

	this._destroy( callback );

}

And that’s it! I hope you enjoyed this tutorial and find it useful!

View demo Download source

Image Accordion with CSS3

$
0
0


Image Accordion with CSS3

View demo Download source

Today we are going to create a CSS-only image accordion. We’ll use a nested structure and apply a technique of radio buttons and the sibling combinator in order to control the slides. The idea is to make each item, or slide, “clickable” by overlaying the radio button over the entire slide, and change the position of a child element when clicking on it.

The images used in the demo are from the “L’aquarelle” project by Andrey & Lili: L’aquarelle on Behance. It is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License .

Please note: the result of this tutorial will only work as intended in browsers that support the respective CSS properties.

The Markup

The main idea is to create a nested structure that will allow us to simply move the respective accordion slides to one side, i.e. moving one slide will move all its inner slides (the nested elements). Each of the figures will have the image, a radio button for controlling the “opening” of a slide, and a caption.

<div class="ia-container">
				
	<figure>
		<img src="images/1.jpg" alt="image01" />
		<input type="radio" name="radio-set" checked="checked" />
		<figcaption><span>True Colors</span></figcaption>
		
		<figure>
			<img src="images/2.jpg" alt="image02" />
			<input type="radio" name="radio-set" />
			<figcaption><span>Honest Light</span></figcaption>
			
			<figure>
				<!-- ... -->
				
				<figure>
					<!-- ... -->
					
					<figure>
						<!-- ... -->
						
						<figure>
							<!-- ... -->
				
							<figure>
								<!-- ... -->										

								<figure>
									<!-- ... -->
								</figure>
								
							</figure>
				
						</figure>	
							
					</figure>	
						
				</figure>
					
			</figure>
			
		</figure>
		
	</figure>
	
</div>

You could also use different kind of content here, what is important is the nested structure and the radio input.

The CSS

We’ll give the slider a width value and hide the excess part with overflow hidden.

.ia-container {
	width: 685px;
	margin: 20px auto;
	overflow: hidden;
	box-shadow: 1px 1px 4px rgba(0,0,0,0.08);
	border: 7px solid rgba(255,255,255,0.6);
}

The width is calculated as follows:

( (Number of images - 1) * 50px ) + 335px

where 50 is the width of the visible piece of an image and 335 is the width of the image. In our case, that gives us ( (8 – 1) * 50px ) + 335px = 350px + 335px = 685px.

Each of the figures will have a left value of 50 pixel (visible piece). That should be their position relatively to their parent. This makes the slider look like an accordion. The width of a figure is 335 pixel, just like the image inside. The figure will also have a transition for a smooth movement:

.ia-container figure {
    position: absolute;
	top: 0;
	left: 50px; /* width of visible piece */
	width: 335px;
    box-shadow: 0 0 0 1px rgba(255,255,255,0.6);
    transition: all 0.3s ease-in-out;
}

The first figure will be positioned completely to the left and we set the !important because we will use media queries later and we always want the left to be 0 for this figure. The position will be set to “relative” in order to give the slider a height:

.ia-container > figure {
    position: relative;
	left: 0 !important;
}

The image will have a width of 100% so that it fills the figure:

.ia-container img {
	display: block;
	width: 100%;
}

Each of the radio buttons will cover the visible part of a slide. We set the opacity to 0 so that we can’t see it but still be able to click it. The z-index should be high, so that it covers everything else:

.ia-container input {
	position: absolute;
	top: 0;
	left: 0;
	width: 50px; /* just cover visible part */
	height: 100%;
	cursor: pointer;
	border: 0;
	padding: 0;
    opacity: 0;
	z-index: 100;
	-webkit-appearance: none;
	-moz-appearance: none;
	appearance: none;
}

When an input is selected or clicked, we want the input to disappear. Actually, we could say something like width: 0px but because of some kind of strange behavior in the Chrome browser we still need it at the right side (the next item won’t trigger the hover if we haven’t hovered over the current input). We also want the sibling figures to move to the left with a transition:

.ia-container input:checked{
	width: 5px;
	left: auto;
	right: 0px;
}
.ia-container input:checked ~ figure {
    left: 335px;
    transition: all 0.7s ease-in-out;
}

Giving two different transition durations (one in the “default” state, one when we check an input) will give us the “shuffle” effect. Playing with these values will give you different effects.

The caption of the figure will cover all the element with a transparent dark overlay and the span will be positioned over the upper half of the figure initially:

.ia-container figcaption {
	width: 100%;
	height: 100%;
	background: rgba(87, 73, 81, 0.1);
	position: absolute;
	top: 0px;
    transition: all 0.2s linear;
}

.ia-container figcaption span {
	position: absolute;
	top: 40%;
	margin-top: -30px;
	right: 20px;
	left: 20px;
	overflow: hidden;
	text-align: center;
	background: rgba(87, 73, 81, 0.3);
	line-height: 20px;
	font-size: 18px;
    opacity: 0;
	text-transform: uppercase;
	letter-spacing: 4px;
	font-weight: 700;
	padding: 20px;
	color: #fff;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.1);
} 

When a slide gets selected, we will remove the overlay by setting the opacity value of the RGBA to 0:

.ia-container input:checked + figcaption,
.ia-container input:checked:hover + figcaption{
	background: rgba(87, 73, 81, 0);
}

The span will fade in and move from the top with a delay (the slide should open first):

.ia-container input:checked + figcaption span {
    transition: all 0.4s ease-in-out 0.5s;
	opacity: 1;
	top: 50%;
}

Since the last slide does not have any other slides coming from the right when we select it, the delay does not need to be as high (we’ve given the last radio input an id ia-selector-last):

.ia-container #ia-selector-last:checked + figcaption span {
	transition-delay: 0.3s;
}

When we hover over the visible piece of a closed slide, we want a slight hover effect. But because the radio input is covering this part (it’s on top of all the other elements) we will use the adjacent sibling combinator (this requires that the input is directly followed by the figcaption element):

.ia-container input:hover + figcaption {
	background: rgba(87, 73, 81, 0.03);
}

All the following siblings of a checked input should get a low z-index, again to prevent some unwanted behavior in Chrome:

.ia-container input:checked ~ figure input{
    z-index: 1;
}

And finally, we’ll add some media queries to make the slider responsive:

@media screen and (max-width: 720px) {
    .ia-container { 
		width: 540px; 
	}
	
	.ia-container figure { 
		left: 40px; 
		width: 260px; 
	}
	
	.ia-container input { 
		width: 40px; 
	}
	
	.ia-container input:checked ~ figure { 
		left: 260px; 
	}
	
	.ia-container figcaption span { 
		font-size: 16px; 
	}
}

@media screen and (max-width: 520px) {
    .ia-container { 
		width: 320px; 
	}
	
	.ia-container figure { 
		left: 20px; 
		width: 180px; 
	}
	
	.ia-container input { 
		width: 20px; 
	}
	
	.ia-container input:checked ~ figure { 
		left: 180px; 
	}
	
	.ia-container figcaption span { 
		font-size: 12px; 
		letter-spacing: 2px; 
		padding: 10px; 
		margin-top: -20px; 
	} 

}

That’s all, hope you like it!

View demo Download source

Fullscreen Video Slideshow with BigVideo.js

$
0
0

Fullscreen Video Slideshow with BigVideo.js

View demo Download source
(Please note that the ZIP file does not contain any video files due to their size.)

BigVideo.js is a jQuery plugin that makes it easy to create fit-to-fill background video on a web page. It can play silent ambient background video (or series of videos), or used as a player to show video playlist.

In this tutorial, we’ll be creating a page that shows a series of video screens, showcasing the free HD stock video footage and animated backgrounds available on Beachfront B-Roll.

Before we get started, give some thought as to whether using this technique at all is appropriate for your project. Background video is bandwidth heavy and can be a big drag on the user’s browser performance. If your site is already video-heavy or incorporating big video is essential to the design and purpose of your site, then using this technique may be a great choice. However, if you can accomplish the same goal with using cinemagraphs for example, maybe that is a better choice. This tutorial is can be helpful to you in either case, as we cover using big background images as well.

The first thing you’ll need to do is get video content. In this case, we went to Beachfront B-Roll and downloaded a few of their free videos. Some of them were quite large in file size, so I used Quicktime Pro to trim them to about 10 seconds or so in length. For each of the videos, we will also need to create a poster image image of the first frame of the video. I used Photoshop to save a 960×540 jpeg for each image. I used a trick of applying a .5 pixel blur and a quality setting of 50 to get a smaller file size (afterall, these are background images, so having a little blurriness on them is not a bad thing).

The Markup

First, let’s do the markup for the page:

We have a header on the center of the screen. Then we have a wrapper div that contains all the different video screens. Then, there is a button we will use to navigate between the screens.

<header>
    <h1>Fullscreen Video Slideshow <span>with BigVideo.js</span></h1>
    <p>The videos in this demo are from <a href="http://beachfrontprod.blogspot.com" target="_blank">Beachfront B-Roll</a>, a great place to download unique HD stock video footage and animated backgrounds for any production purpose (for free!).</p>
    <p><small>A demo for</small> <a href="http://dfcb.github.com/BigVideo.js/" target="_blank">BigVideo.js</a> <small>by <a href="http://twitter.com/johnpolacek" target="_blank" rel="author">@johnpolacek</a></small></p>
</header>

<div class="wrapper">
    <div class="screen" id="screen-1" data-video="vid/bird.mp4">
        <img src="img/bird.jpg" class="big-image" />
        <h1 class="video-title">#1 Bird</h1>
    </div>
    <div class="screen" id="screen-2" data-video="vid/satellite.mp4">
        <img src="img/satellite.jpg" class="big-image" />
        <h1 class="video-title">#2 Satellite</h1>
    </div>
    <div class="screen" id="screen-3" data-video="vid/camera.mp4">
        <img src="img/camera.jpg" class="big-image" />
        <h1 class="video-title">#3 Camera</h1>
    </div>
    <div class="screen" id="screen-4" data-video="vid/spider.mp4">
        <img src="img/spider.jpg" class="big-image" />
        <h1 class="video-title">#4 Spider</h1>
    </div>
    <div class="screen" id="screen-5" data-video="vid/dandelion.mp4">
        <img src="img/dandelion.jpg" class="big-image" />
        <h1 class="video-title">#5 Dandelion</h1>
    </div>
</div>

<nav id="next-btn">
    <a href="#" class="next-icon"></a>
</nav>

The CSS

Before we get into the video, we need to style the page.

Since navigation will be handled by a single big next button, we set the body overflow to hidden. While we’re at it, we’ll make all text on the page white.

html, body {
    margin: 0;
    padding: 0;
    color: #fff;
    overflow: hidden;
    font-family: 'Open Sans Condensed', Arial, sans-serif;
    font-weight: 300;
    font-size: 1em;
}

Next, for the wrapper, we know we have five content screens, so we set the width to 500% (100% per screen) and the height to 100%. We are going to navigate the page by animating the wrapper’s horizontal position, so it will use absolute positioning. We’ll use a z-index of zero to keep the wrapper content in the background.

Note the data-video attribute. This is where our script will grab the url for the video for each screen.

.wrapper {
    position: absolute;
    width: 500%;
    height: 100%;
    z-index: 0;
}

We’ll position the header in the center of the page and make. Also, give it a high z-index so it stays on top of our background content.

header {
    position: absolute;
    top: 50%;
    left: 50%;
    z-index: 999;
    color: #fff;
    background: rgba(0,0,0,0.5);
    padding: 60px;
    width: 400px;
    height: 400px;
    border-radius: 50%;
    margin: -200px 0 0 -200px;
    text-align: center;
}

We want each screen to fill the height and width of the screen, so we set the height to 100%, and the width to 100% divided by the number of screens, in this case this results in 20%. Let’s set them to float left and make the positioning relative so we can absolutely position content inside the screens. We center the position of the image inside the screen div, so we keep the overflow hidden.

.screen {
    position: relative;
    height: 100%; 
    width: 20%; /*  NOTE:numVideos/100%  */
    float: left;
    overflow: hidden;
}

Inside the screen divs, we have a large image of the first frame of the video and a video title. For the image, we use min-width, min-height to make sure the size of the image is never smaller than the size of the .screen container div (which is always the size of the browser window) and set height and width to auto so the image’s aspect ratio is maintained.

.big-image {
    min-width: 100%;
    min-height: 100%; 
    height: auto;
    width: auto; 
}

Let’s put the video titles in the bottom left, make the font size really big and have them be 50% transparent.

.video-title {
    position: absolute;
    bottom: 5%;
    left: 5%;
    opacity: .5;
    margin: 0;
    padding: 0;
    line-height: .65;
    font-size: 4em;
    text-transform: uppercase;
}

Next up, the arrow button. I’ve made a pure CSS arrow button using a technique of putting a box with a border top and left, then rotating that box 45 degrees and putting it inside a div with a large border-radius turning it into a circle. (Note: We will make the rotation work by applying an IE filter for rotating 45 degrees from Boog Design’s excellent Matrix Calculator. See the head of the HTML for the conditional comment.)

nav {
    position:absolute; 
    right: 5%; 
    top: 45%; 
    padding: 20px; 
    background: #000; 
    border-radius: 40px; 
    opacity: .4; 
    cursor: pointer;}
    nav: hover {
    opacity: .6;
}

.next-icon {
    display: block;
    border-top: solid 2px #fff; 
    border-right: solid 2px #fff; 
    width: 20px; 
    height: 20px;
    position: relative;
    left: -5px;
    -webkit-transform: rotate(45deg);
    -moz-transform: rotate(45deg);
    -ms-transform: rotate(45deg);
    -o-transform: rotate(45deg);
    transform: rotate(45deg);
    color: #fff;
    text-decoration: none;
}

The JavaScript

Ok, here’s where we make stuff happen. First, link to all the scripts at the bottom of the page, before the closing body tag. Download the BigVideo.js Github Repo. In addition to BigVideo.js and its dependencies, we will be using jQuery Transit for animations.

<!-- BigVideo Dependencies -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js?84cd58"></script>
<script>window.jQuery || document.write('<script src="js/jquery-1.7.2.min.js"><\/script>')</script>
<script src="js/jquery-ui-1.8.22.custom.min.js"></script>
<script src="js/jquery.imagesloaded.min.js"></script>
<script src="http://vjs.zencdn.net/c/video.js"></script>

<!-- BigVideo -->
<script src="js/bigvideo.js"></script>

<!-- Tutorial Demo -->
<script src="js/jquery.transit.min.js"></script>

Before we get to the video, let’s set up navigation. We will need some vars to help manage navigation.

$(function() {
    var screenIndex = 1,
    numScreens = $('.screen').length,
    isTransitioning = false,
    transitionDur = 1000;
}

Let’s go over what these vars do.

  • screenIndex – keeps track of what screen we are looking at
  • numScreens – the number of screens (in our case 5)
  • isTransition – boolean to prevent extra clicking interfering with the navigation
  • transitionDur – how fast the animation scrolls

Now, add a next button click event and callback to do navigation.

$('#next-btn').on('click', function(e) {
    e.preventDefault();
    if (!isTransitioning) {
        next();
    }
});

function next() {
    isTransitioning = true;
    
    // update video index
    if (screenIndex === numScreens) {
        screenIndex = 1;
    } else {
        screenIndex++;
    }
    
    $('.wrapper').transit(
        {'left':'-'+(100*(screenIndex-1))+'%'},
        transitionDur,
        onTransitionComplete);
}

function onTransitionComplete() {
    isTransitioning = false;
}

Now, that our navigation is working, time to set up the video. When creating a video heavy site, it is important to consider mobile devices. Typically they do not support autoplay, meaning ambient background video will not work. There are tricks to get around this, but we should also consider that it is likely that the user is on a lower bandwidth connection. One thing to consider would be to provide non-video content to these users. In this demo, we will demonstrate how to do that using Modernizr, which you should link to in the document head.

First, add a var for BigVideo (BV) and an isTouch boolean to the top of our script, which gets set by Modernizr’s touch feature detection.

var screenIndex = 1,
    numScreens = $('.screen').length,
    isTransitioning = false,
    transitionDur = 1000,
    BV,
    isTouch = Modernizr.touch;
   

Next, initialize BigVideo for non-touch devices. Create a showVideo() function where we will grab the video filepath from the data-video attribute for the current screen being viewed. Add a showVideo() call to the onTransitionComplete callback function. Also, when the video is loaded and ready to play, we need to fade the framegrab image out. To do that, we use the Video.js api to add a ‘loadeddata’ event listener to the BigVideo player. We attach a callback function to that event which makes the image of the current screen fade out so the video is displayed.

var screenIndex = 1,
    numScreens = $('.screen').length,
    isTransitioning = false,
    transitionDur = 1000,
    BV,
    isTouch = Modernizr.touch;

// Next button click goes to next div
$('#next-btn').on('click', function(e) {
    e.preventDefault();
    if (!isTransitioning) {
        next();
    }
});

if (!isTouch) {
    // initialize BigVideo
    BV = new $.BigVideo({forceAutoplay:isTouch});
    BV.init();
    showVideo();
    
    BV.getPlayer().addEvent('loadeddata', function() {
        onVideoLoaded();
    });

    // adjust image positioning so it lines up with video
    $bigImage
        .css('position','relative')
        .imagesLoaded(adjustImagePositioning);
    // and on window resize
    $(window).on('resize', adjustImagePositioning);
}

// Next button click goes to next div
$('#next-btn').on('click', function(e) {
    e.preventDefault();
    if (!isTransitioning) {
        next();
    }
});

function showVideo() {
    BV.show($('#screen-'+screenIndex).attr('data-video'),{ambient:true});
}

function next() {
    isTransitioning = true;
    
    // update video index, reset image opacity if starting over
    if (screenIndex === numScreens) {
        $bigImage.css('opacity',1);
        screenIndex = 1;
    } else {
        screenIndex++;
    }
    
    if (!isTouch) {
        $('#big-video-wrap').transit({'left':'-100%'},transitionDur)
    }
        
    $('.wrapper').transit(
        {'left':'-'+(100*(screenIndex-1))+'%'},
        transitionDur,
        onTransitionComplete);
}

function onVideoLoaded() {
    $('#screen-'+screenIndex).find('.big-image').transit({'opacity':0},200)
}

function onTransitionComplete() {
    isTransitioning = false;
    if (!isTouch) {
        $('#big-video-wrap')
            .css('left',0);
        showVideo();
    }
}

Tidying up

You may notice that the big image and the big video don’t line up during the fade animation, resulting in a bothersome jump in position.This is because the video is centered inside the screen. To fix the problem, we need also need to center our images. To do that, we create a function that adjusts the positioning of the images.

function adjustImagePositioning() {
    $bigImage.each(function(){
        var $img = $(this),
            img = new Image();

        img.src = $img.attr('src');

        var windowWidth = $window.width(),
            windowHeight = $window.height(),
            r_w = windowHeight / windowWidth,
            i_w = img.width,
            i_h = img.height,
            r_i = i_h / i_w,
            new_w, new_h, new_left, new_top;

        if( r_w > r_i ) {
            new_h   = windowHeight;
            new_w   = windowHeight / r_i;
        }
        else {
            new_h   = windowWidth * r_i;
            new_w   = windowWidth;
        }

        $img.css({
            width   : new_w,
            height  : new_h,
            left    : ( windowWidth - new_w ) / 2,
            top     : ( windowHeight - new_h ) / 2
        })

    });

}

Call the function on document ready and attach it to a window resize event.

$bigImage
    .css('position','relative')
    .imagesLoaded(adjustImagePositioning);
// and on window resize
$(window).on('resize', adjustImagePositioning);

Now the images and videos should be aligned, resulting in a clean transition between the poster image and the video.

Please note that the ZIP file does not contain any video files due to their size.

View demo Download source

Slicebox Revised

$
0
0

Slicebox Revised
View demo Download source

Welcome to the News & Updates section on Codrops where we will be announcing updates of older scripts and tutorials with improvements and suggestions you have sent to us or left in the comments. We will also add these updated scripts to GitHub.

Slicebox – A fresh 3D image slider with graceful fallback has been updated! There are some new features and improvements that you have asked for and the project is now added to GitHub. Check out the original article to find out how to use it and view the demo.

Summary of Changes

A lot of changes were integrated into the new Slicebox, here are the most important ones:

  • Slicebox is now responsive.
  • The images can now be wrapped into an anchor. You can also leave the image without an anchor around or have a mix of both cases (see the original original article for more details on the structure).
  • The description is not taken from the title attribute of the image any more. There is a separate container for that and you can add any markup.
  • The description is now always shown.
  • It’s now possible to jump to any slide with a new method.
  • You can randomize the orientation and the number of slices.
  • Support for Firefox added

Slit Slider Revised

$
0
0

Slit Slider Revised

View demo Download source

We have updated the Slit Slider tutorial, fixed some issues and made the slider responsive. Check out the original tutorial to find out more about it and how to use it.

Demos

We’ve improved the original demo and because of some requests we’ve added another one which shows how the slider can be used in full width, fixed height and background images. Both of them are responsive:

SlitSlider_demo2

Summary of Changes

A lot of changes were integrated into the new Slit Slider, here are the most important ones:

  • The slider can now be used in a responsive context, simply set the wrapper’s width to a percentage and it will adapt.
  • The Transit plugin was removed.
  • We’ve added public methods for navigation control: next, previous and specific slide. This will make it possible to use custom controls.
  • Keyboard navigation has been added (right and left arrow keys).
  • The Firefox bug has been fixed.
  • We’ve added callback functions for before and after each slide transition.
  • New method for dynamically adding new slides.
  • Some other bug fixes, like flickering etc.

Gamma Gallery: A Responsive Image Gallery Experiment

$
0
0

Gamma Gallery: A Responsive Image Gallery Experiment

View demo Download source

Creating a truly responsive image gallery can be a very tricky and difficult thing. There are so many factors to consider like the layout and the features, and so many choices to make when it comes to delivering a good viewing experience for every device. Gamma Gallery is an attempt to create an image gallery that uses a similar responsive images approach to the proposed picture element. Focused on providing suitable image sizes for both, the fluid grid thumbnails and the full image view, the selection of images also depends on container dimensions and not solely on the window size. In some cases it might even be reasonable to load larger thumbnails for a smaller device if we, for example want to show less columns in a grid. So, smaller windows don’t necessarily mean smaller thumbnails.

Gamma Gallery uses David David DeSandro’s Masonry in a fluid mode where column numbers are defined depending on the grid container sizes.

Inspired by Picturefill from Scott Jehl, the responsive images approach that mimics the picture element, we’ve come up with the following HTML structure for defining different image sizes:

<div class="gamma-container gamma-loading" id="gamma-container">

	<ul class="gamma-gallery">
		<li>
			<div data-alt="img01" data-description="<h3>Assemblage</h3>" data-max-width="1800" data-max-height="2400">
				<div data-src="images/xxxlarge/1.jpg" data-min-width="1300"></div>
				<div data-src="images/xxlarge/1.jpg" data-min-width="1000"></div>
				<div data-src="images/xlarge/1.jpg" data-min-width="700"></div>
				<div data-src="images/large/1.jpg" data-min-width="300"></div>
				<div data-src="images/medium/1.jpg" data-min-width="200"></div>
				<div data-src="images/small/1.jpg" data-min-width="140"></div>
				<div data-src="images/xsmall/1.jpg"></div>
				<noscript>
					<img src="images/xsmall/1.jpg" alt="img01"/>
				</noscript>
			</div>
		</li>
		<li> <!-- ... --> </li>
		<!-- ... -->
	</ul>

	<div class="gamma-overlay"></div>

</div>

Depending in which viewing mode we are, thumbnail grid view or full image view, the choice of the fitting image will depend on either the list item’s size or the whole viewport size. Let’s call that the “container size”. An “xsmall” image will be chosen if the container size is less than 140 pixels. A “small” image is picked if the container is wider than 140 pixels (data-min-width=”140″). A “medium” image is chosen if the container is wider than 200 pixels and so on.

Because we have a “fluid” image approach where we define the max-width of the image to be 100%, we will fit the image in its container if the image is bigger than the container. For that, our images will have the sizes that are defined in their consecutive (upper) step. For example, the “large” sized image will have a width of 700 pixels. This will ensure that the image shown is always equal or larger than its container, never smaller. In order to maintain the layout of the grid and never show a pixelated image (i.e. by stretching it), this approach seemed the most reasonable one.

The data-max-width and data-max-height attributes defines the largest image dimension which will restrict it to that size and not stretch it fully in the slideshow view. The slideshow view will take all the window size into account and allow the image to fill up all the possible space. The attributes ensure that the image doesn’t get resized beyond its largest dimension and become pixelated on huge screens.

Some things we are using in Gamma:

There are many things to improve here, it is a very experimental implementation that tries to tackle the responsiveness of a special scenario, an image gallery.

In the demo we don’t really take care of the vertical images which should of course have a set of reasonable dimensions that take the height of common screen sizes into account.

Here are the settings in the Gamma gallery script:

// default value for masonry column count
columns : 4,
// transition properties for the images in ms (transition to/from singleview)
speed : 300,
easing : 'ease',
// if set to true the overlay's opacity will animate (transition to/from singleview)
overlayAnimated : true,
// if true, the navigate next function is called when the image (singleview) is clicked
nextOnClickImage : true,
// circular navigation
circular : true,
// transition settings for the image in the single view.
// These include:
// - adjusting its position and size when the window is resized
// - fading out the image when navigating
svImageTransitionSpeedFade : 300,
svImageTransitionEasingFade : 'ease-in-out',
svImageTransitionSpeedResize : 300,
svImageTransitionEasingResize : 'ease-in-out',
svMarginsVH : {
	vertical : 140,
	horizontal : 120
},
// allow keybord and swipe navigation
keyboard : true,
swipe : true,
// slideshow interval (ms)
interval : 4000,
// if History API is not supported this value will turn false
historyapi : true

And here is an example of how we can initialize the gallery:

<script type="text/javascript">
	
	$(function() {

		var GammaSettings = {
				// order is important!
				viewport : [ {
					width : 1200,
					columns : 5
				}, {
					width : 900,
					columns : 4
				}, {
					width : 500,
					columns : 3
				}, { 
					width : 320,
					columns : 2
				}, { 
					width : 0,
					columns : 2
				} ]
		};

		Gamma.init( GammaSettings );

	});

</script>

The viewport width is the width that we will take into account for choosing the right sized image but also for defining how many columns in our grid layout we want to show. For example, between 320 and 500 pixel we would like to show 2 columns.

The images used in the demo are by Idleformat.

The thumbnail hover style is inspired by the one seen on Nudge.

Feedback, suggestions and comment are very welcome, there is lot’s things to improve and make better. Thank you!

View demo Download source

Flipping Circle Slideshow

$
0
0

Flipping Circle Slideshow

View demo Download source

Today we want to share a simple and fun circular slideshow with you. It’s an experimental concept and the idea is to flip a circle in a specific angle depending on which spot of the circle we click. There are three different possibilities for each side: top, middle and bottom. For example, when clicking on the top right part of the image, the circle will flip in the associated angle, making it look as if we press down that part and reveal the next image that is on the back face of the circle.

The demo features illustrations by Isaac Montemayor. See his works on Dribbble or on his website.

This is how the structure looks like:

<div id="fc-slideshow" class="fc-slideshow">
	<ul class="fc-slides">
		<li><img src="images/1.jpg" /><h3>Hot</h3></li>
		<li><img src="images/2.jpg" /><h3>Cold</h3></li>
		<li><img src="images/3.jpg" /><h3>Light</h3></li>
		<li><img src="images/4.jpg" /><h3>Dark</h3></li>
		<li><img src="images/5.jpg" /><h3>Soft</h3></li>
		<li><img src="images/6.jpg" /><h3>Hard</h3></li>
		<li><img src="images/7.jpg" /><h3>Smooth</h3></li>
		<li><img src="images/8.jpg" /><h3>Rough</h3></li>
	</ul>
</div>

And we transform it into the following:

<div id="fc-slideshow" class="fc-slideshow">

	<ul class="fc-slides">
		<!-- ... -->
	</ul>

	<nav>
		<div class="fc-left">
			<span></span>
			<span></span>
			<span></span>
			<i class="icon-arrow-left"></i>
		</div>
		<div class="fc-right">
			<span></span>
			<span></span>
			<span></span>
			<i class="icon-arrow-right"></i>
		</div>
	</nav>

	<div class="fc-flip">
		<div class="fc-front">
			<div><img src="images/4.jpg"><h3>Dark</h3></div>
			<div class="fc-overlay-light"></div>
		</div>
		<div class="fc-back">
			<div><img src="images/5.jpg"><h3>Soft</h3></div>
			<div class="fc-overlay-dark"></div>
		</div>
	</div>
	
</div>

The nav element has some empty spans that serve as “detection areas”. Each side of the circle has three areas that will be clickable, one at the top, one in the middle and one at the bottom. The i element will serve as a navigation arrow and depending on which span we are hovering, we will rotate the little arrow box to the right place. But we won’t use the arrow as the clickable area but the whole span.

The division for the circle flip contains a special 3D structure: it has a front and a back side. Once we navigate to the next or previous item, we will show that structure and rotate the flip container so that we see the back side.

The overlays make everything look a bit more realistic by providing light or darkness. We animate the opacity depending on the angle of rotation.

We simply call the plugin like this:

$( '#fc-slideshow' ).flipshow();

And this are the options for the plugin:

// the options
$.Flipshow.defaults = {
	// default transition speed (ms)
	speed : 700,
	// default transition easing
	easing : 'cubic-bezier(.29,1.44,.86,1.06)'
};

Please note, that this is very experimental and will only work as intended in browsers that support CSS 3d transforms. For others there is a simple fallback that simply shows and hides the previous or next item.

We hope you find this little plugin inspiring!

View demo Download source

Background Slideshow

$
0
0

Background Slideshow

View demo Download source

A simple fullscreen background slideshow with auto-play and controls to navigate and pause.

The HTML

<ul id="cbp-bislideshow" class="cbp-bislideshow">
	<li><img src="images/1.jpg" alt="image01"/></li>
	<li><img src="images/2.jpg" alt="image02"/></li>
	<li><img src="images/3.jpg" alt="image03"/></li>
	<li><img src="images/4.jpg" alt="image04"/></li>
	<li><img src="images/5.jpg" alt="image05"/></li>
	<li><img src="images/6.jpg" alt="image06"/></li>
</ul>
<div id="cbp-bicontrols" class="cbp-bicontrols">
	<span class="cbp-biprev"></span>
	<span class="cbp-bipause"></span>
	<span class="cbp-binext"></span>
</div>

The CSS

@font-face {
	font-family: 'entypo';
	src:url('../fonts/controls/entypo.eot');
	src:url('../fonts/controls/entypo.eot?#iefix') format('embedded-opentype'),
		url('../fonts/controls/entypo.woff') format('woff'),
		url('../fonts/controls/entypo.ttf') format('truetype'),
		url('../fonts/controls/entypo.svg#entypo') format('svg');
	font-weight: normal;
	font-style: normal;
}

.cbp-bislideshow {
	list-style: none;
	width: 100%;
	height: 100%;
	position: fixed;
	top: 0;
	left: 0;
	z-index: -1;
	padding: 0;
	margin: 0;
}

.cbp-bislideshow li {
	position: absolute;
	width: 101%;
	height: 101%;
	top: -0.5%;
	left: -0.5%;
	opacity: 0;
	-webkit-transition: opacity 1s;
	-moz-transition: opacity 1s;
	transition: opacity 1s;
}

/* If background-size supported we'll add the images to the background of the li */

.backgroundsize .cbp-bislideshow li {
	-webkit-background-size: cover;
	-moz-background-size: cover;
	background-size: cover;
	background-position: center center;
}

/* ...and hide the images */
.backgroundsize .cbp-bislideshow li img {
	display: none;
}

.cbp-bislideshow li img {
	display: block;
	width: 100%;
}

.cbp-bicontrols {
	position: fixed;
	width: 300px;
	height: 100px;
	margin: -50px 0 0 -150px;
	top: 50%;
	left: 50%;
}

.cbp-bicontrols span {
	float: left;
	width: 100px;
	height: 100px;
	position: relative;
	cursor: pointer;
}

.cbp-bicontrols span:before {
	position: absolute;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
	text-align: center;
	font-family: 'entypo';
	speak: none;
	font-style: normal;
	font-weight: normal;
	font-variant: normal;
	text-transform: none;
	line-height: 100px;
	font-size: 80px;
	color: #fff;
	-webkit-font-smoothing: antialiased;
	opacity: 0.7;
}

.cbp-bicontrols span:hover:before {
	opacity: 1;
}

.cbp-bicontrols span:active:before {
	top: 2px;
}

span.cbp-biplay:before {
	content: "\e002";
}

span.cbp-bipause:before {
	content: "\e003";
}

span.cbp-binext:before {
	content: "\e000";
}

span.cbp-biprev:before {
	content: "\e001";
}

.cbp-bicontrols span.cbp-binext {
	float: right;
}

/* Fallback */

.no-js.no-backgroundsize .cbp-bislideshow li:first-child {
	opacity: 1;
}

.no-js.backgroundsize .cbp-bislideshow li:first-child img {
	display: block;
}

The JavaScript

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- imagesLoaded jQuery plugin by @desandro : https://github.com/desandro/imagesloaded -->
<script src="js/jquery.imagesloaded.min.js"></script>
<script src="js/cbpBGSlideshow.min.js"></script>
<script>
	$(function() {
		cbpBGSlideshow.init();
	});
</script>
/**
 * cbpBGSlideshow.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
 */
var cbpBGSlideshow = (function() {

	var $slideshow = $( '#cbp-bislideshow' ),
		$items = $slideshow.children( 'li' ),
		itemsCount = $items.length,
		$controls = $( '#cbp-bicontrols' ),
		navigation = {
			$navPrev : $controls.find( 'span.cbp-biprev' ),
			$navNext : $controls.find( 'span.cbp-binext' ),
			$navPlayPause : $controls.find( 'span.cbp-bipause' )
		},
		// current item´s index
		current = 0,
		// timeout
		slideshowtime,
		// true if the slideshow is active
		isSlideshowActive = true,
		// it takes 3.5 seconds to change the background image
		interval = 3500;

	function init( config ) {

		// preload the images
		$slideshow.imagesLoaded( function() {
			
			if( Modernizr.backgroundsize ) {
				$items.each( function() {
					var $item = $( this );
					$item.css( 'background-image', 'url(' + $item.find( 'img' ).attr( 'src' ) + ')' );
				} );
			}
			else {
				$slideshow.find( 'img' ).show();
				// for older browsers add fallback here (image size and centering)
			}
			// show first item
			$items.eq( current ).css( 'opacity', 1 );
			// initialize/bind the events
			initEvents();
			// start the slideshow
			startSlideshow();

		} );
		
	}

	function initEvents() {

		navigation.$navPlayPause.on( 'click', function() {

			var $control = $( this );
			if( $control.hasClass( 'cbp-biplay' ) ) {
				$control.removeClass( 'cbp-biplay' ).addClass( 'cbp-bipause' );
				startSlideshow();
			}
			else {
				$control.removeClass( 'cbp-bipause' ).addClass( 'cbp-biplay' );
				stopSlideshow();
			}

		} );

		navigation.$navPrev.on( 'click', function() { 
			navigate( 'prev' ); 
			if( isSlideshowActive ) { 
				startSlideshow(); 
			} 
		} );
		navigation.$navNext.on( 'click', function() { 
			navigate( 'next' ); 
			if( isSlideshowActive ) { 
				startSlideshow(); 
			}
		} );

	}

	function navigate( direction ) {

		// current item
		var $oldItem = $items.eq( current );
		
		if( direction === 'next' ) {
			current = current > itemsCount - 1 ? ++current : 0;
		}
		else if( direction === 'prev' ) {
			current = current < 0 ? --current : itemsCount - 1;
		}

		// new item
		var $newItem = $items.eq( current );
		// show / hide items
		$oldItem.css( 'opacity', 0 );
		$newItem.css( 'opacity', 1 );

	}

	function startSlideshow() {

		isSlideshowActive = true;
		clearTimeout( slideshowtime );
		slideshowtime = setTimeout( function() {
			navigate( 'next' );
			startSlideshow();
		}, interval );

	}

	function stopSlideshow() {
		isSlideshowActive = false;
		clearTimeout( slideshowtime );
	}

	return { init : init };

})();

View demo Download source

Morphing Devices

$
0
0

MorphingDevices

View demo Download source

Today we want to share an experimental “morphing” slideshow with you. The idea is to transition between different devices that show a screenshot of a responsive website or app by applying a “device class”. By using the same elements and pseudo-elements for all the devices, we can create an interesting morph effect. We will control the classes and the switching of the image with some JavaScript. We’ve also added an option for autoplaying the slideshow and for rotating some of the devices.

Please note that this is very experimental and only works as intended in browsers that support the respective CSS properties.

MorphingDevices_iMac

There are four devices which we will use to display different screenshots of responsive designs. Two of them, the tablet and the smartphone, can also be rotated. What we do is to interchange the classes depending on the device and since the device has transitions defined, we’ll see a morphing effect. Note that we also animate pseudo-elements here which might not be supported in all browsers (especially mobile browsers).

MorphingDevices_MacBook

The rotation on the last two devices happens by adding another class which will rotate the device 90 degrees.

MorphingDevices_iPad

The images are changed by adding the new picture and fading out the previous one. This also creates a morphing effect where the image gets stretched or squeezed to accomodate the new size.

MorphingDevices_iPhone

The better technology to choose for this kind of effect is undoubtedly SVG, but for practicality reasons we started this experiment with CSS and JS.

We hope you like and enjoy it and get inspired!

View demo Download source

Interactive Particles Slideshow

$
0
0

InteractiveParticlesSlideshow

View demo Download source

This is an experimental interactive slideshow component with math shapes and texts using Canvas. The slideshow consists of pairs of shape and text, both made out of particles that the user can interact with and that transition into the next pair when navigating. With Canvas we can dynamically draw 2D stuff using JavaScript and this experiment shows some of the possibilities.

Please note: this only works as intended in browsers that support the respective CSS properties. Modern browsers only!

The particles for the shapes have a predefined set of colors that the particles will be painted with. The text will be built up of white particles:

InteractiveParticlesSlideshow01

When navigating to the next (or previous) slide, the text and the shape will morph into the new one, creating an interesting effect:
InteractiveParticlesSlideshow02

After the transformation, we can see the shape and the text of the next slide:
InteractiveParticlesSlideshow03

The particles of the text and the shape are interactive in a way that we can push them around a bit:
InteractiveParticlesSlideshow04

Hope you liked it, and that it gave you some inspiration!

View demo Download source

Tilted Content Slideshow

$
0
0

TiltedContentSlideshow

View demo Download source

The FWA landing page has a really nice content slider that plays with 3D perspective on screenshots and animates them in an interesting way. Today we’d like to recreate part of that effect and make a simple content slideshow with some fancy 3D animations. The slideshow won’t be the same as the one on the FWA page: the items won’t be “floating” or moving on hover and we’ll only have a simple navigation.

If you have seen the effect over at the FWA landing page you will notice that the movement directions of the screenshots are random (moving up or down and sliding to the sides). We want to achieve the same effect by randomly adding some data-attributes that control the type of animation.

Please note that we’ll be using CSS 3D Transforms and CSS Animations which might not work in older or mobile browsers.

For the demo we are using some website screenshots from Zurb’s Responsive Gallery.

So, let’s get started!

The Markup

The slideshow has a main container with the class and ID “slideshow” and we can use an ordered list for our slides. Each list item contains a description with a title and a paragraph. It will also contain a division with the class “tiltview” where we will add our screenshots. The classes “col” an “row” will help us set the right layout for the inner anchors:

<div class="slideshow" id="slideshow">
	<ol class="slides">
		<li class="current">
			<div class="description">
				<h2>Some Title</h2>
				<p>Some description</p>
			</div>
			<div class="tiltview col">
				<a href="http://grovemade.com/"><img src="img/1_screen.jpg"/></a>
				<a href="https://tsovet.com/"><img src="img/2_screen.jpg"/></a>
			</div>
		</li>
		<li>
			<div class="description">
				<!-- ... -->
			</div>
			<div class="tiltview row">
				<!-- ... -->
			</div>
		</li>
		<li>
			<!-- ... -->
		</li>
	</ol>
</div>

We’ll also add a navigation element in our JavaScript which we’ll place right after the ordered list. It will consist of a nav with the right amount of spans.

Let’s already have a thought on how we will control the animations for each screenshot. In our script we set a data-attribute for a random incoming and outgoing animation. We’ll use the data-attributes data-effect-in and data-effect-out to control our animations in the CSS. We’ll check out the values for those attributes in a while. Let’s first check out the main style of the slideshow.

The CSS

Note that the CSS will not contain any vendor prefixes, but you will find them in the files (-webkit-).

Our slideshow wrapper and the ordered list will have the following style:

.slideshow {
	position: relative;
	margin-bottom: 100px;
}

.slides {
	list-style: none;
	padding: 0;
	margin: 0;
	position: relative;
	height: 500px;
	width: 100%;
	overflow: hidden;
	background: #ddd;
	color: #333;
}

The slideshow will be 500px high and we need to set the overflow to hidden, so that we don’t see the items fly out.
When we can’t build our slideshow because JavaScript is not enabled, we need to make sure that all slides are shown, so we set the height to auto:

.no-js .slides {
	height: auto;
}

Each list item will be positioned absolutely and occupy all available width and height. By default, we’ll set the visibility to hidden.
Each slide will also serve as the perspective container and we’ll define a perspective value of 1600px:

.slides > li {
	width: 100%;
	height: 100%;
	position: absolute;
	visibility: hidden;
	perspective: 1600px;
}

Let’s not forget the fallback:

.no-js .slides > li {
	position: relative;
	visibility: visible;
}

The navigation which is added dynamically, will appear as a set of lines. Each navigation item is a span and although we are using a tiny line, we want to make sure that the clickable area is actually bigger. This we can simulate by adding a white border:

.slideshow > nav {
	text-align: center;
	margin-top: 20px;
}

.slideshow > nav span {
	display: inline-block;
	width: 60px;
	height: 25px;
	border-top: 10px solid #fff;
	border-bottom: 10px solid #fff;
	background-color: #ddd;
	cursor: pointer;
	margin: 0 3px;
	transition: background-color 0.2s;
}

.slideshow > nav span:hover {
	background-color: #333;
}

.slideshow > nav span.current {
	background-color: #aaa;
}

The description will fill half of the width and since we want a transition on the opacity, we need to set it to 0 initially:

.description {
	width: 50%;
	padding: 2em 4em;
	font-size: 1.5em;
	position: relative;
	z-index: 1000;
	opacity: 0;
}

.no-js .description {
	opacity: 1;
}

.description h2 {
	font-size: 200%;
}

Now, let’s style the most crucial element in our slideshow. The division with the class “tiltview” will help us put our items into perspective. We need to add preserve-3d as transform style because some inner items will need to move on the Z-axis in some animations.
The “tiltview” wrapper will be centered by setting the top to 50% and transforming it -50% on the Y-axis. We’ll also rotate it on the X and Z-axis to create the 3D look:

.tiltview {
	position: absolute;
	left: 50%;
	width: 50%;
	top: 50%;
	transform-style: preserve-3d;
	transform: translateY(-50%) rotateX(60deg) rotateZ(35deg);
}

And the anchors and images will have the following style (the outline helps to avoid jagged edges in Firefox):

.tiltview a {
	outline: 1px solid transparent;
	backface-visibility: hidden;
}

.tiltview a,
.tiltview a img {
	max-width: 100%;
	display: block;
	margin: 0 auto;
}

.tiltview a:first-child {
	margin-bottom: 30px;
}

For the row and column cases we’ll set the widths accordingly:

.tiltview.row a {
	width: 48%;
	width: calc(50% - 15px);
	margin: 0;
}

.tiltview.row a:nth-child(2) {
	left: 50%;
	left: calc(50% + 15px);
	position: absolute;
	top: 0;
}

In our script we will use the classes “show” and “hide” to control the visibility of the slides:

/* Show/Hide */
.slides > li.current,
.slides > li.show {
	visibility: visible;
}

The description will fade in and out:

.description {
	transition: opacity 0.75s;
}

.current .description,
.show .description {
	opacity: 1;
}

.hide .description {
	opacity: 0;
}

As we mentioned before, we’ll control the animations by using some data-attributes. We have to define two types of animations: the incoming one (when we show the next slide) and the outgoing one (when we hide the previous slide).
We want to be able to animate the items in all possible directions, so we’ll need six different types: move up, move down, slide up, slide down, slide left, slide right. This makes a total of 12 animations (incoming and outgoing).

So, let’s define the first one for moving the outgoing element up:

/***********************/
/* Move up */
/***********************/

.hide[data-effect-out="moveUpOut"] .tiltview a {
	animation: moveUpOut 1.5s both;
}

.hide[data-effect-out="moveUpOut"] .tiltview a:nth-child(2) {
	animation-delay: 0.25s;
}

@keyframes moveUpOut {
	25% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateZ(-30px);
	}
	100% {
		transform: translateZ(3000px);
	}
}

We define a slight animation delay for the second item and we use a custom cubic-bezier timing function to add some interesting momentum.

The second animation for this movement is the incoming one which has the initial and end step reversed:

.show[data-effect-in="moveUpIn"] .tiltview a {
	animation: moveUpIn 1.5s 0.5s both;
}

.show[data-effect-in="moveUpIn"] .tiltview a:nth-child(2) {
	animation-delay: 0.75s;
}

@keyframes moveUpIn {
	0% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateZ(-3000px);
	}
	75% {
		transform: translateZ(30px);
	}
	100% {
		transform: translateZ(0);
	}
}

As you might have noticed, we could simplify the animation delay for the “hide” and the “show” case, but keeping the two rules separated will allow for easier adaption in case you’d like to define some different delays.

The resting animations are as follows:

/***********************/
/* Move down */
/***********************/
.hide[data-effect-out="moveDownOut"] .tiltview a {
	animation: moveDownOut 1.5s both;
}

.hide[data-effect-out="moveDownOut"] .tiltview a:nth-child(2) {
	animation-delay: 0.25s;
}

@keyframes moveDownOut {
	25% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateZ(30px);
	}
	100% {
		transform: translateZ(-3000px);
	}
}

.show[data-effect-in="moveDownIn"] .tiltview a {
	animation: moveDownIn 1.5s 0.5s both;
}

.show[data-effect-in="moveDownIn"] .tiltview a:nth-child(2) {
	animation-delay: 0.75s;
}

@keyframes moveDownIn {
	0% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateZ(3000px);
	}
	75% {
		transform: translateZ(-30px);
	}
	100% {
		transform: translateZ(0);
	}
}

/***********************/
/* Slide up */
/***********************/
.hide[data-effect-out="slideUpOut"] .tiltview a {
	animation: slideUpOut 1.5s both;
}

.hide[data-effect-out="slideUpOut"] .tiltview a:nth-child(2) {
	animation-delay: 0.25s;
}

@keyframes slideUpOut {
	25% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateY(30px);
	}
	100% {
		transform: translateY(-3000px);
	}
}

.show[data-effect-in="slideUpIn"] .tiltview a {
	animation: slideUpIn 1.5s 0.5s both;
}

.show[data-effect-in="slideUpIn"] .tiltview a:nth-child(2) {
	animation-delay: 0.75s;
}

@keyframes slideUpIn {
	0% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateY(3000px);
	}
	75% {
		transform: translateY(-30px);
	}
	100% {
		transform: translateY(0);
	}
}

/***********************/
/* Slide down */
/***********************/
.hide[data-effect-out="slideDownOut"] .tiltview a {
	animation: slideDownOut 1.5s both;
}

.hide[data-effect-out="slideDownOut"] .tiltview.row a:nth-child(2),
.hide[data-effect-out="slideDownOut"] .tiltview.col a:first-child {
	animation-delay: 0.25s;
}

@keyframes slideDownOut {
	25% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateY(-30px);
	}
	100% {
		transform: translateY(3000px);
	}
}

.show[data-effect-in="slideDownIn"] .tiltview a {
	animation: slideDownIn 1.5s 0.5s both;
}

.show[data-effect-in="slideDownIn"] .tiltview.row a:nth-child(2),
.show[data-effect-in="slideDownIn"] .tiltview.col a:first-child {
	animation-delay: 0.75s;
}

@keyframes slideDownIn {
	0% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateY(-3000px);
	}
	75% {
		transform: translateY(30px);
	}
	100% {
		transform: translateY(0);
	}
}

/***********************/
/* Slide left */
/***********************/
.hide[data-effect-out="slideLeftOut"] .tiltview a {
	animation: slideLeftOut 1.5s both;
}

.hide[data-effect-out="slideLeftOut"] .tiltview a:nth-child(2) {
	animation-delay: 0.25s;
}

@keyframes slideLeftOut {
	25% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateX(30px);
	}
	100% {
		transform: translateX(-5000px);
	}
}

.show[data-effect-in="slideLeftIn"] .tiltview a {
	animation: slideLeftIn 1.5s 0.5s both;
}

.show[data-effect-in="slideLeftIn"] .tiltview a:nth-child(2) {
	animation-delay: 0.75s;
}

@keyframes slideLeftIn {
	0% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateX(3000px);
	}
	75% {
		transform: translateX(-30px);
	}
	100% {
		transform: translateX(0);
	}
}

/***********************/
/* Slide right */
/***********************/
.hide[data-effect-out="slideRightOut"] .tiltview a {
	animation: slideRightOut 1.5s both;
}

.hide[data-effect-out="slideRightOut"] .tiltview.col a:nth-child(2),
.hide[data-effect-out="slideRightOut"] .tiltview.row a:first-child {
	animation-delay: 0.25s;
}

@keyframes slideRightOut {
	25% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateX(-30px);
	}
	100% {
		transform: translateX(3000px);
	}
}

.show[data-effect-in="slideRightIn"] .tiltview a {
	animation: slideRightIn 1.5s 0.5s both;
}

.show[data-effect-in="slideRightIn"] .tiltview.col a:nth-child(2),
.show[data-effect-in="slideRightIn"] .tiltview.row a:first-child {
	animation-delay: 0.75s;
}

@keyframes slideRightIn {
	0% {
		animation-timing-function: cubic-bezier(1.000, 0.000, 0.000, 1.000);
		transform: translateX(-5000px);
	}
	75% {
		transform: translateX(30px);
	}
	100% {
		transform: translateX(0);
	}
}

Note that for some animations we need to define the animation delay for the first child instead of the second. We need to do this so that the anchors don’t overlap each other for some directions.

When we don’t have support for CSS 3D Transforms or transform-style: preserve-3d, then we want to provide a simple fallback:

/* Fallback for no 3D Transforms and no preserve-3d */
.no-csstransformspreserve3d .show .tiltview a,
.no-csstransformspreserve3d .hide .tiltview a,
.no-csstransforms3d .show .tiltview a,
.no-csstransforms3d .hide .tiltview a {
	animation: none !important;
}

.no-csstransforms3d .tiltview.col {
	top: -50%;
}

.no-csstransforms3d .tiltview.row {
	top: 20px;
}

And last, but not least, we need to make sure that we have a reasonable look for smaller screens. In this case we want the anchors with their screenshots to be just decoration, so we’ll set their opacity lower and make them be not clickable:

@media screen and (max-width: 65.3125em) {
	.description,
	.tiltview {
		width: 100%;
	}

	.tiltview {
		left: 0;
		opacity: 0.3;
		pointer-events: none;
	}
}

@media screen and (max-width: 33.75em) {
	.description {
		font-size: 1.1em;
	}

	.slideshow > nav span {
		width: 20px;
		height: 40px;
		margin: 0 10px;
	}
}

@media screen and (max-width: 24em) {
	.slides {
		height: 320px;
	}

	.description {
		font-size: 1em;
		padding: 1.4em;
	}

	.no-csstransforms3d .tiltview.col,
	.no-csstransforms3d .tiltview.row {
		top: 0;
	}
}

And that’s the style! Let’s do our slideshow script.

The JavaScript

We will start by initializing some variables, like the two arrays with the animation class names that control the incoming and outgoing of our items. These will be picked randomly and set to the items when we navigate the slideshow. Other variables are the items, the current value of the selected item (we assume this will be the first one) and the total number of items inside the slider:

function TiltSlider( el, options ) {
	this.el = el;
	// available effects for the animations (animation class names) - when an item comes in or goes out
	this.animEffectsOut = ['moveUpOut','moveDownOut','slideUpOut','slideDownOut','slideLeftOut','slideRightOut'];
	this.animEffectsIn = ['moveUpIn','moveDownIn','slideUpIn','slideDownIn','slideLeftIn','slideRightIn'];
	// the items
	this.items = this.el.querySelector( 'ol.slides' ).children;
	// total number of items
	this.itemsCount = this.items.length;
	if( !this.itemsCount ) return;
	// index of the current item
	this.current = 0;
	this.options = extend( {}, this.options );
	extend( this.options, options );
	this._init();
}

In order to navigate the slideshow we will add some navigation spans that, when clicked, will make the respective slideshow item appear. The total number of spans will be the same as the total number of items. Let’s add the navigation to our component:

TiltSlider.prototype._addNavigation = function() {
	// add nav "dots"
	this.nav = document.createElement( 'nav' )
	var inner = '';
	for( var i = 0; i < this.itemsCount; ++i ) {
		inner += i === 0 ? '' : '';
	}
	this.nav.innerHTML = inner;
	this.el.appendChild( this.nav );
	this.navDots = [].slice.call( this.nav.children );
}

Next, we need to bind the onclick event to the navigation spans. If we click on any other but the current span, then the current item should animate out and the new one should animate in.

TiltSlider.prototype._initEvents = function() {
	var self = this;
	// show a new item when clicking the navigation "dots"
	this.navDots.forEach( function( dot, idx ) {
		dot.addEventListener( 'click', function() {
			if( idx !== self.current ) {
				self._showItem( idx );
			}
		} );
	} );
}

We need to reference and work with the current item and also the next one that has to appear. We will add and remove classes from both of them in order to apply the respective animations. The animation (i.e. the data-attribute value) itself will be randomly picked from our animEffectsOut and animEffectsIn arrays as described before.

TiltSlider.prototype._showItem = function( pos ) {
	if( this.isAnimating ) {
		return false;
	}
	this.isAnimating = true;

	classie.removeClass( this.navDots[ this.current ], 'current' );

	var self = this,
		// the current item
		currentItem = this.items[ this.current ];

	this.current = pos;

	// next item to come in
	var nextItem = this.items[ this.current ],
		// set random effects for the items
		outEffect = this.animEffectsOut[ Math.floor( Math.random() * this.animEffectsOut.length ) ],
		inEffect = this.animEffectsIn[ Math.floor( Math.random() * this.animEffectsOut.length ) ];

	currentItem.setAttribute( 'data-effect-out', outEffect );
	nextItem.setAttribute( 'data-effect-in', inEffect );

	classie.addClass( this.navDots[ this.current ], 'current' );

	var cntAnims = 0,
		// the number of elements that actually animate inside the current item
		animElemsCurrentCount = currentItem.querySelector( '.tiltview' ).children.length, 
		// the number of elements that actually animate inside the next item
		animElemsNextCount = nextItem.querySelector( '.tiltview' ).children.length,
		// keep track of the number of animations that are terminated
		animEndCurrentCnt = 0, animEndNextCnt = 0,
		// check function for the end of each animation
		isFinished = function() {
			++cntAnims;
			if( cntAnims === 2 ) {
				self.isAnimating = false;
			}
		},
		// function for the end of the current item animation
		onEndAnimationCurrentItem = function() {
			++animEndCurrentCnt;
			var endFn = function() {
				classie.removeClass( currentItem, 'hide' );
				classie.removeClass( currentItem, 'current' );
				isFinished();
			};

			if( !isSupported ) {
				endFn();
			}
			else if( animEndCurrentCnt === animElemsCurrentCount ) {
				currentItem.removeEventListener( animEndEventName, onEndAnimationCurrentItem );
				endFn();
			}
		},
		// function for the end of the next item animation
		onEndAnimationNextItem = function() {
			++animEndNextCnt;
			var endFn = function() {
				classie.removeClass( nextItem, 'show' );
				classie.addClass( nextItem, 'current' );
				isFinished();
			};

			if( !isSupported ) {
				endFn();
			}
			else if( animEndNextCnt === animElemsNextCount ) {
				nextItem.removeEventListener( animEndEventName, onEndAnimationNextItem );
				endFn();
			}
		};

	if( isSupported ) {
		currentItem.addEventListener( animEndEventName, onEndAnimationCurrentItem );
		nextItem.addEventListener( animEndEventName, onEndAnimationNextItem );
	}
	else {
		onEndAnimationCurrentItem();
		onEndAnimationNextItem();
	}

	classie.addClass( currentItem, 'hide' );
	classie.addClass( nextItem, 'show' );
}

And that’s it! We hope you enjoyed this tutorial and find it useful!

Note that IE11 still does not support transform-style: preserve-3d, so the fallback will be shown there.

View demo Download source

Inspiration for Item Transitions

$
0
0

ItemTransitions

View demo Download source

Today we’d like to share some item transition inspiration with you. We tried to keep the idea of the transition general, so we did three different use cases: a small image slideshow, a large header slideshow and a slideshow using product images with a transparent background. These transitions don’t have to be restricted to a slideshow, of course. But to see them in action, this was the best option for a demo. You can surely imagine transitioning from one state to another in many different circumstance (think about scrolling a page, loading items, clicking a button, sending a form, and many many more).

A very special case is the usage of (product) images with transparent background. Depending on the item itself, there are many possibilities for transitions that imitate the real-world movement or behavior. A great example in the wild can be found on the brilliant hat presentation section of Optimo Chicago. The slideshow conveys a feeling of throwing a hat into the viewport, transmitting the feeling of lightness and augmenting the viewing experience. This is such an excellent example of how adding the right animations can enhance product browsing and bring it to another level. In our last demo, we give you some simple inspiration using a wine bottle to explore the feeling for heavier objects.

For transitioning the state, we are using CSS Animations. This allows us to specify a certain behavior for the items when coming from any direction. This can be useful for direction-aware navigation of items, like you can see in the demos.

One of the transitions that can be viewed in the large header slideshow example is based on Hakim El Hattab’s slick context-shift transition, Kontext, and another one makes use of one of Lionel’s CSS Shake animations.

Please note that in some examples we are using CSS Animations on pseudo-elements which might not be supported in some browsers (especially mobile browsers).

Please also note that the examples only serve for your inspiration. We haven’t implemented any fallbacks.

We’ve divided the inspiration for transitions into three demos:

ItemTransitions01

Small Component: these effects are especially interesting for smaller sized images.

ItemTransitions02

Full Width: Having a large header image or background let’s us explore a different set of effects but also restricts the movements and speeds a bit.

ItemTransitions03

Transparent Background: Here we can play with physics and effects that resemble the movements of objects in the real world. Using the right animations can help transmit the feeling for the weight and material of an item.

The images in the first demo are from the Hand Lettering project by Ben Johnston. The ones in the second demo are from Unsplash and the wine bottle mockups can be found over at Pixeden.

We hope you enjoy these effects and find them inspiring.

View demo Download source


How to Create a Tiled Background Slideshow

$
0
0



FourBoxes

View demo Download source

Today we’d like to show you how to recreate the background image slideshow seen on the stunning website of Serge Thoroval’s Atelier that was coded and designed by talented Jean-Christophe Suzanne and Jonathan Da Costa. If you haven’t seen the website, you should definitely go and check it out; it’s full of interesting and creative effects. The slideshow that we are recreating today, is made up of four tiles that move individually, making the image split while scaling up the new image. This combines into a really nice effect which we will re-implement today using 3D transforms, transitions and animations. In addition to that effect we’ll also add two more variations. The aim that we want to keep in mind, is to achieve a super-smooth effect experience.

Note that we’ll be using very modern CSS properties, so the desired effect will only work in the newest browser versions.

The images used in the demos are made with really great mobile phone PSD mockups from the team of Mockuuups.com. You can find the free iPhone 5S Mockups in this Dribbble post by Tomas Jasovsky.

Let’s get started with the markup.

The Markup

For the slideshow we will need a special structure that will allow us to play with four different tiles, all containing the same image but with different positioning. We need to make sure that everything stretches over the full viewport since this is a “fullscreen” slideshow. We’ll define a simple initial structure that will allow us to specify which images will be visible in each panel (or slide) and then we’ll create our repeated structure for the tiles. So, initially we want something like this:

<div id="boxgallery" class="boxgallery" data-effect="effect-1">
	<div class="panel"><img src="img/1.jpg" alt="Image 1"/></div>
	<div class="panel"><img src="img/2.jpg" alt="Image 2"/></div>
	<div class="panel"><img src="img/3.jpg" alt="Image 3"/></div>
	<div class="panel"><img src="img/4.jpg" alt="Image 4"/></div>
</div>

In order to be able to animate the image by breaking it into tiles we will need the following repeated structure for each panel:

<div id="boxgallery" class="boxgallery" data-effect="effect-1">
	<div class="panel current">
		<div class="bg-tile">
			<div class="bg-img"><img src="img/1.jpg" /></div>
		</div>
		<div class="bg-tile">
			<div class="bg-img"><img src="img/1.jpg" /></div>
		</div>
		<div class="bg-tile">
			<div class="bg-img"><img src="img/1.jpg" /></div>
		</div>
		<div class="bg-tile">
			<div class="bg-img"><img src="img/1.jpg" /></div>
		</div>
	</div>
	<div class="panel">
		<div class="bg-tile">
			<div class="bg-img"><img src="img/2.jpg" /></div>
		</div>
		<div class="bg-tile">
			<div class="bg-img"><img src="img/2.jpg" /></div>
		</div>
		<div class="bg-tile">
			<div class="bg-img"><img src="img/2.jpg" /></div>
		</div>
		<div class="bg-tile">
			<div class="bg-img"><img src="img/2.jpg" /></div>
		</div>
	</div>
	<div class="panel">
		<!-- ... -->
	</div>
	<div class="panel">
		<!-- ... -->
	</div>
	<nav>
		<span class="prev"><i></i></span>
		<span class="next"><i></i></span>
	</nav>
</div>

We’ll also have to add a navigation so that we can browse through the panels. The data attribute data-effect will make it possible to define some individual variations of the effect.

Naturally, you might look at the structure and think, why not use background images instead of image elements? After some tries on what performs better cross-browser wise, we came to the conclusion that using a background image together with background-size can cause quite choppy transitions. Using for example, background-size: cover can be a major source of jerkiness. Another critical thing that causes twitchy transitions (at least in Chrome) is using percentage values when translating. This might not be noticeable at first glance but when comparing with a pixel-based translation, you will be able to see an impactful difference. For that reason we will not be using classes to control the transforms, but we’ll instead do it in our script by getting the pixel values of the viewport and applying the respective translation values to the tiles’ “background images”.

Let’s add some style to this thing.

The CSS

With the generated structure in mind, we’ll now add the style.
Note that the CSS will not contain any vendor prefixes, but you will find them in the files.

First off, we are in the “fullscreen” realm, so we need to prepare our page elements for that. Setting the html, the body and the main container to a height of 100% will allow us to expand our slideshow to the full height of the viewport:

html, body, .container {
	height: 100%;
}

The main wrapper and the child divisions will all be of absolute positioning and the panels will occupy all the width and height:

.js .boxgallery,
.js .boxgallery div {
	position: absolute;
}

.js .boxgallery,
.js .panel {
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
}

Since our effects will potentially have elements moving out of their limits, we’ll make sure that nothing overflows:

.js .boxgallery,
.bg-tile,
.bg-img {
	overflow: hidden;
}

When looking at the effect of Serge Thoroval’s slideshow, you’ll notice that when navigating, the newly revealed slide scales up. We’ll be using 3D transforms wherever we can so we’ll need to add some perspective to the slide wrapper so that we can then translate the individual tiles on the z-Axis, i.e. make them scale down visually:

.panel {
	z-index: 0;
	perspective: 1200px;
}

Next, we have to style the tiles. Since we have four tiles, each tile will be 50% high and wide:

.bg-tile {
	width: 50%;
	height: 50%;
}

Let’s position each tile:

.bg-tile:nth-child(2),
.bg-tile:nth-child(4) {
	left: 50%;
}

.bg-tile:nth-child(3),
.bg-tile:nth-child(4) {
	top: 50%;
}

The inner division with the class bg-img will occupy the whole tile:

.bg-img {
	width: 100%;
	height: 100%;
	background: #999;
}

In order to place the images in a way that only shows one complete image per tile, we need to set it’s width and height to the double. Using percentages on both, width and height will distort our image so we need to make sure that we maintain the aspect ration. We can do that by setting only one of the dimensions to 200% and let the other one be defined automatically. But which one can we set? Well, that all depends on whether our viewport is more wide than high and vice versa. Fortunately, we have media queries and one expression in particular will help us do exactly what we want: min-aspect-ratio. This allows us to simply take the real size of our images as ratio value (width/height) and define, when we want the width and when the height to be calculated automatically:

.bg-img img {
	position: absolute;
	display: block;
	height: 200%;
}

@media screen and (min-aspect-ratio: 1280/850) {
	.bg-img img {
		width: 200%;
		height: auto;
	}
}

You can fine-tune this method by also adding width/height expressions in order to offer the desired viewing experience of the image for any screensize. For out slideshow, this little simple method will do.

Each image needs to be positioned as well:

.bg-tile:nth-child(2) .bg-img img,
.bg-tile:nth-child(4) .bg-img img {
	left: -100%;
}

.bg-tile:nth-child(3) .bg-img img,
.bg-tile:nth-child(4) .bg-img img {
	top: -100%;
}

The navigational arrows have the “Fill Path” style from the Arrow Navigation Styles.

So let’s define what happens when we navigate.
First of all, we need to set a z-index value to the current panel so that it stays on top of all the others. Since we’ve previously set all out panels’ z-indices to 0, we can use a value like 1. But because we’ll have a current panel but also one that needs to be shown directly under it (the next or previous one), we’ll set the z-indices as follows:

.panel.current {
	z-index: 2;
}

.panel.active {
	z-index: 1;
}

This will guarantee that the current one is on top of any other and the newly coming one is directly under it.

Considering that we will add the individual 3D transforms to each tile’s bg-img in our JavaScript (remember, we want to use pixel-based values for which we need the window width and height), we still want to set some things in our style sheet, like the transition itself and the animation for the new panel.
So, let’s define a transition on the transform property with a time and easing function that fit nicely:

.panel.current .bg-img {
	transition: transform 1.1s ease-in-out;
}

For the first effect (the one that we are trying to recreate) and the second one (which moves the tiles to the outside), we want the following panel to appear with a scale down effect:

.boxgallery[data-effect="effect-1"] .panel.active .bg-tile,
.boxgallery[data-effect="effect-2"] .panel.active .bg-tile {
	animation: scaleDown 1.1s ease-in-out;
}

@keyframes scaleDown {
	from { transform: translate3d(0,0,380px); }
	to { transform: translate3d(0,0,0); }
}

The following variation of the effect looks really great when adding a little delay to each tile’s child and making everything a bit faster with a nice cubic-bezier timing function. This variation will make the tiles move to the sides when navigating next and up/down when navigating to the previous panel:

/* Variation 2 */

.boxgallery[data-effect="effect-2"] .panel.current .bg-img {
	transition: transform 0.9s cubic-bezier(0.7,0,0.3,1);
}

.boxgallery[data-effect="effect-2"] .panel.current .bg-tile:nth-child(2) .bg-img {
	transition-delay: 0.15s;
}

.boxgallery[data-effect="effect-2"] .panel.current .bg-tile:nth-child(3) .bg-img {
	transition-delay: 0.3s;
}

.boxgallery[data-effect="effect-2"] .panel.current .bg-tile:nth-child(4) .bg-img {
	transition-delay: 0.45s;
}

The third variation serves as an example on how to use an overlay that will fade out once a new panel is revealed:

/* Variation 3 */

.boxgallery[data-effect="effect-3"] .panel::after {
	position: absolute;
	width: 100%;
	height: 100%;
	background: rgba(0,0,0,0.8);
	content: '';
	transition: opacity 1.1s ease-in-out;
}

.boxgallery[data-effect="effect-3"] .panel.current::after,
.boxgallery[data-effect="effect-3"] .panel.active::after {
	opacity: 0;
}

.boxgallery[data-effect="effect-3"] .panel.current::after {
	transition: none;
}

We’ll also use the same cubic-bezier timing function and define some delays (same as before).

.boxgallery[data-effect="effect-3"] .panel.current .bg-img {
	transition: transform 1.1s cubic-bezier(0.7,0,0.3,1);
}

.boxgallery[data-effect="effect-3"] .panel.current .bg-tile:nth-child(2) .bg-img {
	transition-delay: 0.15s;
}

.boxgallery[data-effect="effect-3"] .panel.current .bg-tile:nth-child(3) .bg-img {
	transition-delay: 0.3s;
}

.boxgallery[data-effect="effect-3"] .panel.current .bg-tile:nth-child(4) .bg-img {
	transition-delay: 0.45s;
}

After we go through the JavaScript, make sure to play with these values and add a new effect, there are so many possibilities!

The JavaScript

Like mentioned before, we’ve opted to manipulate the translation values directly in the JS, using pixel-based values obtained from the viewport width and height for a smoother transition. Alternatively, we could set classes and define everything in the stylesheet using percentage-based values, but this leaves us with jerky transitions. (In case we would use an effect that does not need any of those viewport dependent values (e.g. opacity, absolute values, scale, etc.) we could definitely go with the class-based solution.)

Let’s have a look at the init function.
We will start by defining the transforms that we want to apply to the panels as we navigate the slideshow. We need to specify which transforms we want the panels to have for both directions (navigate to the right and left, next and previous, respectively). Let’s define a fitting structure which we set in the setTransforms function.

Next, we get the effect type from the data-effect attribute. This will indicate which transforms we pick from the previously created structure.
We then initialize and cache some variables and finally we create the necessary DOM structure for our four boxes. For that, the image in each panel is replaced by four divisions, each one having an image element with the same src like the image. We also add the nav element at this point.

BoxesFx.prototype._init = function() {
	// set transforms configuration
	this._setTransforms();
	// which effect
	this.effect = this.el.getAttribute( 'data-effect' ) || 'effect-1';
	// check if animating
	this.isAnimating = false;
	// the panels
	this.panels = [].slice.call( this.el.querySelectorAll( '.panel' ) );
	// total number of panels (4 for this demo)
	//this.panelsCount = this.panels.length;
	this.panelsCount = 4;
	// current panel´s index
	this.current = 0;
	classie.add( this.panels[0], 'current' );
	// replace image with 4 divs, each including the image
	var self = this;
	this.panels.forEach( function( panel ) {
		var img = panel.querySelector( 'img' ), imgReplacement = '';
		for( var i = 0; i < self.panelsCount; ++i ) {
			imgReplacement += '<div class="bg-tile"><div class="bg-img"><img src="' + img.src + '" /></div></div>'
		}
		panel.removeChild( img );
		panel.innerHTML = imgReplacement + panel.innerHTML;
	} );
	// add navigation element
	this.nav = document.createElement( 'nav' );
	this.nav.innerHTML = '<span class="prev"><i></i></span><span class="next"><i></i></span>';
	this.el.appendChild( this.nav );
	// initialize events
	this._initEvents();
}

The translate values are calculated using the window width and height. We also add 10 pixels so that the transitions don’t really end in the last visible point, but rather a bit further, adding some more smoothness to the effects:

BoxesFx.prototype._setTransforms = function() {
	this.transforms = {
		'effect-1' : {
			'next' : [
				'translate3d(0, ' + (win.height/2+10) + 'px, 0)', // transforms for panel 1
				'translate3d(-' + (win.width/2+10) + 'px, 0, 0)', // transforms for panel 2
				'translate3d(' + (win.width/2+10) + 'px, 0, 0)', // transforms for panel 3
				'translate3d(0, -' + (win.height/2+10) + 'px, 0)' // transforms for panel 4
			],
			'prev' : [
				'translate3d(' + (win.width/2+10) + 'px, 0, 0)',
				'translate3d(0, ' + (win.height/2+10) + 'px, 0)',
				'translate3d(0, -' + (win.height/2+10) + 'px, 0)',
				'translate3d(-' + (win.width/2+10) + 'px, 0, 0)'
			]
		},
		'effect-2' : {
			'next' : [
				'translate3d(-' + (win.width/2+10) + 'px, 0, 0)',
				'translate3d(' + (win.width/2+10) + 'px, 0, 0)',
				'translate3d(-' + (win.width/2+10) + 'px, 0, 0)',
				'translate3d(' + (win.width/2+10) + 'px, 0, 0)'
			],
			'prev' : [
				'translate3d(0,-' + (win.height/2+10) + 'px, 0)',
				'translate3d(0,-' + (win.height/2+10) + 'px, 0)',
				'translate3d(0,' + (win.height/2+10) + 'px, 0)',
				'translate3d(0,' + (win.height/2+10) + 'px, 0)'
			]
		},
		'effect-3' : {
			'next' : [
				'translate3d(0,' + (win.height/2+10) + 'px, 0)',
				'translate3d(0,' + (win.height/2+10) + 'px, 0)',
				'translate3d(0,' + (win.height/2+10) + 'px, 0)',
				'translate3d(0,' + (win.height/2+10) + 'px, 0)'
			],
			'prev' : [
				'translate3d(0,-' + (win.height/2+10) + 'px, 0)',
				'translate3d(0,-' + (win.height/2+10) + 'px, 0)',
				'translate3d(0,-' + (win.height/2+10) + 'px, 0)',
				'translate3d(0,-' + (win.height/2+10) + 'px, 0)'
			]
		}
	};	
}

Next thing to do is to initialize the events on the navigation arrows, and the window resize event (we need to reset our transforms):

BoxesFx.prototype._initEvents = function() {
	var self = this, navctrls = this.nav.children;
	// previous action
	navctrls[0].addEventListener( 'click', function() { self._navigate('prev') } );
	// next action
	navctrls[1].addEventListener( 'click', function() { self._navigate('next') } );
	// window resize
	window.addEventListener( 'resize', function() { self._resizeHandler(); } );
}

Let’s take a look at the navigate function.
First, we get both, the current and the “upcoming” panels. We also need to reset the current variable value depending on the navigation action we take.
Next, we add the active class to the current panel in order to trigger any animation or transition the element might have (as previously defined in our style sheet). We finally set the transforms to the current panel which we defined before. This should make the four panels animate and cause the next slide to reveal. We just need to make sure we reset both, the classes we’ve added and the transforms on the elements. We do that once all the transitions for all four panels have finished:

BoxesFx.prototype._navigate = function( dir ) {
	if( this.isAnimating ) return false;
	this.isAnimating = true;

	var self = this, currentPanel = this.panels[ this.current ];

	if( dir === 'next' ) {
		this.current = this.current < this.panelsCount - 1 ? this.current + 1 : 0;			
	}
	else {
		this.current = this.current > 0 ? this.current - 1 : this.panelsCount - 1;
	}

	// next panel to be shown
	var nextPanel = this.panels[ this.current ];
	// add class active to the next panel to trigger its animation
	classie.add( nextPanel, 'active' );
	// apply the transforms to the current panel
	this._applyTransforms( currentPanel, dir );

	// let´s track the number of transitions ended per panel
	var cntTransTotal = 0,
		
		// transition end event function
		onEndTransitionFn = function( ev ) {
			if( ev && !classie.has( ev.target, 'bg-img' ) ) return false;

			// return if not all panel transitions ended
			++cntTransTotal;
			if( cntTransTotal < self.panelsCount ) return false;

			if( support.transitions ) {
				this.removeEventListener( transEndEventName, onEndTransitionFn );
			}

			// remove current class from current panel and add it to the next one
			classie.remove( currentPanel, 'current' );
			classie.add( nextPanel, 'current' );
			// reset transforms for the currentPanel
			self._resetTransforms( currentPanel );
			// remove class active
			classie.remove( nextPanel, 'active' );
			self.isAnimating = false;
		};

	if( support.transitions ) {
		currentPanel.addEventListener( transEndEventName, onEndTransitionFn );
	}
	else {
		onEndTransitionFn();
	}
}

BoxesFx.prototype._applyTransforms = function( panel, dir ) {
	var self = this;
	[].slice.call( panel.querySelectorAll( 'div.bg-img' ) ).forEach( function( tile, pos ) {
		tile.style.WebkitTransform = self.transforms[self.effect][dir][pos];
		tile.style.transform = self.transforms[self.effect][dir][pos];
	} );
}

BoxesFx.prototype._resetTransforms = function( panel ) {
	[].slice.call( panel.querySelectorAll( 'div.bg-img' ) ).forEach( function( tile ) {
		tile.style.WebkitTransform = 'none';
		tile.style.transform = 'none';
	} );
}

And that's it! I hope you enjoyed this tutorial and find it useful!

View demo Download source

Draggable Dual-View Slideshow

$
0
0











DRaggableDualViewSlideshow

View demo Download source

Today we’d like to share an experimental slideshow with you. The idea is based on the great navigation concept from the Wild website where one can view the projects in either fullscreen or in a carousel mode. There are different techniques that would allow for such a layout; we tried to re-create this slideshow concept by using the Dragdealer.js library and 3D transforms. The main idea is to translate the slider on the Z axis, allowing it to be either fullscreen or of a smaller carousel size. By switching the actual size of the slideshow and removing the transforms when they are not needed anymore, we allow the slideshow to have a “real” size (i.e. 25% of the window). For the content part, we slide everything up and reveal a scrollable area.

Please note that this slideshow is highly experimental and only works properly in modern browsers.

The images used in the demo are from Unsplash.com.

When the slideshow is loaded, we show some info initially, that shows which interaction possibilities exist:
DraggableFullscreenSlideshow00

The main view of the slideshow is the fullscreen one. This is the only view shown for mobile:
DraggableFullscreenSlideshow01

When clicking on the arrow button, the content will be revealed in a sliding transition:
DraggableFullscreenSlideshow03

When clicking on the button for the view switch, the slideshow will be “zoomed out” to a smaller version:
DraggableFullscreenSlideshow02

Please note that at the time of release, Firefox (30.0, Mac & Windows) was the worst performing browser with issues around using 3D transforms, animations and transitions. Although we tried to fix all issues, some effects might not work properly.

We hope you enjoy this experiment and find it inspiring!

View demo Download source

Wobbly Slideshow Effect

$
0
0

WobblySlideshowEffect

View demo Download source

Today we’d like to share an experimental slideshow effect with you. The idea is based on a Dribble shot called GIF Exercise by Sergey Valiukh where a lot of jell-o-like movements are happening. Navigating from one item to another makes the items move in a wobbly fashion and we thought this might be great to try out on a real slideshow. To achieve this effect we used Snap.svg and animated the slide background SVGs in order to simulate the elastic effect. What happens is that we basically morph an SVG path from a rectangle to a curved shaped; either a right curve or a left curve, depending on where we are navigating to.

Please note that this is highly experimental and tested only in the latest versions of modern browsers.

WobblySlideshowEffect_01

The beautiful icons are from the great free Ballicons 2 icon set created by Pixel Buddha and released on Smashing Magazine.

The markup of the slideshow is a unordered list wrapped by a division:

<div id="slideshow" class="slideshow">
	<ul>
		<li>
			<div class="slide">
				<!-- ... -->
			</div>
		</li>
		<li><!-- ... --></li>
		<li><!-- ... --></li>
		<!-- ... -->
	</ul>
</div>

What we do is to insert an SVG before the slide with a path that is a rectangle. The SVG looks as follows after being inserted:

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 80 60" preserveAspectRatio="none">
	<path d="M33,0C33,0,74,0,74,0C74,0,69,9.871,69,29.871C69,49.871,74,60,74,60C74,60,32.666,60,32.666,60C32.666,60,32.541,60,32.541,60C32.541,60,6,60,6,60C6,60,1,50,1,30C1,10,6,0,6,0C6,0,33,0,33,0" />
</svg>

The three different SVG paths make up the following shapes:

paths

You can also find the SVGs in the image folder.

Depending on where we navigate to, we will either bend the shape to the left or to the right. This animation will make it seem as if the are pulling or pushing an elastic object.

The paths are defined in the slideshow options:

SliderFx.prototype.options = {

	// default transition speed (ms)
	speed : 500,

	// default transition easing
	easing : 'ease',

	// path definitions
	paths : {
		rect : 'M33,0h41c0,0,0,9.871,0,29.871C74,49.871,74,60,74,60H32.666h-0.125H6c0,0,0-10,0-30S6,0,6,0H33',
		curve : { 
			right : 'M33,0h41c0,0,5,9.871,5,29.871C79,49.871,74,60,74,60H32.666h-0.125H6c0,0,5-10,5-30S6,0,6,0H33', 
			left : 'M33,0h41c0,0-5,9.871-5,29.871C69,49.871,74,60,74,60H32.666h-0.125H6c0,0-5-10-5-30S6,0,6,0H33'
		}
	}

}

Here is how we actually control the morphing of the paths when navigating:

SliderFx.prototype._morphSVGs = function( callback ) {

	var self = this,
		speed = this.options.speed,
		pathCurvedLeft = this.options.paths.curve.left,
		pathCurvedRight = this.options.paths.curve.right,
		pathRectangle = this.options.paths.rect,
		dir = this.old < this.curr ? 'right' : 'left';

	// morph svg path on exiting slide to "curved"
	this.items[ this.old ].path.stop().animate( { 'path' : dir === 'right' ? pathCurvedLeft : pathCurvedRight }, speed * .5, mina.easeout );

	// the slider starts a bit later...
	setTimeout(function() { callback.call(); }, speed * .2 );
	
	// change svg path on entering slide to "curved"
	var currItem = this.items[ this.curr ];
	currItem.querySelector('path').setAttribute( 'd', dir === 'right' ? pathCurvedLeft : pathCurvedRight );
	
	// morph svg path on entering slide to "rectangle"
	setTimeout(function() { currItem.path.stop().animate( { 'path' : pathRectangle }, speed * 3, mina.elastic ); }, speed * .5 );

}	

The timing is the most important aspect here. For example, when morphing the SVG path to the right curve, we want to make sure that the morphing takes half as long as the movement of the slides (i.e. speed * .5). This ensures that we still see it getting curved before the slide disappears. We also delay the start of the slider movement a tidbit. The entering slide's path will be first set to be curved and then we morph it to the rectangle shape. You can change the direction of the curve so that you change the effect to look like the element is being pulled or pushed. Change dir === 'right' to dir === 'left' for the entering slide to see the difference. In the last morph where we set the entering path to become a rectangle, we can play with two durations: the speed of the morphing and the delay. We want the morphing to take longer so that we actually see the incoming shape performing its jell-o show. Play with that value and if you like, the easing, in order to achieve subtle, but impactful effects.

WobblySlideshowEffect_next

It's a matter of how you want the effect to appear, so if you play with all those timings, you will see how the movement reflects almost different substances: would you like some pudding or do you prefer some jell-o? :)

If you'd like to see another example in action, check it out here: Alternative Wobbly Slideshow Effect

The code here was changed as follows:

// morph svg path on exiting slide to "curved"
this.items[ this.old ].path.stop().animate( { 'path' : dir === 'right' ? pathCurvedLeft : pathCurvedRight }, speed * .7, mina.easeout );

// the slider starts a bit later...
setTimeout(function() { callback.call(); }, speed * .3 );

// change svg path on entering slide to "curved"
var currItem = this.items[ this.curr ];
currItem.querySelector('path').setAttribute( 'd', dir === 'left' ? pathCurvedLeft : pathCurvedRight );

// morph svg path on entering slide to "rectangle"
setTimeout(function() { currItem.path.stop().animate( { 'path' : pathRectangle }, speed * 5, mina.elastic ); }, speed * .8 );	

You can read more about how to use Snap.svg on their website and especially the documentation.

We hope you enjoy this little fun effect and get inspired!

View demo Download source

Wobbly Slideshow Effect was written by Mary Lou and published on Codrops.

Perspective Mockup Slideshow

$
0
0

MockupSlideshow

A while back, Franklin Ta wrote an article and made a really useful script for transforming an element in 3D in order to fit in a perspective mockup. In his article, he describes how the helper script can be used to create a 3D matrix transformation for embedding an iframe into a mockup image. We thought that it must be really interesting to add a slideshow in order to showcase ones work.

So we’ve used Franklin’s script to create the transformed elements and added a slideshow inside. In order to make the whole thing responsive (the transformation is based on pixels), we scale the main mockup in order to fit into its parent container. The nice thing of using the 3D matrix transforms is that we can use the “real” size for the images in the slideshow (i.e. based on the devices we display). Head over to Franklin’s article to learn how his script works in detail and in order to understand the interesting Math behind it.

In summary, the script can be executed using the console of your dev tools. The corners of the selected element can then be dragged into position and the transform value can be copied (if you inspect your element, you’ll see the value) and pasted into the respective class of the screen element.

Here is an example how we’ve added the transforms to one of our mockup screens (demo 1):

.mobile {
	overflow: hidden;
	position: absolute;
	z-index: 100;
	background: #333;
	width: 320px;
	height: 480px;
	top: 200px;
	left: 500px;
	outline: 1px solid transparent; /* For Firefox (jagged edges bug) */
	transform-origin: 0 0 0;
	transform: matrix3d(0.846234173238242, 0.251585817964749, 0, 0.000085171934399447, -0.115203182108559, 0.800700357116676, 0, -0.000214263459947427, 0, 0, 1, 0, 23, 14, 0, 1);
}

Don’t forget to add the -webkit- prefix for support in Safari, iOS Safari and Android browsers.

The simple slideshow plugin uses CSS animations to show and hide the slides. We’ve used some custom animations, but you can plug in any animation from Daniel Eden’s animate.css.

The styles for the slideshow are the following:

.slideshow {
	padding: 0;
	margin: 0;
	width: 100%;
	height: 100%;
	list-style-type: none;
}

.slideshow__item {
	width: 100%;
	height: 100%;
	position: absolute;
	overflow: hidden;
	pointer-events: none;
	z-index: 1;
	transform: translate3d(-100%, 0, 0);
}

.slideshow__item.current{
	pointer-events: auto;
	z-index: 100;
	transform: translate3d(0, 0, 0);
}

.slideshow img {
	width: 100%;
}

We “hide” the slides by translating them to the left (instead of using opacity, for example). The current item will get the class “current”.

An example for the animation classes is the following (demo 2):

.slideshow__item.in--next {
	animation: inNext 0.5s forwards;
}

.slideshow__item.out--next {
	animation: outNext 0.5s forwards;
}

.slideshow__item.in--prev {
	animation: inPrev 0.5s forwards;
}

.slideshow__item.out--prev {
	animation: outPrev 0.5s forwards;
}

@keyframes inPrev {
	0% {
		transform: translate3d(0, 100%, 0);
	}
	100% {
		transform: none;
	}
}

@keyframes inNext {
	0% {
		transform: scale3d(0.5, 0.5, 1);
	}
	100% {
		transform: none;
	}
}

@keyframes outPrev {
	100% {
		transform: scale3d(0.5, 0.5, 1);
	}
}

@keyframes outNext {
	100% {
		transform: translate3d(0, 100%, 0);
	}
}

Here you can check out some different examples:

MockupSlideshow01

MockupSlideshow02

MockupSlideshow03

MockupSlideshow04

In this last example we have two slideshows running at different times. This might be an interesting idea to showcase responsive works.

You can download the ZIP file of all demos here: Mockup Slideshow ZIP

We hope you like this little idea and find it useful!

A big thank you to the authors of the fantastic resources used in the demos:

Demo 2 image copyright by Vadim Sherbakov with granted permission to be used in our demo:

Perspective Mockup Slideshow was written by Mary Lou and published on Codrops.

Color Extraction Effect

$
0
0

ColorExtraction

View demo Download source

Today we’d like to share a fun little color extraction experiment with you. The idea is to create a color palette from an image with a subtle effect on the image itself and some fanciness on the palette. We are using Vibrant.js by Jari Zwarts to extract the colors from the images, and CSS Filters to transition the image to a black and white version. We also implemented a simple slider with a special “curtain” effect.

Attention: This experiment uses some CSS properties that might not work in older browsers (CSS Animations, CSS Filters).

ColorExtraction_01

For IE 10+, where we don’t have support for CSS Filters (only in Edge behind a flag), we added a simple fallback where we place a SVG image on top of the colored one and apply a SVG filter effect.

ColorExtraction_02

For the color palette items, we add a little wave as background image and animate it, to simulate the item getting “filled” with the color that we extract using Vibrant.js. The Hex color code is placed beneath the extracted color.

ColorExtraction_03

We hope you enjoy this little experiment and find it inspiring!

View demo Download source

Color Extraction Effect was written by Mary Lou and published on Codrops.

Viewing all 53 articles
Browse latest View live