Quantcast
Channel: 3D – Codrops
Viewing all 120 articles
Browse latest View live

3D Flipping Circle with CSS3 and jQuery

$
0
0


3D Flipping Circle with CSS3 and jQuery

View demo Download source

Today I would like to show you how to create a little component with a realistic touch. The idea is based on the sweet paper effect found in the video of the Google Developer Stories. Maybe you have created these kind of paper effects when you were a kid; basically something opens or rotates when pulling or pushing some handle.

We’ll create a circle with a handle which will open once the little handle is clicked, creating a realistic 3D flipping effect with the help of CSS 3D transforms and shadows/gradients.

Check out 00:27 of the same video on HTML5 Rocks so that you can see and understand the effect.

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

The Markup

So, let’s start with the markup. We’ll use some divisions for the shading effects, the handle pieces and the circle parts. What we need, is a main wrapper with the class fc-wrapper which will be a circle. On top of that we will add an overlay which will make the right part of the circle darker once we open the rest. From this lower-most circle we will actually just see the right part. The div with the class fc-handle-pull will be the right-most handle piece outside of the circle. We’ll add a container for the perspective and inside we’ll add the mentioned overlay for the right part and also the second handle piece (it looks like one, but it’s actually two pieces) which has the class fc-handle-out.

Now, we need a division for the left part which will be visible once we open the other half. Here we will add our quote. Note, that we are also adding some overlays which will help us make the opening animation look more realistic.

The division with the class fc-flip will contain a front part and a back part. The back part will not be visible because we will rotate it 180 degrees on the Y-axis. It will serve as the backface of the front part. We’ll add the last bit of the handle to the front since we will want to move it when “opening” the flip container.

<div class="fc-wrapper">
	
	<!-- right-most handle piece -->
	<div class="fc-handle fc-handle-pull"></div>
	
	<div class="fc-perspective">
		
		<!-- right part overlay; get's darker -->
		<div class="fc-overlay fc-overlay-reverse"></div>
		
		<!-- middle handle piece -->
		<div class="fc-handle fc-handle-out"><div></div></div>
		
		<!-- inner bottom content part -->
		<div class="fc-bottom">
			<div class="fc-bottom-bg">
				<div class="fc-content">
					<p>I can live with doubt, and uncertainty, and not knowing. I think it's much more interesting to live not knowing than to have answers which might be wrong. <span>Richard Feynman</span></p>
				</div>
			</div>
			<div class="fc-overlay fc-overlay-solid"></div>
		</div><!-- //fc-bottom -->
		
		<!-- front and back of the flipping half -->
		<div class="fc-flip">
		
			<div class="fc-front">
			
				<div class="fc-inner">
				
					<div class="fc-overlay fc-overlay-left"></div>
					<!-- left-most part of handle -->
					<div class="fc-handle fc-handle-in"><div></div></div>
					
					<div class="fc-content">
						<h3>Free revelations</h3>
						<p>by Codrops</p>
					</div>
					
				</div>
				
			</div><!-- //fc-front -->
			
			<div class="fc-back">
			
				<div class="fc-inner">
				
					<div class="fc-content">
						<div class="feynman">
							<span>1918 – 1988</span>
						</div>
					</div>
					
					<div class="fc-overlay fc-overlay-right"></div>
					
				</div>
				
			</div><!-- //fc-back -->				 
			
		</div><!-- //fc-flip -->				 
		
	</div><!-- //fc-perspective -->
	
</div><!-- //fc-wrapper -->

So, basically, we will have a circular left part and a circular right part that will flip open once we click on the right-most part of the handle.

Let’s style this whole thing.

The CSS

The main wrapper will be 300 pixels high and wide and we’ll center it on the page. For all of the circle elements we will use a paper-like texture image that we’ll overlay on a background color, giving it a realistic touch. The wrapper will be shaped as a circle since we give it a border radius of 50%:

.fc-wrapper {
	width: 300px;
	height: 300px;
	position: relative;
	margin: 30px auto 0;
	background: #846aa7 url(../images/paper.png) repeat center center;
	border-radius: 50%;
	box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
}

Let’s take a look at the handle pieces. All the pieces will have the fc-handle class in common:

.fc-handle {
	position: absolute;
	top: 50%;
	right: 0px;
	width: 80px;
	height: 30px;
	margin-top: -15px;
	background: #775b9d;
	box-shadow: 0 1px 1px rgba(0,0,0,0.1);
}

The next class will define the position for the middle part:

.fc-handle-out {
	right: -65px;
	width: 65px;
}

fc-handle-in is the left most part:

.fc-handle-in {
	right: 80px;
}

The inner divisions of the two handle pieces that are on top of the circle will serve as shadows. When we “pull” the handle from the right, the shadow dropping on the circle will need to look like a triangle that gets taller. We will use two rotated divs with a diagonal gradient, simulating exactly that:

.fc-handle div {
	position: absolute;
	height: 0px;
	width: 80px;
	top: 30px;
	content: '';
	opacity: 0.3;
}

.fc-handle-in div {
	background: 
		linear-gradient(
			75deg, 
			rgba(0,0,0,0) 0%,
			rgba(0,0,0,0) 73%,
			rgba(0,0,0,0.65) 100%
		);
}

.fc-handle-out div {
	background: 
		linear-gradient(
			170deg, 
			rgba(0,0,0,0.65) 0%,
			rgba(0,0,0,0) 27%,
			rgba(0,0,0,0) 100%
		);
}

The following pseudo-element will be used to create an inset shadow effect on the middle piece:

.fc-handle-out::after {
	position: absolute;
	right: 0px;
	width: 4px;
	height: 31px;
	content: '';
	background: 
		linear-gradient(
			to right, 
			rgba(0,0,0,0) 0%,
			rgba(0,0,0,0.15) 100%
		);
}

The right-most piece, i.e. the one that will trigger the opening of the circle booklet when clicked, will be positioned outside of the circular shape:

.fc-handle-pull {
	right: auto;
	left: 100%;
	margin-left: 5px;
	width: 30px;
	box-shadow: 
		1px 0 1px rgba(0,0,0,0.1), 
		inset 3px 0 2px rgba(0,0,0,0.2);
	cursor: pointer;
}

Let’s give it some depth with the help of a gradient:

.fc-handle-pull::after {
	content: '';
	position: absolute;
	top: 0px;
	right: 0px;
	width: 30px;
	height: 100%;
	background: 
		linear-gradient(
			to right, 
			rgba(0,0,0,0) 0%,
			rgba(0,0,0,0) 69%,
			rgba(0,0,0,0.08) 100%
		);
}

The bottom part that will contain the quote content will have the following style:

.fc-bottom {
	width: 220px;
	height: 100%;
	overflow: hidden;
	position: absolute;
	opacity: 0;
}

We are setting the opacity to 0 because we don’t want it to cause jagged edges of the circle when it’s closed. There will be an overlay darkening this part so will notice it if we don’t “hide” it. Note, that we are setting the overflow to hidden. This will make the division with the following class be cut off on the right:

.fc-bottom-bg {
	background: #846aa7 url(../images/paper.png) repeat center center;
	border-radius: 50%;
	width: 300px;
	height: 100%;
}

Now, let’s take a look at the overlays that will help us with a realistic look. Let’s first define a common style for all of them:

.fc-overlay {
	top: 0px;
	left: 0px;
	width: 300px;
	height: 100%;
	position: absolute;
	border-radius: 50%;	
}

The solid overlay will simply be a semi-transparent black division. This one will be used to make a part look dark initially (like the bottom content). When opening the circle, we will lower the opacity level in order to fade it out (the “open” states will be shown in the end):

.fc-overlay-solid {
	background: rgba(0,0,0,0.6);
}

The next overlay will do the opposite: it won’t be visible initially but then we will use it to darken things once we open the booklet:

.fc-overlay-reverse {
	background: rgba(0,0,0,0);
}

That overlay will be used for the right part of the circle that will get covered by the opening bit.

The next two overlays will have semi-transparent gradients that we will control using opacity:

.fc-overlay-left {
	background: linear-gradient(to right, transparent 27%, rgba(0,0,0,0.30) 80%);
	opacity: 0.5; 
}

.fc-overlay-right {
	background: linear-gradient(to left, rgba(0,0,0,0) 0%,rgba(0,0,0,0.30) 100%);
	opacity: 0.5;
}

The left overlay will be used on the front part and the right overlay on the back part. Both will transition to an opacity of 1 once the booklet opens.

The perspective container will, of course, have perspective, and we will make it a bit narrower. The idea is to cut off the circular divisions that we’ll have inside:

.fc-perspective {
	width: 220px;
	height: 300px;
	position: relative;
	perspective: 1000px;
}

The flip container will be rotated to open the whole thing, revealing the back part. To flip in the right place, we need to set the transform-origin to where the fold is, which is at 220 pixel:

.fc-flip {
	width: 100%;
	height: 100%;
	position: absolute;
	transform-origin: 220px 0px;
}

The back and the front division will have the following common style:

.fc-flip > div {
	display: block;
	height: 100%;
	width: 100%;
	margin: 0;
	overflow: hidden;
	position: absolute;
}

Their width will be 220px and since the overflow is set to hidden, the circular shapes of the inner divs will be cut off.

The back part will be rotated -180 degrees in 3D (on the Y-axis):

.fc-flip .fc-back {
	transform: rotate3d(0,1,0,-180deg);
}

Note, that we are not defining the necessary 3D properties here because we don’t want to repeat it for every class. We will gather all the respective class and define those properties later, at once.

The inner parts will have the circular shape and we’ll give them the paper texture:

.fc-flip .fc-inner {
	border-radius: 50%;
	width: 300px;
	height: 100%;
	position: absolute;
	background: #846aa7 url(../images/paper.png) repeat top right;
} 

.fc-flip .fc-back .fc-inner {
	margin-left: -80px;
	background-color: #553c77;
	box-shadow: inset 2px 0 3px rgba(0,0,0,0.1);
}

The back content will need to be “pulled” to the left so that the circle is cut off on the left side and not on the right like the one of the front part.

Now, let’s style the content elements:

.fc-content {
	width: 220px;
	padding: 20px;
	text-align: right;
	position: relative;
	height: 100%;
}

.fc-back .fc-content {
	margin-left: 80px;
}

.fc-bottom-bg .fc-content {
	padding-top: 40px;
}

.fc-content p {
	font-size: 12px;
	line-height: 22px;
	font-family: "Montserrat", sans-serif;
	text-shadow: 0 -1px 1px rgba(255,255,255,0.1);
	color: #3b2954;
	padding: 0 0 0 31px;
}

.fc-flip .fc-front h3,
.fc-flip .fc-front p {
	position: absolute;
	text-align: right;
	width: 180px;
	text-shadow: 0 -1px 1px rgba(255,255,255,0.1);
	color: #3b2954;
}

.fc-flip .fc-front h3,
.feynman span {
	font-family: "Montserrat", sans-serif;
	text-transform: uppercase;
	font-size: 17px;
	letter-spacing: 1px;
	font-weight: normal;
}

.fc-flip .fc-front h3 {
	top: 30px;
	left: 15px;
}

.feynman {
	width: 255px;
	height: 255px;
	position: absolute;
	overflow: hidden;
	top: 50%;
	left: -55px;
	border-radius: 50%;
	box-shadow: 2px 0 3px rgba(0,0,0,0.3);
	margin-top: -127px;
	background: transparent url(../images/feynman.png) no-repeat center right;
}

.feynman span {
	text-align: center;
	width: 100px;
	height: 5px;
	line-height: 30px;
	color: #fff;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.2);
	bottom: 40px;
	right: 80px;
	font-size: 13px;
	position: absolute;
}

.fc-flip .fc-front h3 span{
	font-size: 40px;
}

.fc-flip .fc-front p,
.fc-bottom-bg .fc-content span {
	bottom: 50px;
	left: 15px;
	font-family: "Dancing Script", Georgia, serif;
	font-weight: 700;
	font-size: 22px;
}

.fc-bottom-bg .fc-content span {
	font-size: 18px;
	display: block;
	color: #fff;
	padding: 10px;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.2);
	transform: rotate(-3deg);
}

Let’s gather all the classes that will have a transition:

.fc-flip .fc-back .fc-inner,
.fc-overlay,
.fc-handle,
.fc-handle div,
.fc-flip,
.fc-bottom{
	transition: all 0.6s ease-in-out;
}

.fc-bottom{
	transition-delay: 0.6s;
}

The next properties are important for making the 3D rotation work properly:


.fc-flip,
.fc-flip .fc-inner,
.fc-handle {
	transform-style: preserve-3d;
}

.fc-flip > div,
.fc-flip .fc-inner,
.fc-flip .fc-front h3,
.fc-handle,
.fc-handle div,
.fc-overlay,
.fc-flip .fc-front p,
.fc-flip .fc-front span {
	backface-visibility: hidden;
}

When we open the circular booklet we will apply the class fc-wrapper-open to the main wrapper. Let’s define the “open” states for all the elements.

The left-most piece of the handle will shrink to a width of 0. If you watch the paper effect in the video, you will notice that it gets pulled to the right, though the slit:

.fc-wrapper.fc-wrapper-open .fc-handle-in {
	width: 0px;
}

The diagonal gradients that will serve as the shadow for the two handle pieces will become taller:

.fc-wrapper.fc-wrapper-open .fc-handle-in div {
	height: 180px;
}

.fc-wrapper.fc-wrapper-open .fc-handle-out div {
	height: 100px;
}

The background color of the two first handle pieces will become darker:

.fc-wrapper.fc-wrapper-open .fc-handle {
	background-color: #513a70;
}

The right-most handle will get wider because we are simulating the pulling of the whole handle:

.fc-wrapper.fc-wrapper-open .fc-handle-pull {
	width: 155px;
	background: #775b9d;	
}

The background color of the back part content will become lighter:

.fc-wrapper.fc-wrapper-open  .fc-flip .fc-back .fc-inner { 
	background-color: #846aa7;
}

The overlays will increase in opacity:

.fc-wrapper.fc-wrapper-open .fc-overlay {
	opacity: 1;
}

The solid overlay will fade out:

.fc-wrapper.fc-wrapper-open .fc-overlay-solid {
	background: rgba(0,0,0,0);
}

And the reverse overlay will become darker:

.fc-wrapper.fc-wrapper-open .fc-overlay-reverse {
	background: rgba(0,0,0,0.4);
}

The bottom part will become visible immediately (we remove the transition here, but when it closes it will fade):

.fc-wrapper.fc-wrapper-open .fc-bottom{
	opacity: 1;
	transition: none;
	
}

And of course, the most important thing that is going to happen: the rotation of the flip container:

.fc-wrapper.fc-wrapper-open .fc-flip {
	transform: rotate3d(0,1,0,175deg);
}

And that’s all the style. Now, let’s take a look at the few lines of JavaScript that will help us apply the class and add some swipe support for mobile.

The JavaScript

When we click on he right handle piece, we will apply the class fc-wrapper-open to the main container. We’ll define two simple functions for opening and closing. We’ll also add swipe support using Hammer.js and its jQuery plugin:

$(function() {
			
	var $wrapper= $( '#fc-wrapper' ),
		$handle	= $wrapper.children( 'div.fc-handle-pull' );
	
	$handle.on( 'click', function( event ) {
	
		( $handle.data( 'opened' ) ) ? close() : open();
	
	} );
	
	$wrapper.hammer().bind( 'dragend', function( event ) {
		switch( event.direction ) {
			case 'right'	: open(); break;
			case 'left'		: close(); break;
		}
	});
	
	function open() {
		$wrapper.addClass( 'fc-wrapper-open' );
		$handle.data( 'opened', true );
	}
	
	function close() {
		$wrapper.removeClass( 'fc-wrapper-open' );
		$handle.data( 'opened', false );
	}

} );

That’s all, hope you enjoy it and find it inspiring!

View demo Download source


Circle Hover Effects with CSS Transitions

$
0
0


Circle Hover Effects with CSS Transitions

View demo Download source

In today’s tutorial we’ll experiment with hover effects on circles. Since we have the border radius property, we can create circular shapes and they have been appearing more often as design elements in websites. One use that I especially enjoy seeing is the circular thumbnail which just looks so much more interesting than the usual rectangular. And because the circle is such a special shape, we are going to create some special hover effects for it!

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

We will omit vendor prefixes in this tutorial. But you’ll of course find them in the files.

So, let’s get started!

The HTML

For most of the examples, we’ll be using the following structure:

<ul class="ch-grid">
	<li>
		<div class="ch-item ch-img-1">
			<div class="ch-info">
				<h3>Use what you have</h3>
				<p>by Angela Duncan <a href="http://drbl.in/eOPF">View on Dribbble</a></p>
			</div>
		</div>
	</li>
	<li>
		<div class="ch-item ch-img-2">
			<div class="ch-info">
				<h3>Common Causes of Stains</h3>
				<p>by Antonio F. Mondragon <a href="http://drbl.in/eKMi">View on Dribbble</a></p>
			</div>
		</div>
	</li>
	<li>
		<div class="ch-item ch-img-3">
			<div class="ch-info">
				<h3>Pink Lightning</h3>
				<p>by Charlie Wagers <a href="http://drbl.in/ekhp">View on Dribbble</a></p>
			</div>
		</div>
	</li>
</ul

Although we could use images here, we’ll give ourselves a bit more freedom by using background images instead. We’ll define them in the classes starting with “ch-img-”. Additionally, we’ll have a division for the description of the item with a title.

Now, let’s make some hover effects!

The CSS

Let’s define some common style for the list and the list items:

.ch-grid {
	margin: 20px 0 0 0;
	padding: 0;
	list-style: none;
	display: block;
	text-align: center;
	width: 100%;
}

.ch-grid:after,
.ch-item:before {
	content: '';
    display: table;
}

.ch-grid:after {
	clear: both;
}

.ch-grid li {
	width: 220px;
	height: 220px;
	display: inline-block;
	margin: 20px;
}

We can center the list items by using display inline-block and setting the text-align property of its parent to center.
The clearfix hack is by Nicolas Gallagher: http://nicolasgallagher.com/micro-clearfix-hack/

Some of the examples will have a different structure but we’ll look into that for each example in more detail.

Example 1

CircleHoverEffects_01

The first example is going to reveal the description by scaling it up and we will also animate the inset box shadow of the item itself. So let’s position the item and set a nice, bif inset box shadow and a transition:

.ch-item {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	overflow: hidden;
	position: relative;
	cursor: default;
	box-shadow: 
		inset 0 0 0 16px rgba(255,255,255,0.6),
		0 1px 2px rgba(0,0,0,0.1);
	transition: all 0.4s ease-in-out;
}

As you notices before, we’ve given two classes to the item (not the list item but its child division): one is ct-item and the other one will be used to define a specific background image:

.ch-img-1 { 
	background-image: url(../images/1.jpg);
}

.ch-img-2 { 
	background-image: url(../images/2.jpg);
}

.ch-img-3 { 
	background-image: url(../images/3.jpg);
}

The description element will be positioned absolutely and we’ll give it a semi-transparent background by setting an RGBA value. It’s opacity is going to be 0 and we’ll scale it down to 0, too:

.ch-info {
	position: absolute;
	background: rgba(63,147,147, 0.8);
	width: inherit;
	height: inherit;
	border-radius: 50%;
	overflow: hidden;
	opacity: 0;
	transition: all 0.4s ease-in-out;
	transform: scale(0);
}

The title of the item will have some fitting paddings and margins and a smooth text shadow:

.ch-info h3 {
	color: #fff;
	text-transform: uppercase;
	letter-spacing: 2px;
	font-size: 22px;
	margin: 0 30px;
	padding: 45px 0 0 0;
	height: 140px;
	font-family: 'Open Sans', Arial, sans-serif;
	text-shadow: 
		0 0 1px #fff, 
		0 1px 2px rgba(0,0,0,0.3);
}

The paragraph element has 0 opacity and a transition (we want to fade it in on hover but with a delay):

.ch-info p {
	color: #fff;
	padding: 10px 5px;
	font-style: italic;
	margin: 0 30px;
	font-size: 12px;
	border-top: 1px solid rgba(255,255,255,0.5);
	opacity: 0;
	transition: all 1s ease-in-out 0.4s;
}

The link will be in uppercase letters and we’ll make the hover color yellow:

.ch-info p a {
	display: block;
	color: rgba(255,255,255,0.7);
	font-style: normal;
	font-weight: 700;
	text-transform: uppercase;
	font-size: 9px;
	letter-spacing: 1px;
	padding-top: 4px;
	font-family: 'Open Sans', Arial, sans-serif;
}

.ch-info p a:hover {
	color: rgba(255,242,34, 0.8);
}

And now, the interesting hover action!
The item will animate its box shadow’s spread radius from 16px to 1px:

.ch-item:hover {
	box-shadow: 
		inset 0 0 0 1px rgba(255,255,255,0.1),
		0 1px 2px rgba(0,0,0,0.1);
}

The description will fade in and scale up to 1:

.ch-item:hover .ch-info {
	transform: scale(1);
	opacity: 1;
}

And the paragraph of the description will just fade in (with a delay):

.ch-item:hover .ch-info p {
	opacity: 1;
}

And that’s the first example! Let’s take a look at the next one.

Example 2

CircleHoverEffects_02

The HTML structure in this example is the same as the first one.

In this example we will use the box shadow of the item to fill our circle and serve as the background of the description.
So, nothing special here, just the box shadow that has one more value line:

.ch-item {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	position: relative;
	cursor: default;
	box-shadow: 
		inset 0 0 0 0 rgba(200,95,66, 0.4),
		inset 0 0 0 16px rgba(255,255,255,0.6),
		0 1px 2px rgba(0,0,0,0.1);
	transition: all 0.4s ease-in-out;
}

And the background-images:

.ch-img-1 { 
	background-image: url(../images/4.jpg);
}

.ch-img-2 { 
	background-image: url(../images/5.jpg);
}

.ch-img-3 { 
	background-image: url(../images/6.jpg);
}

The description will be scaled down again:

.ch-info {
	position: absolute;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	overflow: hidden;
	opacity: 0;
	transition: all 0.4s ease-in-out;
	transform: scale(0);
	backface-visibility: hidden;
}

And let’s style the typographical elements:

.ch-info h3 {
	color: #fff;
	text-transform: uppercase;
	position: relative;
	letter-spacing: 2px;
	font-size: 22px;
	margin: 0 30px;
	padding: 65px 0 0 0;
	height: 110px;
	font-family: 'Open Sans', Arial, sans-serif;
	text-shadow: 
		0 0 1px #fff, 
		0 1px 2px rgba(0,0,0,0.3);
}

.ch-info p {
	color: #fff;
	padding: 10px 5px;
	font-style: italic;
	margin: 0 30px;
	font-size: 12px;
	border-top: 1px solid rgba(255,255,255,0.5);
}

.ch-info p a {
	display: block;
	color: rgba(255,255,255,0.7);
	font-style: normal;
	font-weight: 700;
	text-transform: uppercase;
	font-size: 9px;
	letter-spacing: 1px;
	padding-top: 4px;
	font-family: 'Open Sans', Arial, sans-serif;
}

.ch-info p a:hover {
	color: rgba(255,242,34, 0.8);
}

On hover we’ll animate the inset box shadow (the reddish one) to 110px spread radius. This will cover all the circle:

.ch-item:hover {
	box-shadow: 
		inset 0 0 0 110px rgba(200,95,66, 0.4),
		inset 0 0 0 16px rgba(255,255,255,0.8),
		0 1px 2px rgba(0,0,0,0.1);
}

And we’ll scale up the description and fade it in:

.ch-item:hover .ch-info {
	opacity: 1;
	transform: scale(1);	
}

Example 3

CircleHoverEffects_03

In this example, we’ll play with rotation. The structure will be a bit different from the first two examples since we need to add the thumbnail as a second divison. So the list item will look as follows:

<li>
	<div class="ch-item">	
		<div class="ch-info">
			<h3>Music poster</h3>
			<p>by Jonathan Quintin <a href="http://drbl.in/eGjw">View on Dribbble</a></p>
		</div>
		<div class="ch-thumb ch-img-1"></div>
	</div>
</li>

The item division will be styled like before (with a subtle box shadow):

.ch-item {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	position: relative;
	cursor: default;
	box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}

The thumbnail element will have a specific transform-origin (somewhere to the right middle) and a transition. This will be the element that we want to rotate down on hover so that it reveals the description element:

.ch-thumb {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	overflow: hidden;
	position: absolute;
	box-shadow: inset 0 0 0 15px rgba(255,255,255, 0.5);
	transform-origin: 95% 40%;
	transition: all 0.3s ease-in-out;
}

Using the pseudo-class :after we’ll create a little brass fastener with a radial gradient:

.ch-thumb:after {
	content: '';
	width: 8px;
	height: 8px;
	position: absolute;
	border-radius: 50%;
	top: 40%;
	left: 95%;
	margin: -4px 0 0 -4px;
	background: radial-gradient(ellipse at center, rgba(14,14,14,1) 0%,rgba(125,126,125,1) 100%);
	box-shadow: 0 0 1px rgba(255,255,255,0.9);
}

Let’s define the background images for each thumb element:

.ch-img-1 { 
	background-image: url(../images/7.jpg);
	z-index: 12;
}

.ch-img-2 { 
	background-image: url(../images/8.jpg);
	z-index: 11;
}

.ch-img-3 { 
	background-image: url(../images/9.jpg);
	z-index: 10;
}

The desciption element will be styled as follows:

.ch-info {
	position: absolute;
	width: inherit;
	height: inherit;
	border-radius: 50%;
	overflow: hidden;
	background: #c9512e url(../images/noise.png);
	box-shadow: inset 0 0 0 5px rgba(0,0,0,0.05);
}

The typographical elements will be positioned and styled the following way:

.ch-info h3 {
	color: #fff;
	text-transform: uppercase;
	position: relative;
	letter-spacing: 2px;
	font-size: 18px;
	margin: 0 60px;
	padding: 22px 0 0 0;
	height: 85px;
	font-family: 'Open Sans', Arial, sans-serif;
	text-shadow: 
		0 0 1px #fff, 
		0 1px 2px rgba(0,0,0,0.3);
}

.ch-info p {
	color: #fff;
	padding: 10px 5px;
	font-style: italic;
	margin: 0 30px;
	font-size: 12px;
	border-top: 1px solid rgba(255,255,255,0.5);
}

The anchor will be a little circle that should move in from the right on hover:

.ch-info p a {
	display: block;
	color: #333;
	width: 80px;
	height: 80px;
	background: rgba(255,255,255,0.3);
	border-radius: 50%;
	color: #fff;
	font-style: normal;
	font-weight: 700;
	text-transform: uppercase;
	font-size: 9px;
	letter-spacing: 1px;
	padding-top: 24px;
	margin: 7px auto 0;
	font-family: 'Open Sans', Arial, sans-serif;
	opacity: 0;
	transition: 
		transform 0.3s ease-in-out 0.2s,
		opacity 0.3s ease-in-out 0.2s,
		background 0.2s linear 0s;
	transform: translateX(60px) rotate(90deg);
}

.ch-info p a:hover {
	background: rgba(255,255,255,0.5);
}

Since we want the movement and opacity to happen with a delay, but the background hover transition without, we’ll separate the transitions.

On hover we’ll rotate the thumb and move/rotate the link element:

.ch-item:hover .ch-thumb {
	box-shadow: inset 0 0 0 15px rgba(255,255,255, 0.5), 0 1px 3px rgba(0,0,0,0.2);
	transform: rotate(-110deg);
}
.ch-item:hover .ch-info p a{
	opacity: 1;
	transform: translateX(0px) rotate(0deg);
}

Example 4

CircleHoverEffects_04

The forth example will involve some 3D rotations. So, we need to adjust the structured in order to have a container for the perspective and a front and backface. So, the list items will look as follows:

<li>
	<div class="ch-item ch-img-1">				
		<div class="ch-info-wrap">
			<div class="ch-info">
				<div class="ch-info-front ch-img-1"></div>
				<div class="ch-info-back">
					<h3>Bears Type</h3>
					<p>by Josh Schott <a href="http://drbl.in/ewUW">View on Dribbble</a></p>
				</div>	
			</div>
		</div>
	</div>
</li>

As you can see, we’ll add the background image to the item division and also to the front part of the flipping division.
The trick is to give the same background to the ch-info-wrap like the body. This will give the illusion as if our item has a hole.

The item will have the usual styling:

.ch-item {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	position: relative;
	box-shadow: 0 1px 2px rgba(0,0,0,0.1);
	cursor: default;
}

The extra wrapper will be used for the perspective and we’ll also add a transition for the box shadow:

.ch-info-wrap{
	position: absolute;
	width: 180px;
	height: 180px;
	border-radius: 50%;
	perspective: 800px;
	transition: all 0.4s ease-in-out;
	top: 20px;
	left: 20px;
	background: #f9f9f9 url(../images/bg.jpg);
	box-shadow: 
		0 0 0 20px rgba(255,255,255,0.2), 
		inset 0 0 3px rgba(115,114, 23, 0.8);

}

The ch-info div will need preserve-3d for the transform-style and we’ll give it a transition since this is the element we’ll rotate in 3d:

.ch-info{
	position: absolute;
	width: 180px;
	height: 180px;
	border-radius: 50%;
	transition: all 0.4s ease-in-out;
	transform-style: preserve-3d;
}

The front and the backface will have the following common styles:

.ch-info > div {
	display: block;
	position: absolute;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	background-position: center center;
	backface-visibility: hidden;
}

The backface will be rotated so that we won’t see it initially:

.ch-info .ch-info-back {
	transform: rotate3d(0,1,0,180deg);
	background: #000;
}

And again, the background images:

.ch-img-1 { 
	background-image: url(../images/10.jpg);
}

.ch-img-2 { 
	background-image: url(../images/11.jpg);
}

.ch-img-3 { 
	background-image: url(../images/12.jpg);
}

…and the typographical elements:

.ch-info h3 {
	color: #fff;
	text-transform: uppercase;
	letter-spacing: 2px;
	font-size: 14px;
	margin: 0 15px;
	padding: 40px 0 0 0;
	height: 90px;
	font-family: 'Open Sans', Arial, sans-serif;
	text-shadow: 
		0 0 1px #fff, 
		0 1px 2px rgba(0,0,0,0.3);
}

.ch-info p {
	color: #fff;
	padding: 10px 5px;
	font-style: italic;
	margin: 0 30px;
	font-size: 12px;
	border-top: 1px solid rgba(255,255,255,0.5);
}

.ch-info p a {
	display: block;
	color: rgba(255,255,255,0.7);
	font-style: normal;
	font-weight: 700;
	text-transform: uppercase;
	font-size: 9px;
	letter-spacing: 1px;
	padding-top: 4px;
	font-family: 'Open Sans', Arial, sans-serif;
}

.ch-info p a:hover {
	color: rgba(255,242,34, 0.8);
}

On hover, we’ll change the box shadow of the wrapper and rotate the parent of the back and frontface so that we see the back:

.ch-item:hover .ch-info-wrap {
	box-shadow: 
		0 0 0 0 rgba(255,255,255,0.8), 
		inset 0 0 3px rgba(115,114, 23, 0.8);
}

.ch-item:hover .ch-info {
	transform: rotate3d(0,1,0,-180deg);
}

Example 5

CircleHoverEffects_05

In this example we want to scale down the inner thumbnail part to 0 and make the description element appear by fading it in and scaling it down to 1.
The structure of the fifth example will be the same as in the previous example.

The item has the usual style:

.ch-item {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	position: relative;
	box-shadow: 0 1px 2px rgba(0,0,0,0.1);
	cursor: default;
}

The wrapper of the description div and the info div itself will have the following common style:

.ch-info-wrap, 
.ch-info{
	position: absolute;
	width: 180px;
	height: 180px;
	border-radius: 50%;
}

Let’s do again the “hole” trick by setting the same background of the body to the wrapper:

.ch-info-wrap {
	top: 20px;
	left: 20px;
	background: #f9f9f9 url(../images/bg.jpg);
	box-shadow: 
		0 0 0 20px rgba(255,255,255,0.2), 
		inset 0 0 3px rgba(115,114, 23, 0.8);

}

The “front” and the “back” (it’s not really a front and backface anymore) common style:

.ch-info > div {
	display: block;
	position: absolute;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	background-position: center center;
}

The “front” will have a transition (it will scale down and disappear):

.ch-info .ch-info-front {
	transition: all 0.6s ease-in-out;
}

And the back that holds the description will have 0 opacity initially and be scaled up to 1.5:

.ch-info .ch-info-back {
	opacity: 0;
	background: #223e87;
	pointer-events: none;
	transform: scale(1.5);
	transition: all 0.4s ease-in-out 0.2s;
}

We need to set the pointer-events to none since we don’t want the element to “block” everything else…remeber, it’s scaled up, we just can’t see it because of it’s opacity, but it’s still there.

Background images and typographical elements as usual, just with some different colors:

.ch-img-1 { 
	background-image: url(../images/13.jpg);
}

.ch-img-2 { 
	background-image: url(../images/14.jpg);
}

.ch-img-3 { 
	background-image: url(../images/15.jpg);
}

.ch-info h3 {
	color: #fff;
	text-transform: uppercase;
	letter-spacing: 2px;
	font-size: 18px;
	margin: 0 15px;
	padding: 40px 0 0 0;
	height: 80px;
	font-family: 'Open Sans', Arial, sans-serif;
	text-shadow: 
		0 0 1px #fff, 
		0 1px 2px rgba(0,0,0,0.3);
}

.ch-info p {
	color: #fff;
	padding: 10px 5px 0;
	font-style: italic;
	margin: 0 30px;
	font-size: 12px;
	border-top: 1px solid rgba(255,255,255,0.5);
}

.ch-info p a {
	display: block;
	color: #e7615e;
	font-style: normal;
	font-weight: 700;
	text-transform: uppercase;
	font-size: 9px;
	letter-spacing: 1px;
	padding-top: 4px;
	font-family: 'Open Sans', Arial, sans-serif;
}

.ch-info p a:hover {
	color: #fff;
}

On hover we’ll scale down the inner thumbnail part to 0 and set the opacity to 0. This will make it disappear into the back.

.ch-item:hover .ch-info-front {
	transform: scale(0);
	opacity: 0;
} 

The part that contains the description will be scaled down to 1 and faded in. We’ll also set the pointer event to whatever they were before because now we want to be able to click on the link:

.ch-item:hover .ch-info-back {
	transform: scale(1);
	opacity: 1;
	pointer-events: auto;
}

Example 6

CircleHoverEffects_06

In this example we want to flip the inner thumbnail part down in order to reveal the description. The HTML will be the same like in the previous two examples.

The item will be styled as before:

.ch-item {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	position: relative;
	box-shadow: 0 1px 2px rgba(0,0,0,0.1);
	cursor: default;
}

The common style of the wrapper and the description element:

.ch-info-wrap, 
.ch-info{
	position: absolute;
	width: 180px;
	height: 180px;
	border-radius: 50%;
	transition: all 0.4s ease-in-out;
}

The wrapper will have perspective:

.ch-info-wrap {
	top: 20px;
	left: 20px;
	background: #f9f9f9 url(../images/bg.jpg);
	box-shadow: 
		0 0 0 20px rgba(255,255,255,0.2), 
		inset 0 0 3px rgba(115,114, 23, 0.8);
	perspective: 800px;
}

The info element will need the following transform style:

.ch-info {
	transform-style: preserve-3d;
}

The front and the backface will have a transition. Note that this time we’ll not set the backface-visibility to hidden, since we want the back of the inner thumbnail part to show when we flip it down:

.ch-info > div {
	display: block;
	position: absolute;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	background-position: center center;
	transition: all 0.6s ease-in-out;
}

Let’s set the correct transform-origin so that we can open it down:

.ch-info .ch-info-front {
	transform-origin: 50% 100%;	
	z-index: 100;
	box-shadow: 
		inset 2px 1px 4px rgba(0,0,0,0.1);
}

We’ll set an RGBA value with 0 opacity to the background of the description part:

.ch-info .ch-info-back {
	background: rgba(230,132,107,0);
}

And the usual style for the other elements:

.ch-img-1 { 
	background-image: url(../images/16.jpg);
}

.ch-img-2 { 
	background-image: url(../images/17.jpg);
}

.ch-img-3 { 
	background-image: url(../images/18.jpg);
}

.ch-info h3 {
	color: #fff;
	text-transform: uppercase;
	letter-spacing: 2px;
	font-size: 14px;
	margin: 0 25px;
	padding: 40px 0 0 0;
	height: 90px;
	font-family: 'Open Sans', Arial, sans-serif;
	text-shadow: 
		0 0 1px #fff, 
		0 1px 2px rgba(0,0,0,0.3);
}

.ch-info p {
	color: #fff;
	padding: 10px 5px;
	font-style: italic;
	margin: 0 30px;
	font-size: 12px;
	border-top: 1px solid rgba(255,255,255,0.5);
}

.ch-info p a {
	display: block;
	color: rgba(255,255,255,0.7);
	font-style: normal;
	font-weight: 700;
	text-transform: uppercase;
	font-size: 9px;
	letter-spacing: 1px;
	padding-top: 4px;
	font-family: 'Open Sans', Arial, sans-serif;
}

.ch-info p a:hover {
	color: rgba(255,242,34, 0.8);
}

On hover, we’ll rotate the front part and animate the box shadow slightly. The back part will fade in its background color:

.ch-item:hover .ch-info-front {
	transform: rotate3d(1,0,0,-180deg);
	box-shadow: 
		inset 0 0 5px rgba(255,255,255,0.2), 
		inset 0 0 3px rgba(0,0,0,0.3);
}

.ch-item:hover .ch-info-back {
	background: rgba(230,132,107,0.6);
}

Example 7

CircleHoverEffects_07

The last example will act like a rotating cube where we reveal the description by rotating it in from the top back. Since we will rotate each of the faces, we won’t need an extra wrapper. So, our HTML will look as follows:

<li>
	<div class="ch-item">				
		<div class="ch-info">
			<div class="ch-info-front ch-img-1"></div>
			<div class="ch-info-back">
				<h3>Mouse</h3>
				<p>by Alexander Shumihin <a href="http://drbl.in/eAoj">View on Dribbble</a></p>
			</div>	
		</div>
	</div>
</li>

We’ll give a perspective value to the item itself:

.ch-item {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	position: relative;
	cursor: default;
	perspective: 900px;
}

The element with the class ch-info will need the preserve-3d:

.ch-info{
	position: absolute;
	width: 100%;
	height: 100%;
	transform-style: preserve-3d;
}

The front and backface will have a transition and the transform origin will be set to 50% 0%:

.ch-info > div {
	display: block;
	position: absolute;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	background-position: center center;
	transition: all 0.4s linear;
	transform-origin: 50% 0%;
}

Let’s set a nice inset box shadow to the front part:

.ch-info .ch-info-front {
	box-shadow: inset 0 0 0 16px rgba(0,0,0,0.3);
}

The backface will be rotated initially in order to appear as the down face of a cube:

.ch-info .ch-info-back {
	transform: translate3d(0,0,-220px) rotate3d(1,0,0,90deg);
	background: #000;
	opacity: 0;
}

And the usual style for the background images and text elements:

.ch-img-1 { 
	background-image: url(../images/19.jpg);
}

.ch-img-2 { 
	background-image: url(../images/20.jpg);
}

.ch-img-3 { 
	background-image: url(../images/21.jpg);
}

.ch-info h3 {
	color: #fff;
	text-transform: uppercase;
	letter-spacing: 2px;
	font-size: 24px;
	margin: 0 15px;
	padding: 60px 0 0 0;
	height: 110px;
	font-family: 'Open Sans', Arial, sans-serif;
	text-shadow: 
		0 0 1px #fff, 
		0 1px 2px rgba(0,0,0,0.3);
}

.ch-info p {
	color: #fff;
	padding: 10px 5px;
	font-style: italic;
	margin: 0 30px;
	font-size: 12px;
	border-top: 1px solid rgba(255,255,255,0.5);
}

.ch-info p a {
	display: block;
	color: rgba(255,255,255,0.7);
	font-style: normal;
	font-weight: 700;
	text-transform: uppercase;
	font-size: 9px;
	letter-spacing: 1px;
	padding-top: 4px;
	font-family: 'Open Sans', Arial, sans-serif;
}

.ch-info p a:hover {
	color: rgba(255,242,34, 0.8);
}

We’ll use translate3d to move the front part on the Y axis of our 3d space and rotate3d to actually rotate it. We’ll also fade it out because we don’t want to see any part of it afterwards:

.ch-item:hover .ch-info-front {
	transform: translate3d(0,280px,0) rotate3d(1,0,0,-90deg);
	opacity: 0;
}

The backface will be rotated “back” to 0 degrees (remember, initially it was rotated downwards):

.ch-item:hover .ch-info-back {
	transform: rotate3d(1,0,0,0deg);
	opacity: 1;
}

And that’s it! A whole bunch of hover effects that allow for many different variations, just try it out and play with it!

Hope you enjoyed these effects and find them inspiring!

View demo Download source

Credits: Featured image illustration from Arnel Baluyot’s Stay Foxy.

Triple Panel Image Slider

$
0
0

Triple Panel Image Slider

View demo Download source

In this tutorial we will create a jQuery triple panel image slider with a 3D look. The idea is to have a main panel and two lateral panels that are rotated slightly in 3D space. When navigating, we will slide in the respective next image in each panel. We’ll be using CSS 3D Transforms with perspective and CSS Transitions.

The beautiful food images in the demo are by geishaboy500 and they are licensed under Creative Commons Attribution 2.0 Generic (CC BY 2.0).

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

We will omit vendor prefixes in this tutorial. But you’ll of course find them in the files.

Let’s get started!

The Markup

The initial structure that we’ll create will consist of a division with figures. Each figure will contain an image and a figcaption with the title and description for the image:

<div class="fs-slider" id="fs-slider">

	<figure>
		<img src="images/1.jpg" alt="image01" />
		<figcaption>
			<h3>Eloquence</h3>
			<p>American apparel flexitarian put a bird on it, mixtape typewriter irony aesthetic. </p>
		</figcaption>
	</figure>

	<figure>
		<img src="images/2.jpg" alt="image02" />
		<figcaption>
			<h3>Quintessential</h3>
			<p>Cardigan craft beer mixtape, skateboard forage fixie truffaut messenger bag. </p>
		</figcaption>
	</figure>

	<!-- ... -->

</div>

We will want our jQuery plugin to transform that structure into the following one:

<section class="fs-container">

	<div class="fs-wrapper">

		<div class="fs-slider" id="fs-slider">

			<div class="fs-block">

				<figure style="display: block; ">
					<img src="images/1.jpg" alt="image01" />
					<figcaption>
						<h3>Eloquence</h3>
						<p>American apparel flexitarian put a bird on it, mixtape typewriter irony aesthetic. </p>
					</figcaption>
				</figure>

			</div><!-- /fs-block -->

			<div class="fs-block">
				<!-- ... -->
			</div>

			<!-- ... -->

		</div><!-- /fs-slider -->

		<nav class="fs-navigation">
			<span>Previous</span>
			<span>Next</span>
		</nav>

	</div><!-- /fs-wrapper -->

</section><!-- /fs-container -->

Each figure will be wrapped into a division with the class fs-block and we’ll add a navigation to the whole thing.

Let’s look at the style.

The CSS

Since we want the image slider to be responsive, we will give it a percentage width. But we’ll also define a minimal and maximum width so that it doesn’t squeeze too much or grow out of proportion. We will add some paddings to the side because our blocks will be positioned using the CSS translate property (they will all be in the middle) and that will not affect the width of the element:

.fs-container {
	margin: 20px auto 50px auto;
	position: relative;
	width: 40%;
	padding: 0 15%;
	max-width: 700px;
	min-width: 220px;
	height: 500px;	
	box-sizing: content-box;
}

Let’s add a realistic shadow under the slider using a pseudo-element. We’ll use a background image for that and setting a background size of 100% will ensure that the shadow resizes with our slider:

.fs-container:before {
	content: '';
	position: absolute;
	bottom: -40px;
	background: transparent url(../images/shadow.png) no-repeat center center;
	height: 90px;
	width: 90%;
	left: 5%;
	opacity: 0.8;
	background-size: 100% 100%;
}

The additional wrapper that we’ve added around the slider will have perspective:

.fs-wrapper {
	width: 100%;
	height: 100%;
	position: relative;
	perspective: 1000px;
}

The slider itself will need to have the preserve 3d transform-style:

.fs-slider{
	width: 100%;
	height: 100%;
	position: absolute;
	transform-style: preserve-3d;
	pointer-events: none;
}

Each block will be placed in the center by setting the width to 70% and the left value to 15%. We’ll also add a transition to the block since we want to create a neat hover effect:

.fs-block {
	margin: 0;
	position: absolute;
	width: 70%;
	height: 100%;
	left: 15%;
	pointer-events: auto;
	transition: all 1s ease;
}

Now we need to position the blocks. The first one will be moved to the left by setting the translateX value to -100%. By rotating it -35 degrees on the Y-axis, we’ll make it turn away to the back on the left side:

.fs-block:nth-child(1) {	
	transform-origin: top right;
	transform: translateX(-100%) rotateY(-35deg);
}

On hover, we will want that panel block to move a bit to the front. We are using Modernizr, so we can define the hover effect only for non-touch devices (the body will have the class “no-touch”):

.no-touch .fs-block:nth-child(1):hover {
	transform: translateX(-100%) rotateY(-30deg);
}

The middle panel will have a z-index of 100 because we want to ensure that it’s always on top:

.fs-block:nth-child(2) {
	z-index: 100;
}

The last block will be moved right and rotated to the other side:

.fs-block:nth-child(3) {
	transform-origin: top left;
	transform: translateX(100%) rotateY(35deg);
}

And on hover we’ll slightly move it to the front:

.no-touch .fs-block:nth-child(3):hover {
	transform: translateX(100%) rotateY(30deg);
}

Let’s add some semi-transparent overlays to the panels in order to make them look more realistic. We’ll use the pseudo-class :after for that and we0ll make it overflow 1px (to fix a little gap that might show up because of rounding errors of the width):

.fs-block:after{
	content: '';
	position: absolute;
	width: 100%;
	height: 100%;
	z-index: 1000;
	pointer-events: none;
	box-sizing: content-box;
	border-left: 1px solid rgba(119,119,119,1);
	border-right: 1px solid rgba(119,119,119,1);
	left: -1px;
}

Each block will have a different type of gradient:

.fs-block:nth-child(1):after {
	background: 
		linear-gradient(
			to right, 
			rgba(0,0,0,0.65) 0%,
			rgba(0,0,0,0.2) 100%
		);
}

The following gradient for the middle panel will add a slightly bended effect:

.fs-block:nth-child(2):after {
	opacity: 0.8;
	background: 
		linear-gradient(
			to right, 
			rgba(0,0,0,0.5) 0%,
			rgba(0,0,0,0.12) 21%,
			rgba(0,0,0,0.03) 31%,
			rgba(0,0,0,0) 50%,
			rgba(0,0,0,0.03) 70%,
			rgba(0,0,0,0.12) 81%,
			rgba(0,0,0,0.5) 100%
		);
}

The last block will have the reverse gradient of the first one:

.fs-block:nth-child(3):after {
	background: 
		linear-gradient(
			to right, 
			rgba(0,0,0,0.2) 0%,
			rgba(0,0,0,0.65) 100%
		);
}

Now, let’s style the figure elements. They will be positioned absolutely and fill all the block element:

.fs-block figure {
	width: 100%;
	height: 100%;
	margin: 0;
	position: absolute;
	top: 0;
	left: 0;
	overflow: hidden;
	z-index: 1;
}

The idea is to add another figure to a block when we navigate. So we need to set the z-index of the first figure higher so that the added figure does not show up. Then we will animate the width of the first figure to 0%, which will reveal the second one:

.fs-block figure:first-child{
	z-index: 10;
}

The image will also be set to position absolute:

.fs-block figure img {
	position: absolute;
	top: 0;
	left: 0;
	display: block;
}

The figcation element will have a semi-transparent background and we will add a transition. This transition will happen when we add the class fs-transition to it (we’ll do that when we navigate):

.fs-block figcaption {
	padding: 0 20px;
	margin: 0;
	position: absolute;
	width: 100%;
	top: 25%;
	background: rgba(0,0,0,0.4);
	overflow: hidden;
	height: 0%;
	opacity: 0;
	text-align: center;
	transition: all 700ms cubic-bezier(0, 0, .15, 1);
}

.fs-block figcaption.fs-transition {
	height: 35%;
	opacity: 1;
}

Let’s style the text elements:

.fs-block figcaption h3 {
	font-size: 40px;
	line-height: 40px;
	margin: 0;
	padding: 20px 0;
	color: #fff;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.3);
	font-family: 'Prata', serif;
	font-weight: normal;
}

.fs-block figcaption p {
	color: #fff;
	padding: 20px 0;
	margin: 0;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.2);
	border-top: 1px solid rgba(255,255,255,0.2);
	box-shadow: 0 -1px 0 rgba(0,0,0,0.3);
}

The navigation will be placed in the bottom right corner:

.fs-navigation {
	position: absolute;
	z-index: 2000;
	bottom: 10px;
	right: 15%;
	margin-right: 15px;
	user-select: none;
}

The spans will be used as the navigation arrows and they will be floating left and we’ll add the arrow as a background image:

.fs-navigation span {
	float: left;
	width: 26px;
	height: 26px;
	border-radius: 4px;
	text-indent: -90000px;
	cursor: pointer;
	opacity: 0.6;
	margin-right: 3px;
	background: rgba(0,0,0,0.4) url(../images/arrow.png) no-repeat 50% 50%;
	pointer-events: auto;
}

The second span will be rotated so that the arrow points to the right:

.fs-navigation span:nth-child(2) {
	transform: rotate(180deg);
}

On hover we will increase the opacity:

.fs-navigation span:hover{
	opacity: 1;
}

Now, let’s add the transitions for the sliding effect. Each panel will have a different delay. Since we always want to animate a kind of swipe from the right, the first panel will have the highest delay. On the other hand, the third one will have no delay. The timing-function will be a custom cubic-bezier:

.fs-block:nth-child(1) figure {
	transition: width 900ms cubic-bezier(0, 0, .15, 1) 600ms;
}
.fs-block:nth-child(2) figure {
	transition: width 900ms cubic-bezier(0, 0, .15, 1) 300ms;
}
.fs-block:nth-child(3) figure {
	transition: width 900ms cubic-bezier(0, 0, .15, 1);
}

If you want to try different cubic-bezier timing functions, try this nice online tool.

Last, but not least, let’s add some media queries for adjusting the size of the text elements:

/* Media Queries */

@media screen and (max-width: 1024px) {
	.fs-block figcaption h3 {
		font-size: 26px;
	}
}

@media screen and (max-width: 768px) {
	.fs-block figcaption {
		padding: 0 10px;
	}
	.fs-block figcaption h3 {
		font-size: 16px;
		padding: 10px 0;
	}
	.fs-block figcaption p {
		font-size: 13px;
	}
}

And that’s all the style. Now, let’s look at the JavaScript.

The JavaScript

Our plugin options will only have the autoplay settings. Like we have seen before, we set the transitions configuration in the CSS.

$.ImgSlider.defaults	= {
	autoplay	: false,
	interval	: 4000
};

We will start by preloading all the images and once that’s done we will execute the _init function:

_init				: function( options ) {

	// options
	this.options			= $.extend( true, {}, $.ImgSlider.defaults, options );

	this.current			= 0;

	// https://github.com/twitter/bootstrap/issues/2870
	var transEndEventNames	= {
		'WebkitTransition'	: 'webkitTransitionEnd',
		'MozTransition'		: 'transitionend',
		'OTransition'		: 'oTransitionEnd',
		'msTransition'		: 'MSTransitionEnd',
		'transition'		: 'transitionend'
	};
	this.transEndEventName	= transEndEventNames[ Modernizr.prefixed('transition') ];

	// the initial elements
	this.$initElems			= this.$el.children( 'figure' );
	// total number of elements
	this.initElemsCount		= this.$initElems.length;

	if( this.initElemsCount < 3 ) {

		return false;

	}

	// build layout
	this._layout();
	// init events
	this._initEvents();

	// autoplay on
	if( this.options.autoplay ) {

		this._startSlideshow();

	}

}

Here we are basically caching some elements and initializing some variables to be used later.
If we have more than three items or images, we will build the structure mentioned before. We also set the window resize event.

Finally, if the autoplay option is set to true, we trigger the auto slideshow.

_layout				: function() {

	this.$initElems.wrapAll( '<div class="fs-temp"></div>' ).hide();

	this.$initElems
		.filter( ':lt(3)' )
		.clone()
		.show()
		.prependTo( this.$el )
		.wrap( '<div class="fs-block"></div>' );

	this.$el
		.wrap( '<section class="fs-container"></section>' )
		.wrap( '<div class="fs-wrapper"></div>' );

	this.$blocks	= this.$el.children( 'div.fs-block' );

	// cache the 3 main blocks
	this.$blockL	= this.$blocks.eq( 0 );
	this.$blockC	= this.$blocks.eq( 1 );
	this.$blockR	= this.$blocks.eq( 2 );

	this.$blockC.find( 'figcaption' ).addClass( 'fs-transition' );

	// all items
	this.$temp		= this.$el.find( 'div.fs-temp' );

	// resize images
	this._resizeBlocks();

	// add navigation if needed
	if( this.initElemsCount > 3 ) {

		var $nav = $( '<nav class="fs-navigation"><span>Previous</span><span>Next</span></nav>' ).appendTo( this.$el.parent() );

		// next and previous
		this.$navPrev	= $nav.find( 'span:first' );
		this.$navNext	= $nav.find( 'span:last' );

		this._initNavigationEvents();

	}

}

The _layout function will make sure that the first three items will be the ones visible in our 3d structure. All the initial items will be hidden and wrapped in the fs-temp division.

We also need to resize each image according to its wrapper size. The main item’s description (the middle item) is shown while all the others stay hidden. Last, we create the navigation buttons and initialize their events, if we have more than three items.

_initNavigationEvents	: function() {

	var _self = this;

	this.$navPrev.on( 'click.imgslider', function() {

		if( _self.options.autoplay ) {

			clearTimeout( _self.slideshow );
			_self.options.autoplay	= false;

		}

		_self._navigate( 'left' );

	} );
	this.$navNext.on( 'click.imgslider', function() {

		if( _self.options.autoplay ) {

			clearTimeout( _self.slideshow );
			_self.options.autoplay	= false;

		}

		_self._navigate( 'right' );

	} );

}

_navigate				: function( dir ) {

	if( this.isAnimating === true ) {

		return false;

	}

	this.isAnimating = true;

	var _self	= this,
		$items	= this.$temp.children(),
		LIndex, CIndex, RIndex;

	this.$blocks.find( 'figcaption' ).hide().css( 'transition', 'none' ).removeClass( 'fs-transition' );

	if( dir === 'right' ) {

		LIndex = this.current + 1;
		CIndex = this.current + 2;
		RIndex = this.current + 3;

		if( LIndex >= this.initElemsCount ) {

			LIndex -= this.initElemsCount

		}

		if( CIndex >= this.initElemsCount ) {

			CIndex -= this.initElemsCount

		}

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

		LIndex = this.current - 1;
		CIndex = this.current;
		RIndex = this.current + 1;

		if( LIndex < 0 ) {

			LIndex = this.initElemsCount - 1

		}

	}

	if( RIndex >= this.initElemsCount ) {

		RIndex -= this.initElemsCount

	}

	var $elL	= $items.eq( LIndex ).clone().show(),
		$elC	= $items.eq( CIndex ).clone().show(),
		$elR	= $items.eq( RIndex ).clone().show();

	// resize images
	$elL.children( 'img' ).css( this.$blockL.data( 'imgstyle' ) );
	$elC.children( 'img' ).css( this.$blockC.data( 'imgstyle' ) );
	$elR.children( 'img' ).css( this.$blockR.data( 'imgstyle' ) );

	this.$blockL.append( $elL );
	this.$blockC.append( $elC );
	this.$blockR.append( $elR );

	// now show new images

	var $slides = this.$blocks.find( 'figure:first' ).css( 'width', '0%');

	if( Modernizr.csstransitions ) {

		$slides.on( this.transEndEventName, function( event ) {

			var $this 		= $( this ),
				blockIdx	= $this.parent().index('');

			_self._slideEnd( dir, blockIdx, $elC );

			$this.off( _self.transEndEventName ).remove();

		} );

	}
	else {

		$slides.each( function() {

			var $this 		= $( this ),
				blockIdx	= $this.parent().index('');

			_self._slideEnd( dir, blockIdx, $elC );

		} );

		this._slideEnd();

	}

}

The _navigate function takes control of which items will be positioned in the three visible blocks. We insert each of these item’s figure inside the block, resize the new images, and animate the width property of that same current block’s figure to 0px. In the end, we remove it leaving the new figure in its place. Also, we keep showing the middle block’s description while hiding all the others. This and also updating the current variable is done in the _slideEnd function:

_slideEnd				: function( dir, blockIdx, $main ) {

	if( blockIdx === 0 ) {

		if( this.current === this.initElemsCount - 1 && dir === 'right' ) {

			this.current = 0;

		}
		else if( this.current === 0 && dir === 'left' ) {

			this.current = this.initElemsCount - 1;

		}
		else {

			( dir === 'right' ) ? ++this.current : --this.current;

		}

		this.isAnimating = false;

	}
	else if( blockIdx === 1 ) {

		$main.find( 'figcaption' ).addClass( 'fs-transition' );

	}

}

The last thing we need to take care is when the user resizes the window. We need to resize the visible block images accordingly. Like we said before, this is also done once _layout is called:

_initEvents				: function() {

	var _self = this;

	$window.on( 'debouncedresize.imgslider', function() {

		_self._resizeBlocks();

	} );

},
// resize the images
_resizeBlocks			: function() {

	var _self = this;

	this.$blocks.each( function( i ) {

		var $el 	= $( this ).children( 'figure' ),
			$img	= $el.children( 'img' ),
			dim		= _self._getImageDim( $img.attr( 'src' ), { width : $el.width(), height : $el.height() } );

		// save the image dimentions
		switch( i ) {
			case 0 : _self.$blockL.data( 'imgstyle', dim ); break;
			case 1 : _self.$blockC.data( 'imgstyle', dim ); break;
			case 2 : _self.$blockR.data( 'imgstyle', dim ); break;
		};

		// apply style
		$img.css( dim );

	} );

}

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

View demo Download source

Unfolding 3D Thumbnails Concept

$
0
0

Unfolding 3D Thumbnails Concept

View demo Download source

Today we want to share a little 3D concept for an image slideshow (or similar) with you. The idea is to reveal thumbnails using a neat effect: the current image will be “opened” in the middle and the thumbnails view will unfold in 3D.

3D is absolutely in right now. With CSS 3D transforms we can explore new ways of playing with space and create depth and realism to normally flat elements.

Check out the following inspiring concepts on Dribbble:

Please note: this only works in browsers that support the respective CSS properties.

The images used in the demos are by Angelo González and they are licensed under the Creative Commons Attribution 2.0 Generic (CC BY 2.0) License.

(Vendor prefixes will be omitted in the following, but you’ll of course find them in the files.)

For this effect we want to have a simple initial structure, a structure that might be used in an image slideshow. This structure will get transformed, so that we can create the 3D animation.

So, from the following structure

<div id="th-wrap" class="th-wrap">

	<!-- current image -->
	<img src="images/1.jpg"/>
	
	<!-- thumbnails view -->
	<div class="th-inner">
		<ul>
			<li>
				<a href="#">
					<img src="images/thumbs/1.jpg" />
				</a>
			</li>
			<li> <!-- ... --> </li>
			<li> <!-- ... --> </li>
			<li> <!-- ... --> </li>
			<!-- ... -->
		</ul>
	</div>
	
</div>

we want to create this structure:

<div id="th-wrap" class="th-wrap th-active">

	<div class="th-part"></div>
						
	<div class="th-part">
		<div class="th-inner">
			<ul>
				<!-- ... -->
			</ul>
			<div class="th-overlay"></div>
		</div>
	</div>
	
	<div class="th-part">
		<div class="th-inner" style="display: block; ">
			<ul>
				<!-- ... -->
			</ul>
			<div class="th-overlay"></div>
		</div>
	</div>
	
	<div class="th-part"></div>
	
</div>

The divisions with the class th-part will be the parts that will animate. Each one of the parts will have a height of half of the whole wrapper (150 pixels):

.th-wrap .th-part {
	width: 100%;
	height: 150px;
	position: relative;
	overflow: hidden;
	background: #f0f0f0;
}

The two outer ones (the ones with no content) will have the current slideshow image as background image:

.th-wrap .th-part:nth-child(1),
.th-wrap .th-part:nth-child(4) {
	background: #000 url(../images/1.jpg) no-repeat top left;
	z-index: 100;
}
.th-wrap .th-part:nth-child(4) {
	transform: translateY(-300px);
	background-position: bottom left;
}

The last one also needs to be pulled up because we want it to touch the upper one.

The second and third part will contain the th-inner division from the initial structure and we’ll rotate them in 3D:

.th-wrap .th-part:nth-child(2){
	transform-origin: center bottom;	
	transform: translateY(-150px) translateZ(-150px) rotateX(-90deg);
}

.th-wrap .th-part:nth-child(3) {
	transform-origin: center top;	
	transform: translateY(-150px) translateZ(-150px) rotateX(90deg);
}

The idea is that we duplicate the content and pull the one of the second copy up (we set a margin of -150 pixels to the th-inner div of the second one):

.th-wrap .th-part:nth-child(3) .th-inner{
	margin-top: -150px;
}

When we click on the button to toggle the view, we will add the class th-active to the wrapper:

.th-wrap.th-active .th-part:nth-child(2),
.th-wrap.th-active .th-part:nth-child(3){
	transform: translateY(-150px) translateZ(0px) rotateX(0deg);
}

.th-wrap.th-active .th-part:nth-child(1),
.th-wrap.th-active .th-part:nth-child(4){
	transform: translateY(-150px);
}

Of course, we’ll need to add some transitions to the respective elements. The first transition is for all the parts, but then we’ll redefine the timing functions for some cases so that we have a smooth experience. The key here is to understand that the transition defined for the th-part class will be applied when the thumbnail view closes, since we have a redefined transition for the active one. So, when we open the thumbnails view, we want the 2nd and 3rd part to have a ease-in-out timing function:


.th-wrap .th-part {
	transition: all 500ms ease-out;
}

.th-wrap .th-part:nth-child(1),
.th-wrap .th-part:nth-child(4) {
	transition: all 480ms ease-in-out;
}

.th-wrap.th-active .th-part:nth-child(2),
.th-wrap.th-active .th-part:nth-child(3) {
	transition-timing-function: ease-in-out;
}

.th-wrap.th-active .th-part:nth-child(1),
.th-wrap.th-active .th-part:nth-child(4) {
	transition: all 400ms ease-in-out;
}

The tweaking of timing and timing functions depends on the translation values of the parts. If they would be narrower, the difference might be a bit less.

And that’s the important bits and pieces. In the demo we’ll have a slideshow mockup with a button that we will be able to activate. We’ll use some JS magic to add the active class to the wrapper and change the structure to the four parts with the double thumbnails content.

For browsers that don’t support the new CSS properties, we’ll simply show/hide the thumbnails view without any fancy effect.

I hope you find this concept inspiring!

View demo Download source

BookBlock: A Content Flip Plugin

$
0
0

BookBlock: A Content Flip Plugin

View demo Download source

BookBlock is a jQuery plugin that can be used for creating booklet-like components that allow a “page flip” navigation. Any content can be used, such as images or text. The plugin transforms the structure only when needed (i.e. when flipping a page) and uses some overlays as shadow for the pages to create more realism.

We will be using jQuery++ by Bitovi which has some add-ons for jQuery (specifically, to add the swipe event).

The following structure will allow to add custom content in a wrapper with the class “bb-item”, which represents an open page (left and right side):

<div id="bb-bookblock" class="bb-bookblock">
	<div class="bb-item">
		<!-- custom content -->
	</div>
	<div class="bb-item">
		<!-- ... -->
	</div>
	<div class="bb-item">
		<!-- ... -->
	</div>
	<div class="bb-item">
		<!-- ... -->
	</div>
	<!-- ... -->
</div>

The plugin can be called like this:

$(function() {
			
	$( '#bb-bookblock' ).bookblock();

});

You also have to include the other scripts that are needed (see one of the demo files).

Options

The following options are available:

// speed for the flip transition in ms.
speed		: 1000,

// easing for the flip transition.
easing		: 'ease-in-out',

// if set to true, both the flipping page and the sides will have an overlay to simulate shadows
shadows		: true,

// opacity value for the "shadow" on both sides (when the flipping page is over it).
// value : 0.1 - 1
shadowSides	: 0.2,

// opacity value for the "shadow" on the flipping page (while it is flipping).
// value : 0.1 - 1
shadowFlip	: 0.1,

// perspective value
perspective	: 1300,

// if we should show the first item after reaching the end.
circular	: false,

// if we want to specify a selector that triggers the next() function. example: '#bb-nav-next'.
nextEl		: '',

// if we want to specify a selector that triggers the prev() function.
prevEl		: '',

// If true it overwrites the circular option to true!
autoplay        : false,
		
// time (ms) between page switch, if autoplay is true. 
interval        : 3000,

// callback after the flip transition.
// page is the current item's index.
// isLimit is true if the current page is the last one (or the first one).
onEndFlip	: function( page, isLimit ) { return false; },

// callback before the flip transition.
// page is the current item's index.
onBeforeFlip: function( page ) { return false; }

The following public methods are available to use in combination with navigation elements:

  • next()
  • prev()
  • jump(position)

Check out the three demos with different configurations:

View demo Download source

3D Restaurant Menu Concept

$
0
0

3D Restaurant Menu Concept

View demo Download source

Applying CSS 3D transforms to components can bring some more realism to normally flat web elements. We’ve experimented with some simple concepts for restaurant websites and came up with a 3D folded menu (a real menu, not a “web” menu). The result is a restaurant website template where the menu will open by unfolding. Clicking the linked items will reveal a modal overlay which contains some more info.

Since this “flyer” structure requires a decent amount of space, and, although we will make this responsive, we’ll add a media query for smaller screens where everything will fall back to a simplified stacked structure. The same will be applied for browsers that don’t support 3D transforms.

The thumbnails and delicious recipes are by Michael Natkin from herbivoracious.com (all the ones that have a link).

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

Alright, so let’s get started!

The CSS will not contain any vendor prefixes, but you will find them in the files.

The Markup

Our structure will consist of a main container with the class “rm-container” and we’ll have a wrapper inside. The wrapper will contain the three panels. Initially, we only want to see the cover of the folded menu; that is the division with the class “rm-cover”. The last panel is the one with the class “rm-right”. The middle panel is the one we see in the middle when we open the menu:

<div id="rm-container" class="rm-container">

	<div class="rm-wrapper">
		<div class="rm-cover"></div>
		<div class="rm-middle"></div>
		<div class="rm-right"></div>
	</div><!-- /rm-wrapper -->

</div><!-- /rm-container -->

Inside of the rm-cover and the rm-right division, we will have a front- and a backface:

<div class="rm-front">

	<div class="rm-content">
		<!-- Some content -->
	</div><!-- /rm-content -->
	
</div><!-- /rm-front -->

<div class="rm-back">

	<div class="rm-content">
		<!-- Some content -->
	</div><!-- /rm-content -->

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

</div><!-- /rm-back -->

We’ll also add an element for an overlay gradient that will give the menu parts a bit more realism. Note that the front will be empty in the last element.

The middle part of the menu will simply have another wrapper inside, no front or back part:

<div class="rm-inner">

	<div class="rm-content">
		<!-- Some content -->
	</div><!-- /rm-content -->

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

</div><!-- /rm-inner -->

We will never see the back part of this part of the menu, so we won’t need that structure.

The content will consist of some text elements, like definition lists for the dishes and headlines:

<div class="rm-content">

	<h4>Appetizers</h4>

	<dl>
		<dt>Bella's Artichokes</dt>
		<dd>Roasted artichokes with chipotle aioli and cream cheese</dd>

		<dt><a href="http://herbivoracious.com/2011/11/crostini-with-young-pecorino-grilled-figs-and-arugula-mint-pesto-recipe.html" class="rm-viewdetails" data-thumb="images/1.jpg">Green Love Crostini</a></dt>
		<dd>Crostini with young pecorino, grilled figs and arugula & mint pesto</dd>
		
		<dt>Focaccia di Carciofi</dt>
		<dd>Artichoke focaccia with fresh thyme</dd>

		<!-- ... -->
	</dl>

	<h4>Salads & More</h4>
	
	<dl>
		<!-- ... -->
	</dl>

</div><!-- /rm-content -->

Notice the anchor with the class “rm-viewdetails” and the data attribute “data-thumb”. We will use this as the content for a modal overlay box that will appear when clicking on the link:

<div class="rm-modal">
	<div style="background-image: url(images/1.jpg)" class="rm-thumb"></div>
	<h5>Green Love Crostini</h5>
	<p>Crostini with young pecorino, grilled figs and arugula & mint pesto</p>
	<a href="http://herbivoracious.com/2011/11/crostini-with-young-pecorino-grilled-figs-and-arugula-mint-pesto-recipe.html">See the recipe</a>
	<span class="rm-close-modal">x</span>
</div>

It will contain the same headline and description that we get from the followed dd element (definition description), the thumbnail that we get from the data-attribute and the link.

And that’s the HTML, now, let’s take a look at the style.

Note that we won’t go into the styling of the text elements or inner content parts. Instead we will focus on the 3D structure, transition classes and media queries.

The CSS

We want to make the whole thing fluid, so we will give the main container a percentage width, and, since we want some 3D awesomeness, some perspective:

.rm-container {
	width: 33%;
	height: 700px;
	max-width: 370px;
	margin: 0 auto 40px auto;
	position: relative;
	perspective: 1600px;
	color: #2a323f;
}

The wrapper and its children divisions (the cover, middle part and right part) will all have the full width and height and we’ll position them absolutely:

.rm-wrapper,
.rm-wrapper > div {
	width: 100%;
	height: 100%;
	left: 0;
	top: 0;
	position: absolute;
	text-align: center;
	transform-style: preserve-3d;
}

Since we will be working in 3D perspective, we want the transform style of these elements to be “preserve-3d”.

The cover needs a higher z-index than all the other parts and we’ll set the transform origin to be on the left edge (left center). The transition delay is for when we close the menu. The transition to the open state will contain another value, but let’s look at that a bit later:

.rm-wrapper .rm-cover {
	z-index: 100;
	transform-origin: 0% 50%;
	transition-delay: 0.2s;
}

The middle part will have the lowest z-index of all three parts and we’ll add a subtle box shadow:

.rm-wrapper .rm-middle {
	z-index: 50;
	box-shadow: 0 4px 10px rgba(0,0,0,0.7);
}

The right most part will have a z-index that is higher than the middle part one but lower than the cover one. The transform origin will be on the right edge (right center) and there will be no transition delay when we close the menu:

.rm-wrapper .rm-right {
	z-index: 60;
	transform-origin: 100% 50%;
	transition-delay: 0s;
}

The inner divisions which are the ones with the classes “rm-front”, “rm-back” and “rm-inner” will have a paper texture as background and we’ll add a inset box shadow that will simulate a multi-line decorative border:

.rm-wrapper > div > div {
	background: #fff url(../images/white_paperboard.jpg);
	width: 100%;
	height: 100%;
	position: absolute;
	padding: 30px;
	box-shadow: 
		inset 0 0 0 16px #fff, 
		inset 0 0 0 17px #e6b741, 
		inset 0 0 0 18px #fff, 
		inset 0 0 0 19px #e6b741, 
		inset 0 0 0 20px #fff, 
		inset 0 0 0 21px #e6b741;
}

Now, let’s add the important 3D properties. The front and the back face need backface-visibility set to hidden:

.rm-container .rm-front,
.rm-container .rm-back {
	backface-visibility: hidden;
}

Let’s rotate the backfaces so that they are behind the front parts:

.rm-container .rm-back {
	transform: rotateY(-180deg);
}

The overlay will generally be an almost transparent gradient from light to a bit darker:

.rm-overlay {
	position: absolute;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
	pointer-events: none;
	background: linear-gradient(to right, rgba(0,0,0,0) 0%, rgba(0,0,0,0.05) 100%);
}

For the middle overlay we will turn the gradient to the other side:

.rm-middle .rm-overlay {
	background: linear-gradient(to right, rgba(0,0,0,0) 64%, rgba(0,0,0,0.05) 100%);
}

Let’s add some padding the the content divisions:

.rm-content {
	padding: 20px;
}

The modal layer will be invisible: we’ll set the opacity to 0, the pointer-event to none and we’ll translate it on the Z-axis:

.rm-modal {
	position: absolute;
	z-index: 10000;
	width: 120%;
	margin-left: -10%;
	top: 50%;
	padding: 40px;
	background: #fff url(../images/white_paperboard.jpg);
	box-shadow: 
		inset 0 0 0 16px #fff, 
		inset 0 0 0 17px #e6b741, 
		inset 0 0 0 18px #fff, 
		inset 0 0 0 19px #e6b741, 
		inset 0 0 0 20px #fff, 
		inset 0 0 0 21px #e6b741,
		0 4px 20px rgba(0,0,0,0.4);
	opacity: 0;
	pointer-events: none;
	transform: translateZ(1000px);
}

The idea is to reveal the modal when we click on one of the links on the menu. We’ll scale the menu down and make the modal appear from “above”. This concept is inspired by Hakim El Hattab’s modal concept Avgrund.

Let’s add some transitions and define some classes for opening the menu.

First, we’ll give a transition to the wrapper (for scaling it when opening the modal) and to the children of the wrapper, i.e. our three menu parts (for animating the “opening”/rotation):

.rm-wrapper, .rm-wrapper > div { transition: all 0.6s ease-in-out; }

The modal will also have a transition for the transformation (to scale down) and its opacity:

.rm-modal {
	transition: 
		transform 0.6s ease-in-out,
		opacity 0.6s ease-in-out;
}

The idea is to add a class called "rm-open" which will trigger the menu to unfold. This class will be added with JavaScript when we click on the "View the menu" link.

When adding the class, we will define what will happen to all the elements when we open the menu.

The children of the wrapper will all get a box shadow:

.rm-container.rm-open .rm-wrapper > div {
	box-shadow: 0 4px 5px -3px rgba(0,0,0,0.6);
}

The cover will rotate (without any delay) -180 degrees on the Y-axis. Since we defined the transform origin to be on the left center, it will open to the left:

.rm-container.rm-open .rm-cover {
	transform: rotateY(-180deg);
	transition-delay: 0s;
}

The right part will rotate 180 degrees, but here we will add a transition delay of 0.2s because we want the cover to open a bit first:

.rm-container.rm-open .rm-right {
	transform: rotateY(180deg);
	transition-delay: 0.2s;
}

When we click on one of the menu item that is an anchor, we will add another class called "rm-in" to the container and move the wrapper down on the Z-axis:


.rm-container.rm-in .rm-wrapper {
	transform: translateZ(-500px);
}

The cover and the right part will be rotated a bit more to the inside:

.rm-container.rm-in .rm-cover {
	transform: rotateY(-150deg);
}

.rm-container.rm-in .rm-right {
	transform: rotateY(150deg);
}

We need to set the transition delay to 0 seconds for this case:

.rm-container.rm-in .rm-cover, 
.rm-container.rm-in .rm-right,
.rm-container.rm-nodelay .rm-right {
	transition-delay: 0s;
}

The class "rm-nodelay" is an extra class that we'll need for the right menu part when we close the modal. Remember, it had a transition delay for when we open the menu.

The modal will appear by translating it to 0px on the Z-axis and increasing the opacity to 1:

.rm-container.rm-in .rm-modal {
	transform: translateZ(0px);
	opacity: 1;
	pointer-events: auto;
}

Let's define some media queries for smaller screens and devices. The first media query will simply resize the wrapper:

@media screen and (max-width: 1110px) {
	.rm-container {
		height: 800px;
	}
}

Since we made the wrapper fluid, it will need a larger height in order to contain the text.

From 960 pixels on, we no longer want to show the menu as a folded flyer but as normal, scrollable content.
For that, we'll simply set the height of the wrapper to auto, give it 100% of width and define a maximum width:

@media screen and (max-width: 960px) {

	.rm-container {
		width: 100%;
		height: auto;
		max-width: 460px;
	}

All the inner divisions will no longer be absolute, but relative and we'll give them 100% of width and set the height to auto:


	.rm-wrapper, 
	.rm-wrapper > div,
	.rm-wrapper > div > div {
		position: relative;
		width: 100%;
		height: auto;
	}

The content divisions will need some margin:

	.rm-wrapper > div > div{
		margin-bottom: 10px;
		box-shadow: 
			inset 0 0 0 16px #fff, 
			inset 0 0 0 17px #e6b741, 
			inset 0 0 0 18px #fff, 
			inset 0 0 0 19px #e6b741, 
			inset 0 0 0 20px #fff, 
			inset 0 0 0 21px #e6b741,
			0 3px 5px rgba(0,0,0,0.2);
	}

Let's remove the rotations:

	.rm-container .rm-back,
	.rm-container.rm-open .rm-cover,
	.rm-container.rm-open .rm-right {
		transform: rotateY(0deg);
	}

We don't need the overlays any more:

	.rm-overlay, .rm-middle .rm-overlay {
		display: none;
	}

And we set the position of the modal to fixed, so that it sticks on top when we scroll:

	.rm-container .rm-modal {
		position: fixed;
		width: 80%;
		top: 100px;
		left: 50%;
		margin: 0 0 0 -40%;
		transform: translateZ(0px);
		transition: opacity 0.6s ease-in-out 0s;
	}

When we click on a menu item link and the modal appears we won't rotate anything any more:

	.rm-container.rm-in .rm-cover,
	.rm-container.rm-in .rm-right,
	.rm-container.rm-in .rm-wrapper {
		transform: rotateY(0deg);
		transition-delay: 0s;
	}
}

And that's all the important style. For browsers that don't support 3D transforms, we will use almost the same styling like we do for this last media query. Since we use Modernizr, we'll just add those classes again, preceded by .no-csstransforms3d (which will be added to the body).

Now, let's add some lines of JavaScript.

The JavaScript

We will start by caching some elements:

	// main container
var $container = $( '#rm-container' ),						
	// the cover, middle and right panels
	$cover = $container.find( 'div.rm-cover' ),
	$middle = $container.find( 'div.rm-middle' ),
	$right = $container.find( 'div.rm-right' ),
	// open and close elements
	$open = $cover.find('a.rm-button-open'),
	$close = $right.find('span.rm-close'),
	// the links for each recipe (photo and details)
	$details = $container.find( 'a.rm-viewdetails' ),

The events for opening/closing the menu and also for showing each item's details (modal) are initialized:

init = function() {

		initEvents();

	},
initEvents = function() {

	$open.on( 'click', function( event ) {

		openMenu();
		return false;

	} );

	$close.on( 'click', function( event ) {

		closeMenu();
		return false;

	} );

	$details.on( 'click', function( event ) {

		$container.removeClass( 'rm-in' ).children( 'div.rm-modal' ).remove();
		viewDetails( $( this ) );
		return false;

	} );
	
},

To open/close the menu we will be adding/removing the class 'rm-open' from the $container. Remember, this is the class where the transitions are defined.

Note that on close, we are also removing the classes 'rm-nodelay' and 'rm-in'. These are classes that are added if we click to see the menu item's details.

openMenu = function() {

	$container.addClass( 'rm-open' );

},
closeMenu = function() {

	$container.removeClass( 'rm-open rm-nodelay rm-in' );

},

Finally, if we click to see an item's details, a modal box will be shown with the image and the text for that item. We translate the main container on the Z-axis (and adjust the top margin in order to center it), and slightly rotate the left and right panels:

viewDetails = function( recipe ) {

	var title = recipe.text(),
		img = recipe.data( 'thumb' ),
		description = recipe.parent().next().text(),
		url = recipe.attr( 'href' );

	var $modal = $( '
' + title + '

' + description + '

See the recipex
' ); $modal.appendTo( $container ); var h = $modal.outerHeight( true ); $modal.css( 'margin-top', -h / 2 ); setTimeout( function() { $container.addClass( 'rm-in rm-nodelay' ); $modal.find( 'span.rm-close-modal' ).on( 'click', function() { $container.removeClass( 'rm-in' ); } ); }, 0 ); };

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

View demo Download source

Windy: A Plugin for Swift Content Navigation

$
0
0

Windy

View demo Download source

Windy is a jQuery plugin that allows a rapid navigation through a pile of items using some CSS 3D transforms and transitions. The effect of a continuous navigation flow reminds of a deck of cards or sheets flying away, hence the name. This plugin idea is inspired by the CSS shaders examples as shown in the CSS shaders Flipbook video by Adobe.

There are several options available, including defining the range of spreading the items when navigating, speed (CSS transitions), adding custom navigations, etc.

For browsers that don’t support CSS 3D transforms or transitions, there will be a simple fallback, i.e. normal navigation of the items (appear/disappear).

For using the plugin, an unordered list with the class “wi-container” is expected:

<ul id="wi-el" class="wi-container">
	<li>
		<img src="images/demo1/1.jpg" alt="image1"/>
		<h4>Coco Loko</h4>
		<p>Total bicycle rights in blog four loko raw denim ex, helvetica sapiente odio placeat.</p>
	</li>
	<li> <!-- ... --> </li>
	<li> <!-- ... --> </li>
	<li> <!-- ... --> </li>
	<!-- ... --> 
</ul>

The plugin can be called like this:

$( '#wi-el' ).windy();

Options

The following options are available:

// the options
$.Windy.defaults = {
	// if we want to specify a selector that triggers the next() function. Example: '#wi-nav-next'.
	nextEl : '',
	// if we want to specify a selector that triggers the prev() function.
	prevEl : '',
	// rotation and translation boundaries for the items transitions
	boundaries : {
		rotateX : { min : 40 , max : 90 },
		rotateY : { min : -15 , max : 15 },
		rotateZ : { min : -10 , max : 10 },
		translateX : { min : -200 , max : 200 },
		translateY : { min : -400 , max : -200 },
		translateZ : { min : 250 , max : 550 }
	}
};

The boundaries define the values for the “dispersing” or “flying away” of the items. The plugin will randomly select a value between the indicated limits.

The following public methods are available to use in combination with navigation elements:

  • next()
  • prev()
  • navigate(position)

If you’d like to see some examples on how to use navigation elements, check out the demos where we use arrows and the jQuery UI slider element.

The CSS transition and basic styling is defined in the windy.css file.

Check out the three demos with different configurations:

View demo Download source

PFold: Paper-Like Unfolding Effect

$
0
0

PFold: Paper-Like Unfolding Effect

View demo Download source

PFold is a very experimental jQuery plugin that makes it possible to open elements in a paper-like unfolding effect. An element can be unfolded in 3D in order to reveal some other content. The idea is to have several levels of folding, where each step of opening will double the element size, hence imitating a folded paper being opened.

A couple of options are available, such as the unfolding direction for each step and the number of folding steps.

For browsers that don’t support CSS 3D transforms or transitions, there will be a simple fallback, i.e. the larger content appears.

For using the plugin, the following structure is expected:

<div id="uc-container" class="uc-container">
	<div class="uc-initial-content">
		<!-- initial content -->
	</div>
	<div class="uc-final-content">
		<!-- final content -->
	</div>
</div>

The final content’s size will depend on the initial content’s size (set in the CSS), the folding directions and the number of folding steps. For example, having an initial element of 200px width and height, a folding direction of bottom and left and two folding steps will create a final area of 400px in width and height.

The plugin can be called like this:

$( '#uc-container' ).pfold();

Options

The following options are available:

// the options
$.PFold.defaults = {
	// perspective value
	perspective : 1200,

	// each folding step's speed
	speed : 450,

	// each folding step's easing 
	easing : 'linear',

	// delay between each (un)folding step (ms)
	folddelay : 0,

	// number of times the element will fold
	folds : 2,

	// the direction of each unfolding step
	folddirection : ['right','top'],

	// use overlays to simulate a shadow for each folding step 
	overlays : true,

	// the main container moves (translation) in order to keep its initial position 
	centered : false,

	// allows us to specify a different speed for the container's translation
	// values range : [0 - 1] 
	// if 0 the container jumps immediately to the final position (translation).
	// this is only valid if centered is true
	containerSpeedFactor : 1,

	// easing for the container transition
	// this is only valid if centered is true
	containerEasing : 'linear',

	// callbacks
	onEndFolding : function() { return false; },
	onEndUnfolding : function() { return false; }
};

Check out the three demos with different configurations:

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

Responsive CSS Timeline with 3D Effect

$
0
0

Responsive CSS Timeline with 3D Effect

View demo Download source

In this tutorial we are going to experiment with perspective and use a radio input trick to create a fun css-only timeline-like structure. The idea is to show a teaser of an item and when clicking on the associated radio input, the content will expand and rotate in 3D, giving some depth to the whole thing. We’ll be using CSS 3D transform, transitions and sibling selectors.

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

Also note that the 3D effect looks best in WebKit browsers. Unfortunately, Firefox does not play along very nicely.

Let’s get started!

The Markup

Let’s create an unordered list which will have the class “timeline”. We’ll add several list items with the class “event”. Each event will have a radio input, an empty label, a thumbnail and a container for the content. This container will have perspective, so we’ll give it the class “content-perspective”. Note that the radio inputs all have the same name. That’s how we indicate that they all belong to the same group and we can only select one at a time.

<ul class="timeline">
	
	<li class="event">
		<input type="radio" name="tl-group" checked/>
		<label></label>
		<div class="thumb user-4"><span>19 Nov</span></div>
		<div class="content-perspective">
			<div class="content">
				<div class="content-inner">
					<h3>I find your lack of faith disturbing</h3>
					<p>Some text</p>
				</div>
			</div>
		</div>
	</li>

	<li class="event">
		<input type="radio" name="tl-group"/>
		<!-- ... -->
	</li>

	<!-- ... -->

</ul>

The thumbnail will have the class “thumb” and and additional class for the user which will be user-1 to user-8 in our case. The span will be used to add the date.

The CSS

Note that the CSS will not contain any vendor prefixes, but you will find them in the files.
We’ll be using the border-box box-sizing and reset all the paddings and margins with the following snippet:

*,
*:after,
*:before {
	box-sizing: border-box;
	padding: 0;
	margin: 0;
}

You can of course omit the resetting of the paddings and margins, just keep in mind to adjust them when we define the style of the following elements.

Let’s first add a font that we’ve created with fontello.com. This font will only have four characters, two arrows and two states that we’ll associate with being “checked” and “unchecked”. It’s part of the Font Awesome font:

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

Let’s set the timeline to be relative and add some padding to it:

.timeline {
	position: relative;
	padding: 30px 0 50px 0;
	font-family: 'Gorditas', Arial, sans-serif;
}

The white striped line will be created by using a pseudo element. It’s position will be absolute and we’ll give it a striped background (made with Patternify):

.timeline:before {
	content: '';
	position: absolute;
	width: 5px;
	height: 100%;
	top: 0;
	left: 165px;
	background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAJUlEQVQIW2NkQAP/gYARWQwkAOLDBeECjEAAkkEWAKtEFwAJAgAZEBP+oM1IkwAAAABJRU5ErkJggg==);
}

Each event will be of position relative and we’ll add some margin to the bottom to have some space between the items and some padding to the right. This will make sure that the list item does not overflow too much to the right when we apply the 3D rotation:

.event {
	position: relative;
	margin-bottom: 80px;
	padding-right: 40px;
}

Let’s take care of the left side for now. Here we want the thumbnail to show up, so we’ll set it to absolute, make it round by setting the border-radius to 50%:

.thumb {
	position: absolute;
	width: 100px;
	height: 100px;
	box-shadow: 
		0 0 0 8px rgba(65,131,142,1), 
		0 1px 1px rgba(255,255,255,0.5);
	background-repeat: no-repeat;
	border-radius: 50%;
	transform: scale(0.8) translateX(24px);
}

We’ll also add a transformation which will scale it down a bit. Our aim is to scale it up, once the corresponding radio button is selected. Since the scaling will move the element a bit, we could either set the transform origin or translate it on the X-axis. We’ll go for the latter one and translate it 24 pixels. Why do we move it at all? We’ll have a little pseudo-element with a zig zag background that will point from the thumbnail and we want it to “touch” the radio input:

.thumb:before {
	content: '';
	position: absolute;
	height: 8px;
	z-index: -1;
	background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAAPElEQVQYV2NkQAP/gYARCNDFUQRgirAphitEl0TngxXisg5ZnBGXIpgbYfIYjkb3BNxGbBLYxIgyEaQRAA8KKAWYsZtjAAAAAElFTkSuQmCC);
	width: 51px;
	top: 42px;
	left: 100%;
	margin-left: 8px;
}

Let’s style the span for the date. We’ll set it to position absolute and put it under the thumbnail:

.thumb span {
	color: #41838e; 
	width: 100%;
	text-align: center;
	font-weight: 700;
	font-size: 15px;
	text-transform: uppercase;
	position: absolute;
	bottom: -30px;
}

Now, let’s define the different background images for the users (we could also use images instead of background-images):

.user-1 {
	background-image: url(../images/chewbacca.jpg);
}

.user-2 {
	background-image: url(../images/barf.jpg);
}

.user-3 {
	background-image: url(../images/darkhelmet.jpg);
}

.user-4 {
	background-image: url(../images/darthvader.jpg);
}

.user-5 {
	background-image: url(../images/leia.jpg);
}

.user-6 {
	background-image: url(../images/vespa.jpg);
}

.user-7 {
	background-image: url(../images/c3po.jpg);
}

.user-8 {
	background-image: url(../images/dotmatrix.jpg);
}

For the radio input we’ll use a little trick. We want our transitions and changes to trigger when we click on an input since we can make use of the “checked” state and the sibling selectors. There are many different ways to do it, one of them involving clicking on the label which selects the input in most of the browsers automatically. But we don’t want to use that hack, instead we’ll just use the input itself by stacking it on top of the label and making it transparent. What appears to be a click on the label is actually a click on the input. The label is just used for fanciness in this case, and if we could add pseudo-elements to inputs, we wouldn’t need it at all.

So, what we do is to give both, the label and the input the same width and height and put them in the same place:

.event label,
.event input[type="radio"] {
	width: 24px;
	height: 24px;
	left: 158px;
	top: 36px;
	position: absolute;
	display: block;
}

Since the input needs to precede all the other elements (remember that we want to reach them via the sibling selector), we’ll give it a higher z-index, to be on top of the label. If we wouldn’t do that, the label would naturally be on top of it:

.event input[type="radio"] {
	opacity: 0;
	z-index: 10;
	cursor: pointer;
}

Now, let’s create a little pseudo-element for the label, one that will contain a little icon from the icon font that we’ve included before. We’ll pull it into place and position it absolutely:

.event label:after {
	font-family: 'fontawesome-selected';
	content: '\e702';
 	background: #fff;
    border-radius: 50%;
    color: #41838E;
    font-size: 26px;
    height: 100%;
    width: 100%;
    left: -2px;
    top: -3px;
    line-height: 24px;
    position: absolute;
    text-align: center;
}

Let’s move on to the right side of our timeline, the content. We need a wrapper with perspective which will have a left margin that will move it to the side:

.content-perspective {
	margin-left: 230px;
	position: relative;
	perspective: 600px;
}

To “connect” the content part with the input, we’ll use a pseudo element that will look like a line:

.content-perspective:before {
	content: '';
	width: 37px;
	left: -51px;
	top: 45px;
	position: absolute;
	height: 1px;
	z-index: -1;
	background: #fff;
}

It’s important that we set the pseudo-class to the perspective wrapper since we don’t want it to rotate like anything else inside.

So, the content will be rotated 10 degrees, which, together with a transform origin set to the outer top and left, will make it appear as if its right side is being pushed back:

.content {
	transform: rotateY(10deg);
	transform-origin: 0 0;
	transform-style: preserve-3d;
}

The inner content will actually have the background color and the box shadow. We need this extra element in order to avoid funny things happening:

.content-inner {
	position: relative;
	padding: 20px;
	color: #333;
	border-left: 5px solid #41838e;
	box-shadow: 0 4px 6px rgba(0,0,0,0.1);
	background: #fff;
}

Let’s make the h3 look pretty:

.content-inner h3 {
	font-size: 26px;
	padding: 5px 0 5px 0;
	color: #41838e;
}

The paragraph will have a special role since it will transition it’s height. Because we can’t animate to height: auto (which would be optimal since we don’t know the height of the element), we’ll need to use a smart hack suggested by Lea Verou. We’ll animate the max-height value instead. Note that this does only create the desired transition if the max-height value is very close to the real height itself. So, we’ll need to adjust the max-height to not be too big. This is not a good solution, especially in a responsive setting. You’ll of course not want to worry about that since it almost breaks down to the same problem as setting the height itself. For our experiment we don’t want to use any JavaScript, so this is the best option and we’ll also define some media queries, so that things don’t break.

So, we set the max-height to 0 pixel and give the paragraph a transparent color (playing with opacities here was causing some flickering, so we’ll use this trick instead):

.content-inner p {
	font-size: 1.8px;
	max-height: 0px;
	overflow: hidden;
	color: rgba(0,0,0,0);
	text-align: left;
}

The aim is to expand the paragraph and animate the alpha opacity value of the RGBA to 1, simulating a fading in.

Let’s add the little arrow to the left side, using the icon font:

.content-inner:before {
	font-family: 'fontawesome-selected';
	content: '\25c2';
	font-weight: normal;
	font-size: 54px;
	line-height: 54px;
	position: absolute;
	width: 30px;
	height: 30px;
	color: #41838e;
	left: -22px;
	top: 19px;
	z-index: -1;
}

Next, we’ll define all the different transitions for the respective elements. We first want the content to expand and after that we want all the other transitions to happen. So we’ll need to add some delay for the resting transitions.

Let’s give the thumbnail, its span and the headline of the content a transition with 0.2s delay:

.thumb,
.thumb span,
.content-inner h3 {
	transition: all 0.6s ease-in-out 0.2s;
}

The inner content needs a transition for the box shadow:

.content-inner {
	transition: box-shadow 0.8s linear 0.2s;
}

The content will have a transition for the transformation and we’ll add a nice smooth bounce as the timing function:

.content {
	transition: transform 0.8s cubic-bezier(.59,1.45,.69,.98) 0.2s;
}

Now, the paragraph needs a transition for the max-height and for the color.

.content-inner p {
	transition: max-height 0.5s linear, color 0.3s linear;
}

Next, we’ll define what happens when we select a radio input. Once a radio is “checked”, we’ll give a different icon to the label’s pseudo class and change its color and box shadow:

.event input[type="radio"]:checked + label:after {
	content: '\2714';
	color: #F26328;
	box-shadow: 0 0 0 5px rgba(255, 255, 255, 0.8);
}

Let’s also change the color of the line and the heading of the content:

.event input[type="radio"]:checked ~ .content-perspective:before {
	background: #F26328;
}

.event input[type="radio"]:checked ~ .content-perspective .content-inner h3 {
	color: #F26328;
}

The content will rotate to the front:

.event input[type="radio"]:checked ~ .content-perspective .content {
	transform: rotateY(-5deg);
}

And the inner content will get a different border color and box shadow:

.event input[type="radio"]:checked ~ .content-perspective .content-inner {
	border-color: #F26328;
	box-shadow: 10px 0px 10px -6px rgba(0, 0, 0, 0.1);
}

The paragraph will animate to a max-height of 260 pixels and the color will become more opaque:

.event input[type="radio"]:checked ~ .content-perspective .content-inner p {
	max-height: 260px; /* Add media queries */
	color: rgba(0,0,0,0.6);
	transition-delay: 0s, 0.6s;
}

The transition delay set here will ensure that the color transition will happen with a delay. We first want the max-height to animate and then then the transparency of the RGBA color.

Let’s change the color of the arrow:

.event input[type="radio"]:checked ~ .content-perspective .content-inner:before {
	color: #F26328;
}

The thumbnail will scale up (and translate back to 0px) and we’ll give it a border-like box shadow:

.event input[type="radio"]:checked ~ .thumb {
	transform: scale(1);
	box-shadow: 
		0 0 0 8px rgba(242,99,40,1), 
		0 1px 1px rgba(255,255,255,0.5);
}

The date will change color:

.event input[type="radio"]:checked ~ .thumb span {
	color: #F26328;
}

And we’ll replace the blue zig zag line with an orange one:

.event input[type="radio"]:checked ~ .thumb:before {
	background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAAPUlEQVQYV2NkQAOfUjX+882+wYgujiIAU4RNMVwhuiQ6H6wQl3XI4oy4FMHcCJPHcDS6J2A2EqUQpJhohQBbNyaHFmzEqgAAAABJRU5ErkJggg==);
}

Now we need to make sure that everything looks fine when we view this with smaller screens. At 850 pixel we’ll set the font sizes to be smaller and also reset the max-height of the paragraph:


@media screen and (max-width: 850px) { 
	.content-inner h3 {
		font-size: 20px;
	}

	.content-inner p {
		font-size: 14px;
		text-align: justify;
	}

	.event input[type="radio"]:checked ~ .content-perspective .content-inner p {
		max-height: 500px; 
	}
}

From 540 pixel we need to do some more stuff. Since everything is getting a bit crammed, we will change out layout a bit. The thumbnail will stay where it is, but the content will be moved from he right side to under the thumbnail. The input will be placed on top of the thumbnail, so that we need to click the there in order to open the content. We’ll get rid of some elements that we won’t need here anymore, like the label and the lines. We’ll also change the rotation of the content making it stick out at the bottom and not the right side:

@media screen and (max-width: 540px) {
	.timeline:before {
		left: 50px;
	}

	.event { 
		padding-right: 0px;
		margin-bottom: 100px;
	}

	.thumb {
		transform: scale(0.8);
	}

	.event input[type="radio"] {
		width: 100px;
		height: 100px;
		left: 0px;
		top: 0px;
	}

	.thumb:before,
	.event input[type="radio"]:checked ~ .thumb:before {
		background: none;
		width: 0;
	}

	.event label {
		display: none;
	}

	.content-perspective {
		margin-left: 0px; 
		top: 80px;
	}

	.content-perspective:before {
		height: 0px; 
	}

	.content {
		transform: rotateX(-10deg);
	}

	.event input[type="radio"]:checked ~ .content-perspective .content {
		transform: rotateX(10deg);
	}

	.content-inner {
		border-left: none;
		border-top: 5px solid #41838e;
	}

	.event input[type="radio"]:checked ~ .content-perspective .content-inner {
		border-color: #F26328;
		box-shadow: 0 10px 10px -6px rgba(0, 0, 0, 0.1);
	}

	.content-inner:before {
		content: '\25b4';
		left: 33px;
		top: -32px;
	}

	.event input[type="radio"]:checked ~ .content-perspective .content-inner p {
		max-height: 300px; 
	}
}

The max-height value needs to be adjusted again because now we’ll have a bit more horizontal space for the paragraph.

If you’d like it to be possible to open more than one content item, just try out using checkboxes instead of radio inputs.

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

View demo Download source

Simple Effects for Drop-Down Lists

$
0
0

Simple Effects for Drop-Down Lists

View demo Download source

Today we want to share some simple drop-down list effects with you. The idea is to transform a normal select input into something more appealing with a jQuery plugin. The expanding of the options will happen with a transition and parameters can be configured in a way to achieve unique effects.

After the great custom drop-down list styles made by Hugo in his tutorial Custom Drop-Down List Styling, we wanted to make it simple to create a custom structure from a normal select input (without multiple option selection). So, we’ve come up with this little plugin that will allow some custom styling of the drop-down list. With the generated structure it is easy to apply some simple effects in order to spice it up.

Please note that the CSS transforms and transitions only work in browsers that support them.

Without JavaScript, the normal select input will simply be shown.

The icon font was created with IcoMoon.

So, we start out with a select input, for example:

<select id="cd-dropdown" class="cd-select">
	<option value="-1" selected>Choose an animal</option>
	<option value="1" class="icon-monkey">Monkey</option>
	<option value="2" class="icon-bear">Bear</option>
	<option value="3" class="icon-squirrel">Squirrel</option>
	<option value="4" class="icon-elephant">Elephant</option>
</select>

A dropdown plugin can be applied as follows:

$( '#cd-dropdown' ).dropdown();

The select and the options get transformed into the following structure:

<div class="cd-dropdown">
	<span>Choose an animal</span>
	<input type="hidden" name="cd-dropdown">
	<ul>
		<li data-value="1"><span class="icon-monkey">Monkey</span></li>
		<li data-value="2"><span class="icon-bear">Bear</span></li>
		<li data-value="3"><span class="icon-squirrel">Squirrel</span></li>
		<li data-value="4"><span class="icon-elephant">Elephant</span></li>
	</ul>
</div>

When clicking on the first span, we will apply the class “cd-active” to its parent, the division with the class “cd-dropdown”. When selecting an option, the respective span will get inserted into the first one.

Options

The following default options are available:

speed : 300,
easing : 'ease',
gutter : 0,

// initial stack effect
stack : true,

// delay between each option animation
delay : 0,

// random angle and positions for the options
random : false,

// rotated [right || left || false]: the options will be rotated to the right side or left side
// make sure to set the transform-origin in the style sheet
rotated : false,

// effect to slide in the options
// value is the margin to start with
slidingIn : false

The basic common styling is defined in the common.css file and you can see the individual styles in the styleN.css style sheets.

Demos

Check out the demos to see some different configurations in action:

Hope you enjoy this little plugin!

View demo Download source

StackSlider: A Fun 3D Image Slider

$
0
0

StackSlider: A Fun 3D Image Slider

View demo Download source

StackSlider is a highly experimental jQuery image slider plugin that explores a different and fun viewing concept for thumbnails, utilizing CSS 3D transforms and perspective. The idea is to navigate through two piles of images where the transition resembles an item being lifted off from the top of the stack and moved/rotated to the center of the container to be viewed. The previously viewed image falls on the opposite stack.

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

Please note that this is highly experimental and not fully tested. And obviously the 3D effect just works in supporting browsers. The fallback is simply showing and hiding the images when navigating. The effect looks smoothest in WebKit browsers.

For using the plugin, an unordered list with the following structure is needed:

<ul id="st-stack" class="st-stack-raw">
	<li>
		<div class="st-item">
			<a href="http://drbl.in/fgQV">
				<img src="images/1.jpg" />
			</a>
		</div>
		<div class="st-title">
			<h2>Graverobber</h2><h3>by Isaac Montemayor</h3>
		</div>
	</li>
	<li> <!-- ... --> </li>
	<li> <!-- ... --> </li>
	<li> <!-- ... --> </li>
	<!-- ... -->
</ul>

The class “st-stack-raw” is only being used to hide the list when JS is enabled. It can be used to style the list for a no JS fallback.

The plugin can be called like this:

$( '#st-stack' ).stackslider();

A new structure will be generated, containing the list with items, the two stacks, the title container and a navigation.

Options

The following default options are available:

// default transition speed
speed : 700,

// default transition easing
easing : 'ease-in-out',

// render both piles
piles : true

The styles are defined in the style.css file and we use media queries to adjust the size of the thumbnails and the positioning of the title and navigation. If you’d like to try it out, make sure to change the media queries to fit your container and image size.

View demo Download source

Fullscreen Pageflip Layout

$
0
0

Fullscreen Pageflip Layout with BookBlock

View demo Download source

We have received a couple of requests on how to use the BookBlock plugin in fullscreen. So, we decided to create a fullscreen layout, apply BookBlock to it and add a sidebar menu. We’ll show you how to customize BookBlock and use some available options for navigating the content.

The idea is to navigate the pages using the arrows or swiping the pages, and to slide out the menu when clicking on the menu button. The sidebar menu will contain links to different pages, i.e. the table of contents. When clicking on a table of contents entry, we’ll jump to the respective page.

We’ll also use jScrollPane by Kevin Luck to add a custom scrollbar for the content when needed.

Please note: CSS 3D transforms will only work in modern browsers that support those properties.

The demo contains excerpts form the hilarious “The Funny Side of Physic” by A. D. Crabtre from Project Gutenberg.

The following libraries and jQuery plugins will be used:

  1. BookBlock by Pedro Botelho
  2. Custom jQuery++ by Bitovi
  3. jScrollPane by Kevin Luck
  4. jQuery Mouse Wheel Plugin by Brandon Aaron
  5. Custom Modernizr (peek inside to see what this build includes)

So, let’s get started!

The Markup

Let’s have a main container for all our elements. Inside, we’ll add a division for the sidebar menu which we’ll give the class “menu-panel”, and the wrapper for the BookBlock with the class “bb-custom-wrapper”. The BookBlock will contain the wrapper (that we’ll apply the plugin to) and the structure that the plugin needs. Inside of each item, we’ll add a content wrapper with a division that we’ll need for the custom scroll functionality:

<div id="container" class="container">	

	<div class="menu-panel">
		<h3>Table of Contents</h3>
		<ul id="menu-toc" class="menu-toc">
			<li class="menu-toc-current"><a href="#item1">Self-destruction</a></li>
			<li><a href="#item2">Why we die</a></li>
			<li><a href="#item3">The honeymoon</a></li>
			<li><a href="#item4">A drawing joke</a></li>
			<li><a href="#item5">Commencing practice</a></li>
		</ul>
	</div>

	<div class="bb-custom-wrapper">

		<div id="bb-bookblock" class="bb-bookblock">

			<div class="bb-item" id="item1">
				<div class="content">
					<div class="scroller">
						<h2>Self-destruction</h2>
						<p>...</p>
					</div>
				</div><!-- /content -->
			</div><!-- /bb-item -->

			<div class="bb-item" id="item2"><!-- ... --></div>

			<div class="bb-item" id="item3"><!-- ... --></div>

			<div class="bb-item" id="item4"><!-- ... --></div>

			<div class="bb-item" id="item5"><!-- ... --></div>

		</div><!-- /bb-bookblock -->
		
		<nav>
			<a id="bb-nav-prev" href="#">←</a>
			<a id="bb-nav-next" href="#">→</a>
		</nav>

		<span id="tblcontents" class="menu-button">Table of Contents</span>

	</div><!-- /bb-custom-wrapper -->

</div><!-- /container -->

The menu items will point to the respective BookBlock pages (bb-item). We’ll also add navigation arrows and a button for toggling the opening and closing of the menu.

Let’s move on to the style.

The CSS

Note that we won’t use any vendor specific prefixes here, but you’ll find them in the files.

We don’t discuss the style of the BookBlock plugin here (you can find the styles in bookblock.css), instead we’ll focus on all the other styles that are important for the layout and customize a couple of things.

Let’s start by importing the Lato font from Google web fonts:

@import url(http://fonts.googleapis.com/css?family=Lato:300,400,700);

The html element should have a height of 100% since we will need to set the heights of absolute elements inside to expand to the window’s height:

html { 
	height: 100%; 
}

Let’s use border-box for box-sizing which will allow us to define percentages for widths and heights of elements while using paddings, without worrying about oversizing elements:

*,
*:after,
*:before {
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
	padding: 0;
	margin: 0;
}

(We are brutally resetting the paddings in margins of all elements; you don’t have to do that, instead you could just normalize the respective elements.)

Let’s define the font for the body and set it’s height to 100%. Remember, we need to set it because of the children that will be absolute and 100% in height as well:

body {
	font-family: 'Lato', Calibri, Arial, sans-serif;
	font-weight: 400;
	font-size: 100%;
	color: #333;
	height: 100%;
}

We are using Modernizr and we’ve added the class “no-js” to the html element. When JavaScript is enabled, Modernizr will replace that class with “js”. This will allow us to give certain CSS properties to elements that we don’t want if JavaScript is disabled. Our 100% width and height layout only makes sense if we have JS enabled and only then we want the body to have its overflow hidden:

.js body {
	overflow: hidden;
}

Let’s define some link styles:

a {
	color: #555;
	text-decoration: none;
}

a:hover {
	color: #000;
}

We want our main container to occupy all the window width and height. The sidebar menu will be positioned outside of it by setting a negative left value (of its own width). The idea is to animate the whole container when we click to open the menu. It will slide to the right revealing the overflown sidebar menu.

So, let’s set the main wrappers’ widths and heights to 100% and add a transition to the container:

.container,
.bb-custom-wrapper,
.bb-bookblock {
	width: 100%;
	height: 100%;
}

.container {
	position: relative;
	left: 0px;
	transition: left 0.3s ease-in-out;
}

When we click on the menu button, we will add another class to the container which will set the left to 240 pixels (width of the sidebar menu):

.slideRight {
	left: 240px;
}

Without JavaScript enabled, we won’t be able to do this, so let’s add a left padding instead:

.no-js .container {
	padding-left: 240px;
}

By default, we want the sidebar menu to be fixed to the left side:

.menu-panel {
	background: #f1103a;
	width: 240px;
	height: 100%;
	position: fixed;
	z-index: 1000;
	top: 0;
	left: 0;
	text-shadow: 0 1px 1px rgba(0,0,0,0.1);
}

When JS is enabled, we’ll set the position to absolute and the left to -240 pixel:

.js .menu-panel {
	position: absolute;
	left: -240px;
}

Let’s style the elements of the menu:

.menu-panel h3 {
	font-size: 1.8em;
	padding: 20px;
	font-weight: 300;
	color: #fff;
	box-shadow: inset 0 -1px 0 rgba(0,0,0,0.05);
} 

.menu-toc {
	list-style: none;
}

.menu-toc li a {
	display: block;
	color: #fff;
	font-size: 1.1em;
	line-height: 3.5;
	padding: 0 20px;
	cursor: pointer;
	background: #f1103a;
	border-bottom: 1px solid #dd1338;
}

.menu-toc li a:hover,
.menu-toc li.menu-toc-current a{
	background: #dd1338;
}

The navigation will be positioned absolutely on top of everything:

.bb-custom-wrapper nav {
	top: 20px;
	left: 60px;
	position: absolute;
	z-index: 1000;
}

The arrow links and the menu button will also be positioned absolutely and we’ll make them round by setting the border-radius to 50%:

.bb-custom-wrapper nav span,
.menu-button {
	position: absolute;
	width: 32px;
	height: 32px;
	top: 0;
	left: 0;
	background: #f1103a;
	border-radius: 50%;
	color: #fff;
	line-height: 30px;
	text-align: center;
	speak: none;
	font-weight: bold;
	cursor: pointer;
}

.bb-custom-wrapper nav span:last-child {
	left: 40px;
}

.bb-custom-wrapper nav span:hover,
.menu-button:hover {
	background: #000;
}

The menu button will be positioned in the top left corner and we’ll hide its text:

.menu-button {
	z-index: 1000;
	left: 20px;
	top: 20px;
	text-indent: -9000px;
}

Let’s create a little menu icon by using a pseudo-element with a double box shadow for the upper and lower line:

.menu-button:after {
	position: absolute;
	content: '';
	width: 50%;
	height: 2px;
	background: #fff;
	top: 50%;
	margin-top: -1px;
	left: 25%;
	box-shadow: 0 -4px #fff, 0 4px #fff;
}

In case that there is no JS enabled, we don’t need any of those elements, so we’ll simply hide them:

.no-js .bb-custom-wrapper nav span,
.no-js .menu-button {
	display: none;
}

Let’s move to the inner parts of the BookBlock items. The content division needs to be absolute and we’ll set the overflow to hidden. This is important because we want to apply our custom scroll here and we’ll only do that when a page was turned. If we wouldn’t set the overflow to hidden, we’d see the content overflowing. Again, this only makes sense when we have JS enabled, so we’ll add the “js” class:

.js .content {
	position: absolute;
	top: 60px;
	left: 0;
	bottom: 50px;
	width: 100%;
	overflow: hidden;
}

The scroller div is the one that will grow with content, so let’s set some paddings here:

.scroller {
	padding: 10px 5% 10px 5%;
}

Using percentages as lateral padding will make the layout adjust liquidly to the screen size.

Let’s hide those sharp edges when we scroll by adding pseudo-elements with a white to transparent gradient to the top and the bottom of the content div:

.js .content:before,
.js .content:after {
	content: '';
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 20px;
	z-index: 100;
	pointer-events: none;
	background: 
		linear-gradient(
			to bottom, 
			rgba(255,255,255,1) 0%, 
			rgba(255,255,255,0) 100%
		);
}

.js .content:after {
	top: auto;
	bottom: 0;
	background: 
		linear-gradient(
			to top, 
			rgba(255,255,255,1) 0%, 
			rgba(255,255,255,0) 100%
		);
}

This will make the text appear slightly faded out.

Let’s style the text elements:

.content h2 {
	font-weight: 300;
	font-size: 4em;
	padding: 0 0 10px;
	color: #333;
	margin: 0 1% 40px;
	text-align: left;
	box-shadow: 0 10px 0 rgba(0,0,0,0.02);
	text-shadow: 0 0 2px #fff;
}

.no-js .content h2 {
	padding: 40px 1% 20px;
}

.content p {
	font-size: 1.2em;
	line-height: 1.6;
	font-weight: 300;
	padding: 5px 8%;
	text-align: justify;
}

Finally, let’s add some media queries. Without JavaScript enabled, we don’t want to show the menu anymore from 800 pixels on. This is just an example of how we can control these elements under certain conditions.

The last media query will resize the font a bit, for smaller devices. Using ems makes things easier for these cases.


@media screen and (max-width: 800px){
	.no-js .menu-panel {
		display: none;
	}

	.no-js .container {
		padding: 0;
	}
}

@media screen and (max-width: 400px){
	.menu-panel,
	.content {
		font-size: 75%;
	}
}

And that was all the style! Let’s add some funky dance moves, shall we?

The JavaScript

We will start by caching some elements and initializing the BookBlock plugin. We need to set some things after each page flip, mainly the current item’s index and the jScrollPane behavior. This is specified in the onEndFlip callback passed to the BookBlock.

var $container = $( '#container' ),

	// the element we will apply the BookBlock plugin to
	$bookBlock = $( '#bb-bookblock' ),

	// the BookBlock items (bb-item)
	$items = $bookBlock.children(),

	// index of the current item
	current = 0,

	// initialize the BookBlock
	bb = $( '#bb-bookblock' ).bookblock( {
		speed : 800,
		perspective : 2000,
		shadowSides	: 0.8,
		shadowFlip	: 0.4,
		// after each flip...
		onEndFlip : function(old, page, isLimit) {
			
			// update the current value
			current = page;

			// update the selected item of the table of contents (TOC)
			updateTOC();

			// show and/or hide the navigation arrows
			updateNavigation( isLimit );

			// initialize the jScrollPane on the content div for the new item
			setJSP( 'init' );

			// destroy jScrollPane on the content div for the old item
			setJSP( 'destroy', old );

		}
	} ),
	// the navigation arrows
	$navNext = $( '#bb-nav-next' ),
	$navPrev = $( '#bb-nav-prev' ).hide(),

	// the table of content items
	$menuItems = $container.find( 'ul.menu-toc > li' ),

	// button to open the TOC
	$tblcontents = $( '#tblcontents' ),

	transEndEventNames = {
		'WebkitTransition': 'webkitTransitionEnd',
		'MozTransition': 'transitionend',
		'OTransition': 'oTransitionEnd',
		'msTransition': 'MSTransitionEnd',
		'transition': 'transitionend'
	},

	// transition event name
	transEndEventName = transEndEventNames[Modernizr.prefixed('transition')],

	// check if transitions are supported
	supportTransitions = Modernizr.csstransitions;

First, let’s bind the events to some of the elements initialized before. Also, we need to initialize the jScrollPane for the first (current) item.

function init() {

	// initialize jScrollPane on the content div of the first item
	setJSP( 'init' );
	initEvents();

}

Since we’ll eventually need to initialize, reinitialize and destroy the jScrollPane, let’s define a function for this:

function setJSP( action, idx ) {
		
	var idx = idx === undefined ? current : idx,
		$content = $items.eq( idx ).children( 'div.content' ),
		apiJSP = $content.data( 'jsp' );
	
	if( action === 'init' && apiJSP === undefined ) {
		$content.jScrollPane({verticalGutter : 0, hideFocus : true });
	}
	else if( action === 'reinit' && apiJSP !== undefined ) {
		apiJSP.reinitialise();
	}
	else if( action === 'destroy' && apiJSP !== undefined ) {
		apiJSP.destroy();
	}

}

We will need to bind several events:

  1. We will call the BookBlock’s next() and prev() methods when we click the navigation arrows or when swiping the page
  2. The TOC will be shown / hidden when we click the $tblcontents (menu) button
  3. We will call the BookBlock jump() method when we click a TOC item
  4. The jScrollPane will be initialized on window resize

So, here we go:

function initEvents() {

	// add navigation events
	$navNext.on( 'click', function() {
		bb.next();
		return false;
	} );

	$navPrev.on( 'click', function() {
		bb.prev();
		return false;
	} );
	
	// add swipe events
	$items.on( {
		'swipeleft'		: function( event ) {
			if( $container.data( 'opened' ) ) {
				return false;
			}
			bb.next();
			return false;
		},
		'swiperight'	: function( event ) {
			if( $container.data( 'opened' ) ) {
				return false;
			}
			bb.prev();
			return false;
		}
	} );

	// show TOC
	$tblcontents.on( 'click', toggleTOC );

	// click a menu item
	$menuItems.on( 'click', function() {

		var $el = $( this ),
			idx = $el.index(),
			jump = function() {
				bb.jump( idx + 1 );
			};
		
		current !== idx ? closeTOC( jump ) : closeTOC();

		return false;
		
	} );

	// reinit jScrollPane on window resize
	$( window ).on( 'debouncedresize', function() {
		// reinitialise jScrollPane on the content div
		setJSP( 'reinit' );
	} );

}

The navigation arrows’ visibility will depend on the current page. If we are on the first page we’ll only see the next arrow and if we are on the last page we’ll only see the previous arrow:

function updateNavigation( isLastPage ) {
	
	if( current === 0 ) {
		$navNext.show();
		$navPrev.hide();
	}
	else if( isLastPage ) {
		$navNext.hide();
		$navPrev.show();
	}
	else {
		$navNext.show();
		$navPrev.show();
	}

}

When we open the TOC we want to hide the navigation arrows and we just show them again after closing the TOC.
We will animate the sidebar menu with a CSS transition. If there’s no support for transitions then a simple show/hide fallback will be used:

function toggleTOC() {
	var opened = $container.data( 'opened' );
	opened ? closeTOC() : openTOC();
}

function openTOC() {
	$navNext.hide();
	$navPrev.hide();
	$container.addClass( 'slideRight' ).data( 'opened', true );
}

function closeTOC( callback ) {

	$navNext.show();
	$navPrev.show();
	$container.removeClass( 'slideRight' ).data( 'opened', false );
	if( callback ) {
		if( supportTransitions ) {
			$container.on( transEndEventName, function() {
				$( this ).off( transEndEventName );
				callback.call();
			} );
		}
		else {
			callback.call();
		}
	}

}

Phew! :) That’s it! I hope you enjoyed this tutorial and find it useful!

View demo Download source

3D Book Showcase

$
0
0

3DBookShowcase_Main

View demo Download source

Today we want to share an experimental book showcase concept with you. The idea is to make books look more realistic by using 3D transforms and apply some fun effects to them, like rotating, flipping and opening. This might be an interesting concept for online book stores as it adds some interactivity the user might find entertaining. The idea is taken from Lift Interactive (scroll down to see the book style).

Note that this only works in browsers that support CSS 3D transforms. For other browsers, we simply show the book cover.

The demos are best viewed in WebKit browsers.

3DBookShowcase

What we did was to build a structure that we can transform into a 3D object with CSS 3D transforms. Our book has six main sides and one inner page element that we’ll use to paginate through some content, simulating something like a “view inside” functionality. We could have used the BookBlock jQuery plugin for flipping through the pages but we didn’t want to overload it with too many effects.

A book is build up as follows:

<div class="bk-book">

	<div class="bk-front">
		<div class="bk-cover">
			<h2>
				<span>Anthony Burghiss</span>
				<span>A Catwork Orange</span>
			</h2>
		</div>
		<div class="bk-cover-back"></div>
	</div>

	<div class="bk-page">
		<div class="bk-content bk-content-current">
			<p>Red snapper Kafue pike fangtooth humums slipmouth, salmon cutlassfish; swallower European perch mola mola sunfish, threadfin bream. Billfish hog sucker trout-perch lenok orbicular velvetfish. Delta smelt striped bass, medusafish dragon goby starry flounder cuchia round whitefish northern anchovy spadefish merluccid hake cat shark Black pickerel. Pacific cod.</p>
		</div>
		<div class="bk-content">
			<!-- ... -->
		</div>
		<div class="bk-content">
			<!-- ... -->
		</div>
	</div>

	<div class="bk-back">
		<p>In this nightmare vision of cats in revolt, fifteen-year-old Alex and his friends set out on a diabolical orgy of robbery, rape, torture and murder. Alex is jailed for his teenage delinquency and the State tries to reform him - but at what cost?</p>
	</div>

	<div class="bk-right"></div>

	<div class="bk-left">
		<h2>
			<span>Anthony Burghiss</span>
			<span>A Catwork Orange</span>
		</h2>
	</div>

	<div class="bk-top"></div>

	<div class="bk-bottom"></div>
</div>

The class names are based on the sides of a book when holding it and looking at the front, the cover. Since we want to open the book cover, we need to give the front a main cover side and a back side. The back of the book will also contain some content which will be visible when flipping the book.

When hovering over a book, we will rotate it slightly.

3DBookShowcase2

Specifically, we rotate the whole book 35 degrees on the Y-axis:

.bk-list li .bk-book.bk-bookdefault:hover {
	transform: rotate3d(0,1,0,35deg);
}

When opening the book by clicking on “View inside”, the front part will flip open and we can navigate through the preview pages by clicking on the arrows.

3DBookShowcase3

The rotations and flipping are done by applying certain classes:

/* Transform classes */

.bk-list li .bk-viewinside .bk-front {
	transform: translate3d(0,0,20px) rotate3d(0,1,0,-160deg);
}

.bk-list li .bk-book.bk-viewinside {
	transform: translate3d(0,0,150px) rotate3d(0,1,0,0deg);
}

.bk-list li .bk-book.bk-viewback {
	transform: translate3d(0,0,0px) rotate3d(0,1,0,180deg);
}

In the second demo, we rotate the book so that we can only see the spine, the left side. On hover we simulate the familiar movement of taking a quick look of a book by slightly moving it towards us and rotating it. When clicking on it we will open it.

3DBookShowcase4

A second click on the book will close it again and put it back into the shelf.

Note that this is really just a concept and highly experimental. It’s probably very buggy and has much room for improvement. But anyway, we hope you enjoyed this experiment and find it inspiring!

View demo Download source

3D Image Gallery Room

$
0
0

3D Gallery Room

View demo Download source

Today we want to share another 3D experiment with you: a gallery room in 3D. The idea is to create a realistic environment for an image exposition using CSS 3D transforms. Images are “hanged” along a wall which will have an end. Once the end of the wall is reached, a rotation will happen and we’ll be turned to the next wall whith more images. To give a real sensation of being in a room, we only have four walls and corners. Each image will have a little description tag with a small-sized font and when clicking on it, a larger version will appear from below.

Please note that this is highly experimental and probably very buggy.

We recommend to view the demos in Google Chrome, they perform best there.

We use the following initial structure for adding figures and their captions:

<div id="gr-gallery" class="gr-gallery">

	<div class="gr-main">

		<figure>
			<div><img src="images/11.jpg" alt="img01" /></div>
			<figcaption>
				<h2><span>Penn. Station, Madison Square Garden and Empire State Building</span></h2>
				<div><p>New York City, 2009, by <a href="http://www.flickr.com/photos/thomasclaveirole">Thomas Claveirole</a></p></div>
			</figcaption>
		</figure>

		<figure>
			<!-- -->
		</figure>

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

We will first transform it into the following structure that will contain a “room” with a main “wall”:

<div id="gr-gallery" class="gr-gallery">

	<div class="gr-main" style="display: none;">
		<!-- -->
	</div>

	<div class="gr-room">
		<div class="gr-wall-main">
			<div class="gr-floor" style="width: 3382px;"></div>
			<figure></figure>
			<figure></figure>
			<!-- -->
		</div>
	</div>

	<nav>
		<span class="gr-prev">prev</span>
		<span class="gr-next">next</span>
	</nav>

	<div class="gr-caption">
		<span class="gr-caption-close">x</span>
	</div>
	
</div><!-- /gr-gallery -->

The main wall will get a width so that a certain amount of images fits inside. The default number of images per wall are the total number divided by four since we have four walls to spread them on. We can also set the number of items per wall by initializing the layout array, as see in the demos:

Gallery.init( {
	layout : [3,2,3,2]
} );

Now, when we reach the end of a wall and rotate to see the next one, we’ll add another wall dynamically and set the right transforms so that it’s in the right angle towards the main wall.

Let’s take a look at some screenshots. The gallery starts by looking at the first image which will be centered in the viewport. With a very large screen, we will be able to see the neighboring images as well, depending on the wall size of course:

3DGalleryRoom_01

When clicking on the little description, a larger version will slide in from the bottom of the screen:
3DGalleryRoom_02

When clicking on a navigation arrow (we click to go to the right) we will move to the next or previous picture. When the end of the wall is reached, we’ll give a turn:
3DGalleryRoom_03

The next image is placed into another wall a positioned accordingly:
3DGalleryRoom_04

When we turn fully, the walls will be reset and we’ll again have only the main wall:
3DGalleryRoom_05

The biggest problem for these kind of extreme experiments is the different handling of 3D transforms in the browsers and of course the performance. When playing with extreme widths and perspective, there can be glitches in viewing because an element might be rotated “right into your face” :) The larger the element is in width, the higher the perspective value needs to be (at least for Firefox).

Sadly, the performance suffers when adding something like a box shadow, so we have stripped the demos down to their bare style and not added too much fanciness. It does look a bit ugly, but we simply wanted to show the 3D effect and the smoothness is more important for us in this experiment.

Anyway, we hope you enjoyed this little experiment and find it inspiring!

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

Image Techniques for Creating Depth in Web Design

$
0
0

Image Techniques for Creating Depth in Web Design

We live in a three-dimensional world, something that designers are trying to recreate in web design every day. The element of depth can add a greater touch of reality to certain design projects.

Creating it can be easier than you think. The key to simulating reality on the 2D screen is by creating depth using images. This can be accomplished in a variety of ways, from the photography itself, to layering of images, manipulation and the use of special effects.

Layering

rangerover

mikedascola

adlucent

gardner

matteo

Layering is a tool that can be intentionally used in the way images and objects are placed in the design process, as a tool to manipulate images or purposefully in the image itself.

We’ve all seen the layout where images are placed on top of each other haphazardly to look like a stack of printed photos. You know the trick. And it works by creating a sense of depth that makes you feel as if you are thumbing through the photographs. This is layering in the design process. Sometimes the effect is more obvious, as in this instance but other times it is more hidden.

Often multiple photos are merged into one to create two distinct parts of an image – a foreground and another background. When done well, the effect should be virtually unseen. Take a look at the Range Rover site above – both the car and the city images are spectacular and when combined they make you feel as if you are standing next to the vehicle looking over the city.

Another effective layering tool is laying a photograph or image over a seemingly flat background. This works fantastically with odd orientations or angled images and backgrounds that have graduated color.

Finally, some images are created with layering in mind. This photography technique is based on the idea of using overlapping lines created by different objects in the image to create depth. Often lines are emphasized by color and contrast in the image to separate objects in the scene from one another. This technique can be difficult for amateur photographers to create.

Photography

ermenegildo

ownedition

worth

charmer

hytter

draught

One of the most important tools for creating depth is in the images. Framing and composing images with depth in mind can make building for depth a breeze.

But how do you compose photos in this manner? While many opt to hire a professional photographer and just provide instructions for depth, this is not always an option. If you are taking photos yourself for a project think about depth as you shoot with these tips.

Create a visual interest in the foreground. Make sure the photo has a subject and background. Thinking about these as separate parts will help you better frame and create an image.

Change the view. Take a photo from an unexpected angle. This changes the perspective of both the subject and background, adding visual dimension. You can also accomplish this by playing with angles; frame the same image from a comfortable level and with a wider angle.

Take photos in portrait orientation. By changing to a vertical axis, you will frame photos differently.

Think about natural lines and shadows. Sometimes what’s in the photo adds its own sense of depth. Distinct shadows, for example, can make an object come into sharper focus while extending the background.

Image Manipulation

Using photo editing software such as Adobe Photoshop, designers can create added depth in photos with a few simple tricks. This can be a great alternative when working with portraits or other images that were not shot with depth in mind.

The trick is this: With the subject in the foreground, add a blur to the background, while leaving the subject alone. (In Photoshop, a Gaussian blur adjusted to a radius of 5.0 works wonders.)
This simple trick adds focus to the subject in an image while creating a sense of depth by making the background less of a visual priority.

Shadow Effects

guestd

upstate

toykomilk

oyyo

Shadows are one of the easiest and most natural ways to create a sense of depth and place for objects and images. Placement, darkness and the shape of shadows can imply very specific meanings. They key for using shadows is for them to look natural.

The best shadows are subtle and follow patterns of light. Make sure shadows mirror the feel of images they accompany. If the first thing a user sees is the shadow, then it probably does not work.

Rounded shadows that rest below objects can create a floating, 3D-style effect. This is a common technique for buttons and smaller user interface elements. Shape-specific shadows in front of objects can make a flat item look like it is standing up, such as those on the Guest’d site. (Did you even notice the shadow before they were mentioned?) Warped shadows – those that do not mirror the shape of the image they relate to – can make an item appear to have another shape, or lift off a background image.

Shadows can also add a touch of realism to an object or item. A shadow behind a person can help lift him off the background, bringing the subject to the forefront of the image. This is a common technique but it is very important that shadows fall opposite of light sources in the image so that the end result appears natural and unforced.

Illustrations and Other Images

emotions

flourish

dascola

octi

The same concept and manipulations that work on photographs also work for illustrations and other images used in design projects. The difference is that you often have to create these effects yourself.

The dramatic shadow used above is a great example of this. The light and dark parts of the background relate specifically to the image, making it “move forward” from the rest of the site.

Conclusion

Depth is a great tool for creating a sense of realism in design projects. It can connect users to what they are viewing on the screen, making them feel like more of a part of the scene.

Even images, from photographs to vector-based user interface elements, that lack true depth can be manipulated to add a sense of having a third dimension. Layering, object orientation and shadows are some of the best tools for creating extra depth.

HexaFlip: A Flexible 3D Cube Plugin

$
0
0

HexaFlip

View demo Download source

Today I’d like to share my process for creating a flexible JavaScript UI plugin I’ve dubbed HexaFlip.

Lately I’ve taken to building simple experimental UI plugins that manipulate elements with a combination of JavaScript and modern CSS techniques (e.g. oriDomi and Maskew). As Codrops is known for featuring some progressive techniques in browser-based UI design, HexaFlip should fit in nicely.

I originally developed a simpler version of HexaFlip for an iPhone app I built called ChainCal where it served as a time-picker interface for setting alarms. Most mobile time-picker widgets are fashioned after a dial, but I reasoned that rotating cubes would serve for a more unique and memorable experience. As we all know, a cube has six (i.e. “hexa”) faces, but when rotating it around a single axis, we only have four to work with (front, top, back, and bottom). Thus if we built a cube interface using CSS alone, our interface would be limited to four options per choice. HexaFlip solves this issue and playfully challenges the user’s expectations by allowing the cube to cycle over a list of any length.

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

The Markup

Since HexaFlip is designed to be used as a plugin, it generates its own markup based on the options given to it. For each demo, we only require a single element:

<div id="hexaflip-demo1" class="demo"></div>

The id and class are for convenience, should we want to add specific styles to our instance.

The CSS

(Note: For brevity, the following snippets don’t use any vendor prefixes, though the included source does.)

Inside every element passed to a HexaFlip instance, HexaFlip automatically builds markup for cubes (classed with hexaflip-cube) based on the number of option sets supplied to it.

.hexaflip-cube {
  font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;
  text-rendering: optimizeLegibility;
  font-smoothing: antialiased;
  cursor: move;
  cursor: grab;
  display: inline-block;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 0.4s;
}

To prompt the user to manipulate the cube with the mouse, we set a cursor property on it. The reason both move and grab are set is because grab is currently vendor-specific and we need a fallback. display is set to inline-block so the cubes sit next to each other and we set position: relative to allow the JS to later set the z-index. transform-style is especially important for the use case of a cube because each of the six faces are independently manipulated in their own 3D space. Without setting preserve-3d, the cubes would appear flat. Finally we set a transition property on cubes so their rotations can be tweened.

Next we have a class called no-tween that is automatically toggled on the cubes by the JS when the user moves over a face with either a mouse button or finger down.

.hexaflip-cube.no-tween {
  transition-duration: 0;
}

This class simply disables tweening in that context so movement is immediate during interaction.

Each cube’s faces are simply nested divs so we use an immediate child selector (>) to style them.

.hexaflip-cube > div {
  width: 100%;
  overflow: hidden;
  height: 100.5%;
  position: absolute;
  user-select: none;
  background-size: cover;
  text-align: center;
  background-color: #333;
  color: #fff;
  font-weight: 100;
  text-shadow: 0 -2px 0 rgba(0,0,0,0.3);
  line-height: 1.5;
}

You may notice that the height is set to 100.5%. This is done to add some extra “bleed” to the size of each face to mitigate a common issue seen during 3D CSS transforms where edges don’t match up perfectly and “cracks” appear in the object. Each face is given absolute positioning so they stack on top of each other before they’re transformed in 3D space. We also give them user-select: none so the text of the face isn’t accidentally selected when dragging the mouse to rotate the cube. background-size: cover is applied so that if the user wants to display images on the cube, they fill the entire face without distorting either dimension. The rest of the CSS properties are simply stylistic and can be overridden if you’d like to customize your HexaFlip instance.

These classes refer to the side faces that aren’t displayed directly toward the user. They’re given a gray color by default:

.hexaflip-left, .hexaflip-right {
  background-color: #555 !important;
}

True to its roots, HexaFlip can still be used as a time-picker and when used in this mode, a specific class is applied. This final CSS definition simply colors alternating faces red (as they originally appeared in ChainCal) using the :nth-child(odd) pseudo-class.

.hexaflip-timepicker .hexaflip-cube:last-child > div:nth-child(odd) {
  background-color: #ff575b;
}

The JavaScript

(Note: HexaFlip was originally written in CoffeeScript and the original source is included in the download. For the purposes of this article I’ll be walking through the generated JavaScript.)

We start by defining an immediately invoked function to create a new scope context so that we don’t touch the global environment. This is especially good practice when building a plugin designed for integration into other developers’ projects.

(function() {
	//...
}).call(this);

After defining some variables, we must tackle the issue of detecting CSS feature support and which vendor prefix to use for properties. The following section defines a function to cycle through the major vendor prefixes to find a compatible match. If no match is found, the function returns false to denote the browser’s lack of support for that property.

prefixList = ['webkit', 'Moz', 'O', 'ms'];

prefixProp = function(prop) {
var prefix, prefixed, _i, _len;
if (document.body.style[prop.toLowerCase()] != null) {
  return prop.toLowerCase();
}
for (_i = 0, _len = prefixList.length; _i < _len; _i++) {
  prefix = prefixList[_i];
  prefixed = prefix + prop;
  if (document.body.style[prefixed] != null) {
    return prefixed;
  }
}
return false;

For our purposes we need to test two specific CSS3 properties (transform and perspective) and store them in an object literal simply called css:

css = {};

_ref = ['Transform', 'Perspective'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  prop = _ref[_i];
  css[prop.toLowerCase()] = prefixProp(prop);
}

Next we have a set of default options for new HexaFlip instances. When a user doesn't supply a specific option when creating a new HexaFlip instance, the default value in this object is used:

defaults = {
  size: 280,
  margin: 10,
  fontSize: 185,
  perspective: 1000,
  touchSensitivity: 1
};

Additionally we define some static properties that apply to all instances of HexaFlip:

cssClass = baseName.toLowerCase();

faceNames = ['front', 'bottom', 'back', 'top', 'left', 'right'];

faceSequence = faceNames.slice(0, 4);

urlRx = /^((((https?)|(file)):)?\/\/)|(data:)|(\.\.?\/)/i;

urlRx is a simple regular expression that we'll later use to test if a value is a URL. If the string begins with http, https, file, data:, //, ./, or ../, HexaFlip assumes it's a URL and loads its image on the cube face.

Once we have that bootstrapping out of the way, we can define HexaFlip's behavior via its constructor and prototype. If you're not familiar with this pattern, we're going to create a function that builds every instance of HexaFlip. By attaching properties to this function's prototype object, we define pieces of functionality for every instance that's created in the future. Underscore-prefixed properties denote that the property is only meant to be used internally by HexaFlip, and the user typically shouldn't need to access them.

The constructor itself accepts three arguments: the target DOM element, an object literal containing sets to display, and an optional object literal of customizations to override the defaults map. These arguments are attached to the instance itself (this):

function HexaFlip(el, sets, options) {
  var cube, cubeFragment, i, image, key, midPoint, option, set, setsKeys, setsLength, val, value, z, _j, _len1, _ref1, _ref2;
  this.el = el;
  this.sets = sets;
  this.options = options != null ? options : {};

For every key in sets, a new cube will be created. The value of each key should be an array of values to be displayed on the cube faces.

Before continuing, the constructor checks to see if CSS transforms are supported and if an element was passed. If either test fails, the constructor immediately returns.

  if (!(css.transform && this.el)) {
    return;
  }

This block fills in any missing options with the defaults we defined earlier:

for (option in defaults) {
    value = defaults[option];
    this[option] = (_ref1 = this.options[option]) != null ? _ref1 : defaults[option];
  }
  if (typeof this.fontSize === 'number') {
    this.fontSize += 'px';
  }

If the user doesn't pass any sets, we will continue with setting up this instance as a time-picker. The following block contains some simple loops that populate the sets with hours and minutes in intervals of ten:

if (!this.sets) {
  this.el.classList.add(cssClass + '-timepicker');
  this.sets = {
    hour: (function() {
      var _j, _results;
      _results = [];
      for (i = _j = 1; _j <= 12; i = ++_j) {
        _results.push(i + '');
      }
      return _results;
    })(),
    minute: (function() {
      var _j, _results;
      _results = [];
      for (i = _j = 0; _j <= 5; i = ++_j) {
        _results.push(i + '0');
      }
      return _results;
    })(),
    meridian: ['am', 'pm']
  };
}

Next, we loop over the sets and perform a number of operations. For the primary task, we create an object for each cube using _createCube (which will be explained momentarily) and append their elements to a document fragment. Using the total number of given sets, we give the cube elements a sequence of z indexes so they stack properly. By splitting the sets length in half, we increment the indexes for the first half of the set and decrement them passing the midpoint. Due to 3D perspective, the view of the cubes is only straight ahead toward the center of the container element, and towards the edges, the cubes' sides are visible. If we didn't perform this stacking manipulation, the cubes in the latter half of the sets would sit on top of each other incorrectly and the illusion would be thrown off.

setsKeys = Object.keys(this.sets);
setsLength = setsKeys.length;
cubeFragment = document.createDocumentFragment();
i = z = 0;
midPoint = setsLength / 2 + 1;
this.cubes = {};
_ref2 = this.sets;
for (key in _ref2) {
  set = _ref2[key];
  cube = this.cubes[key] = this._createCube(key);
  if (++i < midPoint) {
    z++;
  } else {
    z--;
  }
  cube.el.style.zIndex = z;
  this._setContent(cube.front, set[0]);
  cubeFragment.appendChild(cube.el);
  for (_j = 0, _len1 = set.length; _j < _len1; _j++) {
    val = set[_j];
    if (urlRx.test(val)) {
      image = new Image;
      image.src = val;
    }
  }
}

In the conclusion of that loop you'll notice another loop that iterates over the values in a set and uses the regular expression defined earlier to check for URLs. If there's a match, we can assume the user has passed an image and we construct a new image object in memory to force the browser to preload it. The goal is that images aren't loaded on demand when the end user spins the cube as that would degrade the experience.

The constructor concludes its work by setting a correct height, width, and perspective for the container element and appends the cubes.

this.cubes[setsKeys[0]].el.style.marginLeft = '0';
this.cubes[setsKeys[setsKeys.length - 1]].el.style.marginRight = '0';
this.el.classList.add(cssClass);
this.el.style.height = this.size + 'px';
this.el.style.width = ((this.size + this.margin * 2) * setsLength) - this.margin * 2 + 'px';
this.el.style[css.perspective] = this.perspective + 'px';
this.el.appendChild(cubeFragment);

Next, let's look at our first method, previously used in the constructor:

HexaFlip.prototype._createCube = function(set) {
  var cube, eString, eventPair, eventPairs, rotate3d, side, _fn, _j, _k, _l, _len1, _len2, _len3,
    _this = this;
  cube = {
    set: set,
    offset: 0,
    y1: 0,
    yDelta: 0,
    yLast: 0,
    el: document.createElement('div')
  };
  cube.el.className = "" + cssClass + "-cube " + cssClass + "-cube-" + set;
  cube.el.style.margin = "0 " + this.margin + "px";
  cube.el.style.width = cube.el.style.height = this.size + 'px';
  cube.el.style[css.transform] = this._getTransform(0);

Each cube is just an object literal that holds properties including a DOM element. To create a three dimensional cube from six flat divs, we loop through the faces and apply a specific style setting for rotational axis and angle based on the face name:

for (_j = 0, _len1 = faceNames.length; _j < _len1; _j++) {
  side = faceNames[_j];
  cube[side] = document.createElement('div');
  cube[side].className = cssClass + '-' + side;
  rotate3d = (function() {
    switch (side) {
      case 'front':
        return '0, 0, 0, 0deg';
      case 'back':
        return '1, 0, 0, 180deg';
      case 'top':
        return '1, 0, 0, 90deg';
      case 'bottom':
        return '1, 0, 0, -90deg';
      case 'left':
        return '0, 1, 0, -90deg';
      case 'right':
        return '0, 1, 0, 90deg';
    }
  })();
  cube[side].style[css.transform] = "rotate3d(" + rotate3d + ") translate3d(0, 0, " + (this.size / 2) + "px)";
  cube[side].style.fontSize = this.fontSize;
  cube.el.appendChild(cube[side]);
}

Finally, _createCube attaches event listeners for both, mouse and touch interaction and returns the cube object:

eventPairs = [['TouchStart', 'MouseDown'], ['TouchMove', 'MouseMove'], ['TouchEnd', 'MouseUp'], ['TouchLeave', 'MouseLeave']];
  mouseLeaveSupport = 'onmouseleave' in window;
  for (_k = 0, _len2 = eventPairs.length; _k < _len2; _k++) {
    eventPair = eventPairs[_k];
    _fn = function(fn, cube) {
      if (!((eString === 'TouchLeave' || eString === 'MouseLeave') && !mouseLeaveSupport)) {
        return cube.el.addEventListener(eString.toLowerCase(), (function(e) {
          return _this[fn](e, cube);
        }), true);
      } else {
        return cube.el.addEventListener('mouseout', (function(e) {
          return _this._onMouseOut(e, cube);
        }), true);
      }
    };
    for (_l = 0, _len3 = eventPair.length; _l < _len3; _l++) {
      eString = eventPair[_l];
      _fn('_on' + eventPair[0], cube);
    }
  }
  this._setSides(cube);
  return cube;
};

The next method is relied on by a few other methods and performs some simple string concatenation for creating CSS transform values:

HexaFlip.prototype._getTransform = function(deg) {
  return "translateZ(-" + (this.size / 2) + "px) rotateX(" + deg + "deg)";
};

The reason we set a negative Z value for the translate operation is because the front face is extended toward the user in 3D space and its texture would appear somewhat blurry otherwise.

Next we have _setContent which accepts a cube face element and a value to display on the face:

HexaFlip.prototype._setContent = function(el, content) {
  var key, style, val, value;
  if (!(el && content)) {
    return;
  }
  if (typeof content === 'object') {
    style = content.style, value = content.value;
    for (key in style) {
      val = style[key];
      el.style[key] = val;
    }
  } else {
    value = content;
  }
  if (urlRx.test(value)) {
    el.innerHTML = '';
    return el.style.backgroundImage = "url(" + value + ")";
  } else {
    return el.innerHTML = value;
  }
};

For the sake of flexibility, HexaFlip allows the user to pass objects within set arrays so any value can have a specific styling. When a value is an object (rather than a string or number), we loop through the style property's pairs of CSS keys and values and apply them to the face. This means you could supply a set like this

[
	'hello',
 	{
 		value: 'i am green',
 		style: {
 			backgroundColor: '#00ff00'
 		}
 	}
]

where the first element (displaying "hello") would have default styling, but the second would always appear with a green background.

Finally _setContent checks for a URL value and sets the background image accordingly.

_setSides is the most important method in HexaFlip as it maps the values in a set to the four active faces of a cube:

HexaFlip.prototype._setSides = function(cube) {
  var bottomAdj, faceOffset, offset, set, setLength, setOffset, topAdj;
  cube.el.style[css.transform] = this._getTransform(cube.yDelta);
  cube.offset = offset = Math.floor(cube.yDelta / 90);
  if (offset === cube.lastOffset) {
    return;
  }
  cube.lastOffset = faceOffset = setOffset = offset;
  set = this.sets[cube.set];
  setLength = set.length;
  if (offset < 0) {
    faceOffset = setOffset = ++offset;
    if (offset < 0) {
      if (-offset > setLength) {
        setOffset = setLength - -offset % setLength;
        if (setOffset === setLength) {
          setOffset = 0;
        }
      } else {
        setOffset = setLength + offset;
      }
      if (-offset > 4) {
        faceOffset = 4 - -offset % 4;
        if (faceOffset === 4) {
          faceOffset = 0;
        }
      } else {
        faceOffset = 4 + offset;
      }
    }
  }
  if (setOffset >= setLength) {
    setOffset %= setLength;
  }
  if (faceOffset >= 4) {
    faceOffset %= 4;
  }
  topAdj = faceOffset - 1;
  bottomAdj = faceOffset + 1;
  if (topAdj === -1) {
    topAdj = 3;
  }
  if (bottomAdj === 4) {
    bottomAdj = 0;
  }
  this._setContent(cube[faceSequence[topAdj]], set[setOffset - 1] || set[setLength - 1]);
  return this._setContent(cube[faceSequence[bottomAdj]], set[setOffset + 1] || set[0]);
};

In a nutshell, this method calculates the number of times a cube has been rotated from its initial state (zero degrees) and transposes that number to a position in that cube's array. This method handles a number of cases such as if the number of values in the set exceeds the number of faces, if the number of rotations exceeds the length of the set, and backwards rotations as well. The key to the illusion of showing more than four values as the user rotates lies in deriving the current topAdj and bottomAdj or top and bottom adjacent sides. These sides are relative to the side currently facing the user and since they aren't visible at the moment they sit at the top and bottom, their content can be immediately swapped without tipping off the user to our trick. With this strategy in mind, our code can make sure the adjacent top and bottom sides will always be the previous and next values in the array.

Next, we have a collection of methods that handle mouse and touch interaction. _onTouchStart is called during a click (or touch) and sets a property (touchStarted) on the target cube to note that the mouse button is currently active. The method then immediately disables the tweening provided by CSS transitions by adding the .no-tween class. This is done so the cube rotates fluidly with mouse movement in our next method. Finally, the starting position of the mouse (or touch) is recorded so we can later calculate how far it has moved:

HexaFlip.prototype._onTouchStart = function(e, cube) {
  e.preventDefault();
  cube.touchStarted = true;
  e.currentTarget.classList.add('no-tween');
  if (e.type === 'mousedown') {
    return cube.y1 = e.pageY;
  } else {
    return cube.y1 = e.touches[0].pageY;
  }
};

Movement immediately followed by the click is handled by _onTouchMove:

HexaFlip.prototype._onTouchMove = function(e, cube) {
  if (!cube.touchStarted) {
    return;
  }
  e.preventDefault();
  cube.diff = (e.pageY - cube.y1) * this.touchSensitivity;
  cube.yDelta = cube.yLast - cube.diff;
  return this._setSides(cube);
};

This method is called many times in succession as the user rotates the cube and constantly calculates the distance in pixels moved since we originally recorded y1 in the last method. The cube's yDelta is the current distance travelled plus all previous rotations in the past. By calling _setSides, the cube's faces update and display the correct cycle of values. _setSides also applies the total yDelta to the cube's DOM element's transform style and the result is that the cube appears to rotate analogous to the user's movements.

When the mouse button is released, _onTouchEnd is called:

HexaFlip.prototype._onTouchEnd = function(e, cube) {
  var mod;
  cube.touchStarted = false;
  mod = cube.yDelta % 90;
  if (mod < 45) {
    cube.yLast = cube.yDelta + mod;
  } else {
    if (cube.yDelta > 0) {
      cube.yLast = cube.yDelta + mod;
    } else {
      cube.yLast = cube.yDelta - (90 - mod);
    }
  }
  if (cube.yLast % 90 !== 0) {
    cube.yLast -= cube.yLast % 90;
  }
  cube.el.classList.remove('no-tween');
  return cube.el.style[css.transform] = this._getTransform(cube.yLast);
};

In most cases, the user will release the mouse button while the cube is somewhat askew (at an angle that isn't a multiple of ninety). Rather than leaving the cube rotated in a haphazard way, this method calculates the remainder between the current rotation and ninety, and finds the nearest clean value. Before applying this rotation transform, the no-tween class is removed and the result is a smooth snapping behavior, where the cubes always drift back into a proper position.

Finally we have two simple interaction-related methods which handle when the user's mouse/finger leave the cube. The latter is a necessary polyfill for browsers that don't support the mouseleave event:

HexaFlip.prototype._onTouchLeave = function(e, cube) {
  if (!cube.touchStarted) {
    return;
  }
  return this._onTouchEnd(e, cube);
};

HexaFlip.prototype._onMouseOut = function(e, cube) {
  if (!cube.touchStarted) {
    return;
  }
  if (e.toElement && !cube.el.contains(e.toElement)) {
    return this._onTouchEnd(e, cube);
  }
};

Next, we have two methods designed for use by other developers. The utility of our cube interfaces would be quite limited if the values of their current positions couldn't be read or manipulated externally. To programmatically change which faces are displayed on the cubes, we have a method called setValue that accepts an object literal with a key for every cube set, with a corresponding value to display:

HexaFlip.prototype.setValue = function(settings) {
  var cube, index, key, value, _results;
  _results = [];
  for (key in settings) {
    value = settings[key];
    if (!(this.sets[key] && !this.cubes[key].touchStarted)) {
      continue;
    }
    value = value.toString();
    cube = this.cubes[key];
    index = this.sets[key].indexOf(value);
    cube.yDelta = cube.yLast = 90 * index;
    this._setSides(cube);
    _results.push(this._setContent(cube[faceSequence[index % 4]], value));
  }
  return _results;
};

The logic is simple: we get the value's position in the array with indexOf and rotate the cube ninety degrees for every offset from zero.

getValue performs the opposite task and retrieves the current values of the cubes. While it may be obvious to the user via simply looking, external code needs a way of knowing which cube faces and corresponding values are facing the user:

HexaFlip.prototype.getValue = function() {
  var cube, offset, set, setLength, _ref1, _results;
  _ref1 = this.cubes;
  _results = [];
  for (set in _ref1) {
    cube = _ref1[set];
    set = this.sets[set];
    setLength = set.length;
    offset = cube.yLast / 90;
    if (offset < 0) {
      if (-offset > setLength) {
        offset = setLength - -offset % setLength;
        if (offset === setLength) {
          offset = 0;
        }
      } else {
        offset = setLength + offset;
      }
    }
    if (offset >= setLength) {
      offset %= setLength;
    }
    if (typeof set[offset] === 'object') {
      _results.push(set[offset].value);
    } else {
      _results.push(set[offset]);
    }
  }
  return _results;
};

Above, we loop through the cubes and determine what position in the array they are each showing based on their count of ninety degree rotations. The result is an array with a value for each cube.

Finally, we have two convenience methods, flip and flipBack. These methods advance all of the cubes forward or backwards by one ninety degree rotation, respectively. While this behavior is entirely possible by using setValue, it would be tedious as the developer would have to get the current values with getValue and then refer to the original set arrays and determine their successive values. flip accepts an argument that reverses the rotation so flipBack simply piggybacks off its functionality.

HexaFlip.prototype.flip = function(back) {
  var cube, delta, set, _ref1, _results;
  delta = back ? -90 : 90;
  _ref1 = this.cubes;
  _results = [];
  for (set in _ref1) {
    cube = _ref1[set];
    if (cube.touchStarted) {
      continue;
    }
    cube.yDelta = cube.yLast += delta;
    _results.push(this._setSides(cube));
  }
  return _results;
};

HexaFlip.prototype.flipBack = function() {
  return this.flip(true);
};

As with setValue, we'll ignore any cube if the user is currently manipulating it.

That's it! Hopefully you've gained some insight into the thought process and best practices regarding flexible UI plugins.

Demos

  1. Default: Try dragging some cubes with your mouse.
  2. Time Picker: Drag the cubes or use the select menus to set a time.
  3. Image Cycle: Notice that the number of images exceeds the four cube faces.
  4. Visual Password Experiment: The password is “red yellow blue green.” See if you can get it.

The third demo features illustrations by Jason Custer.

If you find any bugs or have some improvements to contribute, submit them to the GitHub repository.

View demo Download source

App Showcase with Grid Overlay

$
0
0

App Showcase with Grid Overlay

View demo Download source

Today we will be creating a little app showcase with a neat effect. The idea is to show a mobile device with a screenshot of an app and when clicking on the device, a grid appears, showing some more screenshots. The effect is very subtle: the device moves back and the grid fades in and scales up. When clicking on another screenshot, the device image gets updated and the grid disappears again.

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

The beautiful iPhone mockup used in the demo is by Jakub Kejha

Let’s get started with the markup.

The Markup

The HTML will consist of a main wrapper that contains a heading, a division for the device and a division for the grid:

<div id="ac-wrapper" class="ac-wrapper">
	<h2>Weatherous <span>Concept & UI Design</span></h2>
	<div class="ac-device">
		<a href="#"><img src="images/screen1.jpg"/></a>
		<h3 class="ac-title">Gentrify small batch umami retro vegan</h3>
	</div>
	<div class="ac-grid">
		<a href="#"><img src="images/screen1.jpg"/><span>Gentrify small batch umami retro vegan</span></a>
		<a href="#"><img src="images/screen2.jpg"/><span>Chambray squid semiotics</span></a>
		<a href="#"><img src="images/screen3.jpg"/><span>Fashion axe blue bottle</span></a>
		<a href="#"><img src="images/screen4.jpg"/><span>Photo booth single-origin coffee</span></a>
		<a href="#"><img src="images/screen5.jpg"/><span>Flexitarian synth keytar blog master</span></a>
		<a href="#"><img src="images/screen6.jpg"/><span>Next level retro flexitarian freegan</span></a>
		<a href="#"><img src="images/screen7.jpg"/><span>Pour-over superious meggings terry</span></a>
		<a href="#"><img src="images/screen8.jpg"/><span>Seitan william vinyl chillwave</span></a>
	</div>
</div>

Once we click on a grid item, we will update the content of the device container. We will also make the span for each grid item appear on hover.

Let’s style everything.

The CSS

Note that the CSS will not contain any vendor prefixes, but you will find them in the files.
In this tutorial we will be going through the style of demo 1.

So let’s start with the main wrapper. This will be the container that will have perspective. The origin will not be in the center but a bit more up:

.ac-wrapper {
	width: 100%;
	position: relative;
	perspective: 1000px;
	perspective-origin: 50% 25%;
}

The heading will be positioned absolutely on the left side of the device:

.ac-wrapper h2 {
	top: 20%;
	width: 50%;
	position: absolute;
	font-weight: 300;
	font-size: 4em;
	text-align: right;
	padding: 0 180px 0 50px;
}

Let’s give a slightly different look to the span:

.ac-wrapper h2 span {
	display: block;
	font-size: 60%;
	color: #c0c0c0;
}

The device will have the iPhone mockup as a background image and we will set the right dimensions. This container will need to preserve the 3D transforms and we’ll add a transition to it. Later, we’ll define a “transition classes” that will contain the properties for the altered states.

.ac-device {
	background: url(../images/iPhone.png) no-repeat;
	width: 288px;
	height: 611px;
	margin: 0 auto;
	position: relative;
	transition: all 0.3s ease;
	transform-style: preserve-3d;
}

The screenshot will be inside of an anchor and we’ll set the dimensions here and position it to fit into the mockup:

.ac-device a {
	height: 440px;
	width: 249px;
	display: inline-block;
	margin: 85px 0 0 20px;
}

.ac-device a img {
	display: block;
}

The title for each screenshot once it’s in the mockup view will be positioned absolutely on the right side of the device:

.ac-device h3 {
	position: absolute;
	font-size: 2.5em;
	left: 100%;
	width: 100%;
	top: 60%;
	margin-left: 30px;
	font-weight: 300;
	color: #888;
}

Now, let’s style the grid. We want to display a total of eight items so a row will have four items. Let’s set a fitting width, make it absolute and center it by setting a negative left margin (half of its width) and a left value of 50%. The initial opacity is 0 and since the grid is displayed and covering the device, we’ll set the pointer events to none so that we can’t click on it when it’s invisible. We’ll also add a transition and translate it -350px on the Z axis:

.ac-grid {
	position: absolute;
	width: 620px;
	left: 50%;
	margin-left: -310px;
	height: 100%;
	z-index: 1000;
	top: 0;
	opacity: 0;
	pointer-events: none;
	transform-style: preserve-3d;
	transition: all 0.3s ease;
	transform: translateZ(-350px);
}

The anchors in the grid will be floated left and the images inside will be set to 100% width. This will come in handy later on when we apply some media queries:

.ac-grid a {
	width: 145px;
	display: block;
	position: relative;
	float: left;
	margin: 10px 5px;
	cursor: pointer;
}

.ac-grid a img {
	display: block;
	width: 100%;
}

The span for the description will be positioned absolutely on top of the anchor and we’ll fade it in and move it a bit on hover:

.ac-grid a span {
	position: absolute;
	height: 100%;
	width: 100%;
	left: 0;
	top: 0;
	text-transform: uppercase;
	padding: 3em 1em 0;
	z-index: 100;
	color: #ddd;
	background: rgba(0,0,0,0.4);
	font-weight: 700;
	opacity: 0;
	transform: translateY(-5px);
	transition: all 0.2s ease;
}

.ac-grid a:hover span {
	opacity: 1;
	transform: translateY(0);
}

Next, we’ll define the “transition classes”. When we click on the device, we’ll apply a class to the wrapper which will trigger the fading in and scaling up of the grid and the moving back of the device:

.ac-wrapper.ac-gridview .ac-device {
	transform: translateZ(-350px);
	opacity: 0.6;
}

.ac-wrapper.ac-gridview .ac-grid {
	transform: translateZ(0px);
	opacity: 1;
	pointer-events: auto;
}

Once the grid is there, we also set the pointer-events to auto again.

Our layout has some absolutely positioned elements and we’ll need to take care of them on smaller screens. The idea is that we will switch the main heading to the right side first and then center everything once the screen is very small. The second media query takes care of the grid structure. Here we will set a fluid width for both, the grid and the anchors:

@media screen and (max-width: 63.875em) {
	.ac-wrapper { 
		font-size: 60%; 
		width: 100%; 
		padding: 0 20px;
	}

	.ac-device {
		margin: 0;
		width: 100%;
	}

	.ac-device h3 { 
		width: 50%;
		left: 290px;
	}

	.ac-wrapper h2 { 
		left: 308px; 
		padding: 0; 
		text-align: left; 
		margin-left: 30px;
	}
}

@media screen and (max-width: 39.8125em) {
	.ac-grid {
		width: 90%;
		left: 5%;
		margin-left: 0;
		padding-top: 150px;
	}

	.ac-grid a {
		width: 22%;
	}
}


@media screen and (max-width: 35.6875em) {
	.ac-wrapper {
		padding: 0 20px 100px;
	}

	.ac-wrapper h2 { 
		width: 100%;
		text-align: center;
		margin: 0 0 1em;
		top: 0;
		left: auto;
		position: relative;
	}

	.ac-device {
		margin: 0 auto;
		width: 288px;
	}

	.ac-device h3 {
		position: relative;
		margin: 0;
		left: auto;
		width: 100%;
		top: 100px;
		display: block;
		text-align: center;
	}
}

And that’s all the style! Let’s take a look at the JavaScript.

The JavaScript

Let’s start by caching some elements and initialize some variables:

	var $el = $( '#ac-wrapper' ),
		// device element
		$device = $el.find( '.ac-device' ),
		// the device image wrapper
		$trigger = $device.children( 'a:first' ),
		// the screens
		$screens = $el.find( '.ac-grid > a' ),
		// the device screen image
		$screenImg = $device.find( 'img' ),
		// the device screen title
		$screenTitle = $device.find( '.ac-title' ),
		// HTML Body element
		$body = $( 'body' ); 	

We will bind the events to the device’s image wrapper (anchor) and to the screen elements.

	function init() {
		// show grid
		$trigger.on( 'click', showGrid );
		// when a grid´s screen is clicked, show the respective image on the device
		$screens.on( 'click', function() {
			showScreen( $( this ) );
			return false;
		} );
	}

When the device’s image is clicked, the grid is shown. For this to happen the class “ac-gridview” has to be added to the ac-wrapper element:

	function showGrid() {
		$el.addClass( 'ac-gridview' );
		// clicking somewhere else on the page closes the grid view
		$body.off( 'click' ).on( 'click', function() { showScreen(); } );
		return false;
	}

When a screen element is clicked we remove the “ac-gridview” class from the ac-wrapper element, and update both, image source and title on the respective elements:

	function showScreen( $screen ) {
		$el.removeClass( 'ac-gridview' );
		if( $screen ) {
			// update image and title on the device
			$screenImg.attr( 'src', $screen.find( 'img' ).attr( 'src' ) );
			$screenTitle.text( $screen.find( 'span' ).text() );
		}
	}

For the third demo we also want to offer the possibility to navigate through the screenshots without having to open the grid. Depending to the direction we are navigating, the next screen will either scale up / fade in (navigating to the next screen) or move up / fade in (navigating to the previous screen). The same logic applies to the current screenshot. In order for this to work, we need to add the next/previous screen’s image to the DOM right before the current screen’s image (both images being absolute). When the transition ends we remove the old one:

	function navigate( direction ) {

		// if currently animating return
		if( animating ) {
			return false;
		}

		animating = true;
		
		// update current
		if( direction === 'next' ) {
			current = current < screensCount - 1 ? ++current : 0;
		}
		else if( direction === 'prev' ) {
			current = current > 0 ? --current : screensCount - 1;
		}
		
		// next screen to show
		var $nextScreen = $screens.eq( current );

		// if css transitions support:
		if( support ) {

			// append new image to the device and set the transition and initial style
			var $nextScreenImg = $( '' ).css( {
				transition : 'all 0.5s ease',
				opacity : 0,
				transform : direction === 'next' ? 'scale(0.9)' : 'translateY(100px)'
			} ).insertBefore( $screenImg );

			// update title
			$screenTitle.text( $nextScreen.find( 'span' ).text() );

			setTimeout( function() {

				// current image fades out / new image fades in
				$screenImg.css( {
					opacity : 0,
					transform : direction === 'next' ? 'translateY(100px)' : 'scale(0.9)' 
				} ).on( transEndEventName, function() { $( this ).remove(); } );

				$nextScreenImg.css( {
					opacity : 1,
					transform : direction === 'next' ? 'scale(1)' : 'translateY(0px)' 
				} ).on( transEndEventName, function() {
					$screenImg = $( this ).off( transEndEventName );
					animating = false;
				} );

			}, 25 );

		}
		else {
			// update image and title on the device
			$screenImg.attr( 'src', $nextScreen.find( 'img' ).attr( 'src' ) );
			$screenTitle.text( $nextScreen.find( 'span' ).text() );
			animating = false;
		}

	}

This navigation concept was introduced by Evan You and you can check it out here, or watch the video.

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

View demo Download source

A Collection of Page Transitions

$
0
0

A Collection of Page Transitions

View demo Download source

Today we’d like to share a collection of creative page transitions with you. We’ve put together a couple of animations that could be applied to “pages” for creating interesting navigation effects when revealing a new page. While some effects are very simplistic, i.e. a simple slide movement, others make use of perspective and 3d transforms to create some depth and dynamics.

Please note that this is just for showcasing some interesting effects and for inspiration. It is not a slider or anything like that. We’ll just apply some classes to make the page transition visible, not for navigating.

The CSS animations are divided into different sets, depending on what they do.

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

For showcasing the page transitions, we’ve used the following structure:

<div id="pt-main" class="pt-perspective">
	<div class="pt-page pt-page-1">
		<h1><span>A collection of</span><strong>Page</strong> Transitions</h1>
	</div>
	<div class="pt-page pt-page-2"><!-- ... --></div>
	<!-- ... -->
</div>

The perspective container is relative and we add a perspective of 1200px to it. The following styles are needed for all animations to work:

.pt-perspective {
	position: relative;
	width: 100%;
	height: 100%;
	perspective: 1200px;
	transform-style: preserve-3d;
}

.pt-page {
	width: 100%;
	height: 100%;
	position: absolute;
	top: 0;
	left: 0;
	visibility: hidden;
	overflow: hidden;
	backface-visibility: hidden;
	transform: translate3d(0, 0, 0);
}

.pt-page-current,
.no-js .pt-page {
	visibility: visible;
}

.no-js body {
	overflow: auto;
}

.pt-page-ontop {
	z-index: 999;
}

The .pt-page-ontop is used for some of the page transitions where we need one of the pages to stay on top of the other one.

An example for animation classes and keyframe animations are the following ones that scale the pages in different directions and fade them in or out:

/* scale and fade */

.pt-page-scaleDown {
	animation: scaleDown .7s ease both;
}

.pt-page-scaleUp {
	animation: scaleUp .7s ease both;
}

.pt-page-scaleUpDown {
	animation: scaleUpDown .5s ease both;
}

.pt-page-scaleDownUp {
	animation: scaleDownUp .5s ease both;
}

.pt-page-scaleDownCenter {
	animation: scaleDownCenter .4s ease-in both;
}

.pt-page-scaleUpCenter {
	animation: scaleUpCenter .4s ease-out both;
}

/************ keyframes ************/

/* scale and fade */

@keyframes scaleDown {
	to { opacity: 0; transform: scale(.8); }
}

@keyframes scaleUp {
	from { opacity: 0; transform: scale(.8); }
}

@keyframes scaleUpDown {
	from { opacity: 0; transform: scale(1.2); }
}

@keyframes scaleDownUp {
	to { opacity: 0; transform: scale(1.2); }
}

@keyframes scaleDownCenter {
	to { opacity: 0; transform: scale(.7); }
}

@keyframes scaleUpCenter {
	from { opacity: 0; transform: scale(.7); }
}

For the purpose of this demonstration we apply the respective animation classes to the current page and the incoming one. For example:

//...

case 17:
	outClass = 'pt-page-scaleDown';
	inClass = 'pt-page-moveFromRight pt-page-ontop';
	break;
case 18:
	outClass = 'pt-page-scaleDown';
	inClass = 'pt-page-moveFromLeft pt-page-ontop';
	break;
case 19:
	outClass = 'pt-page-scaleDown';
	inClass = 'pt-page-moveFromBottom pt-page-ontop';
	break;

// ...

Check out the demo where you can simply iterate through the whole set of page transitions using the first button. You can also choose a specific effect from the drop-down menu.

I hope you enjoy this and get inspired to build some exciting things!

View demo Download source

Viewing all 120 articles
Browse latest View live