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.

Feedback 30

Comments are closed.
  1. At first I thought this piece wouldn’t benefit me, because I can’t think of a place to use it. But once again, Mary Lou, you’re attention to detail and user experience is simply amazing a lesson in and of itself. I downloaded the source and set the 0.6s transition to 10s just so I could see it in all its glory. Your fallback CSS is an excellent graceful degradation, too! So much to learn from you in terms of UX.

  2. Beautiful. On Chrome (22.0.1207.1 dev-m) the image flickers while animating and is not visible when it ends.

    • I also see this on Chrome 21.0.1180.57 beta. It seems that this can be fixed by adding the following to to .fc-front

      -webkit-transform: translateZ(1px);

  3. Amazing as always! Brilliant and creative!

    (Mary Lou, I think I’m in love with you)

  4. Que hermoso chica te quedo precioso, mas alla de la creatividad del concepto, es la creatividad del codigo, realmente una obra de genios. Saludos

  5. Your all articles and even their background and designs are superb, i wish i had a wife like you, hahahaha

  6. Once again … awesome.

    Thanks a lot Mary Lou, I learn so much with your articles !

  7. Thank you all for your wonderful feedback! I’m happy you like it ๐Ÿ™‚

  8. Mary Lou is the reason I keep coming back to this site (well, mostly why). I love the little things like the ribbon thing pulling the circle. Pretty neat and useful!

  9. Very wonderful and simple 3d Flipping Circle but attractive.. I like this tutorial and I will try this also.. Thanks Mary!:-)

  10. Mary,

    just one question;
    …nice designs but most of the time not suitable for IE. Wouldn’t it be nice to create something that does work on IE ?

    Chris.

  11. Lovely concept.
    I do however not care for the “click here” or even the need for it.
    Can’t you make it more obvious by making some kind of > chevron on the little flap to pull?
    Should be intuitive to use in my opinion.
    Keep up the great work

  12. there is a bug (maybe 2) on the latest google chrome on mac – when clicking on the opener the picture is just flashing thrice (really fast) but in the end it doesnt show the picture, just the cover but reversed. 2nd the opener (the click here quare) has space between the circle and the opener, so it doesnt look realistic.

    • “the pictures is just flashing thrice”

      Wow! It’s really fast, how did you count? ^^’

  13. Hi,

    Thanks so much for your tutorial “3D Flipping Circle using CSS3 and JQuery.” It’s really cool but … well …

    I know it’s because of my lack of knowledge about jquery … but I just can’t get it to work. Can you help me out? What would be the best way for you to do that, if you can? I can zip up the directory on my localhost and send it to you or I can put it online and let you look at it. Currently, it’s on my localhost.

    Thanks so much for your help!