3D Flipping Circle with CSS3 and jQuery

In this tutorial we will 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.

3D Flipping Circle with CSS3 and jQuery

3D Flipping Circle with CSS3 and jQuery

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!

Tagged with:

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay up to date with the latest web design and development news and relevant updates from Codrops.