📜 ⬆️ ⬇️

Flipboard animation with CSS3 and JavaScript

Good day. Surely many have seen an application for iOS called Flipboard. With all its virtues, personally, it first of all pleased me with its funny page turning animations. The idea was born to implement something similar for your site exclusively on Javascript and CSS3.

In this case, I used the CSS3 transform: rotate3d (...) property, which requires support for hardware-accelerated graphics and works adequately only in Chrome 16+ , so I restricted all the necessary properties to -webkit- prefixes. In production for users with inappropriate parameters, I replaced the animation with a simpler one.

Here is the final result:
')


Live demo



Let's start with HTML. I wanted to make a universal script, which practically will not depend on the source html, so it contains only a container with the necessary class, images and a link to start the animation:

<div class="flip-images"> <img src="./images/image_1.jpg"> <img src="./images/image_2.jpg"> <img src="./images/image_3.jpg"> </div> <a href="#" id='flip-one-more'>Flip</a> 


The script performs mostly a regulatory function, but its first task is parsing, put the url of pictures into an array (for convenience, I use jQuery):

 var image_index = 0; //   var images = parse_image_container(); function collect_images(){ var images = new Array(); $('.flip-images img').each(function(){ images.push($(this).attr('src')); $(this).remove(); }); return images; } 


After the collect_images () function has completed, the empty container with the class flip-images remains, you need to fill it with an html template that will contain all the necessary layers. Each time you need two pictures to animate turning over, one in front, the other in the back, besides each picture must be divided into two parts, plus for greater realism you need to add pseudo-shadows.

 $('.flip-images').append("<div class='flip-container'>\ <div class='flip-top'>\ <div class='shadow-front'></div>\ <div class='front-image-top'></div>\ <div class='back-image-bottom'></div>\ <div class='shadow-back'></div>\ </div>\ <div class='back-image-top'></div>\ <div class='flip-bottom'>\ <div class='shadow-bottom'></div>\ <div class='front-image-bottom'></div>\ </div>\ </div>"); 


As a result, the container with the flip-top class will be flipped overlapping the flip-bottom container.

Now we need to expand the necessary images into the necessary containers, this role is assigned to the place_images () function:

 function place_images(){ var next_image_index = image_index+1; if(next_image_index==images.length){ next_image_index = 0; } var front_image = "<img src='"+images[image_index]+"'>"; var back_image = "<img src='"+images[next_image_index]+"'>"; $('.back-image-top').html("").append(back_image); $('.back-image-bottom').html("").append(back_image); $('.front-image-top').html("").append(front_image); $('.front-image-bottom').html("").append(front_image); } 


Just put a picture with the image_index index into two containers responsible for the lower and upper parts of the front picture, and put the next picture in containers for the back image.

In the script it remains only to hang up the action on the link to start the animation, but we will deal with this a little later. Let's go to CSS. I will not paint each class separately, they are primitive, just comment on the key points.

 .flip-images img{ width: 300px; height: 200px; } .flip-container{ width: 300px; height: 200px; position: relative; -webkit-perspective: 600px; } .flip-top{ height: 100px; overflow: hidden; width: 300px; } .flip-bottom{ position: relative; width: 300px; height: 100px; } /*   */ .front-image-top{ /*     */ display: block; -webkit-backface-visibility: hidden; /*    */ position: absolute; top: 0px; width: 300px; height: 100px; /* ,       */ overflow: hidden; /*  */ z-index: 900; } .front-image-bottom{ /*     */ height: 100px; width: 300px; overflow: hidden; vertical-align: bottom; position: absolute; z-index: -2; } .front-image-bottom img{ position: absolute; top: -100px; /*     ,    */ } /*   */ .back-image-top{ height: 100px; position: absolute; top: 0px; vertical-align: top; overflow: hidden; z-index: -1; } .back-image-bottom{ display: block; position: absolute; top: 0px; height: 100px; -webkit-transform: rotateY(180deg) rotateZ(180deg); /*       */ overflow: hidden; width: 300px; -webkit-backface-visibility: hidden; z-index: 800; } .back-image-bottom img{ position: absolute; top: -100px; } /**/ .shadow-top-front{ position: absolute; background: #000; z-index: 1000; width: 300px; height: 100px; -webkit-backface-visibility: hidden; opacity: 0; /*  ,    */ } .shadow-top-back{ position: absolute; top: 0px; width: 300px; height: 100px; background: #000; z-index: 1000; -webkit-backface-visibility: hidden; -webkit-transform: rotateY(180deg); /*   ,       */ opacity: 1; /*  ,    */ } .shadow-bottom{ width: 300px; height: 100px; position: absolute; z-index: -1; background: #000; opacity: 0; } 


At this stage, all the images are in their places, it remains only to set the animation, for this we use the property keyframes.

The main animation will be called a flip and it will be like this: the upper part of the image falls, bounces off the bottom, soaring slightly up and falling all the way down again. So it will look like in CSS:

 @-webkit-keyframes flip { 0% { -webkit-transform: rotate3d(1,0,0, 0deg); } 50% { -webkit-transform: rotate3d(1,0,0, -180deg); } 60% { -webkit-transform: rotate3d(1,0,0, -155deg); } 70% { -webkit-transform: rotate3d(1,0,0, -140deg); } 100% { -webkit-transform: rotate3d(1,0,0, -180deg); } } 


Intervals and rotation values ​​are selected by trial and error. Animations for shadows are made in the same way:

 @-webkit-keyframes shadowTopFront{ 0% { opacity: 0; } 70% { opacity: 1; } 100% { opacity: 0; } } @-webkit-keyframes shadowTopBack { 0% { opacity: 0.8; } 50% { opacity: 0; } 60% { opacity: 0.05; } 70% { opacity: 0.1; } 100% { opacity: 0; } } @-webkit-keyframes shadowBottom { 0% { opacity: 0; } 50% { opacity: 0.6; } 60% { opacity: 0.4; } 70% { opacity: 0.3; } 100% { opacity: 0.5; } } 


It remains to describe the classes that will run the animation:

 .flip { /*background: #ccc;*/ width: 200px; height: 100px; -webkit-transform-origin: bottom; -webkit-animation: flip 1s; /*    */ -webkit-animation-iteration-count: 1; /*    */ -webkit-animation-timing-function: cubic-bezier(0,0,1,0.5); /*        */ -webkit-transform: rotate3d(1,0,0, 180deg); /*   ,        0 */ } .shadow-top-front-animate{ -webkit-animation: shadowTopFront 1s; -webkit-animation-iteration-count: 1; -webkit-animation-timing-function: cubic-bezier(0,0,1,0.5); opacity: 0; } .shadow-top-back-animate{ -webkit-animation: shadowTopBack 1s; -webkit-animation-iteration-count: 1; -webkit-animation-timing-function: cubic-bezier(0,0,1,0.5); opacity: 0; } .shadow-bottom-animate{ -webkit-animation: shadowBottom 1s; -webkit-animation-iteration-count: 1; -webkit-animation-timing-function: cubic-bezier(0,0,1,0.5); opacity: 1; } 


Let's go back to javascript and set an action to click on the link with id flip-one-more . By clicking the appropriate container, the necessary images should be inserted, classes with animations should be added and the image_index variable should increase.

 $('#flip-one-more').click(function(){ place_images(image_index); $('.flip-top').addClass('flip'); $('.shadow-top-front').addClass('shadow-top-front-animate'); $('.shadow-top-back').addClass('shadow-top-back-animate'); $('.shadow-bottom').addClass('shadow-bottom-animate'); image_index = image_index + 1; if(image_index==images.length){ image_index = 0; } return false; }); 


In this form, everything will work, but we will only see the animation once, after repeated clicks on the link, the pictures will simply be rearranged. This is because CSS animation is a one-time phenomenon, it should always be returned to its original position. Alas, simply deleting the assigned classes will not help the case, but there is one simple, quite working way to return the animation to the original one. It is necessary to remove and add again the blocks to which the animation was applied. As a result, the final version of the function for a click is as follows:

 $('#flip-one-more').click(function(){ flip_container = $('.flip-container'); new_flip_container = flip_container.clone(true); flip_container.before(new_flip_container); $("." + flip_container.attr("class") + ":last").remove(); place_images(); $('.flip-top').addClass('flip'); $('.shadow-top-front').addClass('shadow-top-front-animate'); $('.shadow-top-back').addClass('shadow-top-back-animate'); $('.shadow-bottom').addClass('shadow-bottom-animate'); image_index++; if(image_index==images.length){ image_index = 0; } return false; }); 


Now everything works as planned. Apply a similar effect in most cases, it remains only to wait for the support of the desired properties of all popular browsers.

Thank you for your attention, I understand that the code is far from ideal, so I will be glad to hear constructive criticism.

Demo

Archive with an example

UPD. I added parameters with the prefixes -moz-, -ms- and -o- to the example, but it works correctly only in Chrome.

UPD2. If you have an example with bugs in Chrome 16+, try to include several options in chrome: flags, namely:
- Override the software rendering list
- Processing of all pages using the graphics processor
- Accelerated drawing by the graphics processor

Source: https://habr.com/ru/post/140527/


All Articles