This paper mainly discusses the realization of the following two book-turning animations:
The first is the whole page flip effect:
This whole page flip effect is done by animating rotateY, combined with some 3D properties of CSS.
The effect of the second folding line is shown below:
Mainly by calculating the position of the page over.
Both are not very complicated in principle and need to work together to create a coherent animation of the book.
Let’s focus on the implementation of the first page turn effect.
1. Basic layout
This implementation is relatively simple. Let’s get the DOM structure ready, as shown in the following code:
<ul class="pages">
<! -- A li. Paper contains two pages -->
<li class="paper" data-left>
<! -- A page is a page of content -->
<div class="page page-1-back">
<img src="1.jpg" alt>
</div>
<div class="page page-1">
<img src="2.jpg" alt>
</div>
</li>
<li class="paper" data-right>
<div class="page page-2">
<img src="3.jpg" alt>
</div>
<div class="page page-2-back">
<img src="4.jpg" alt>
</div>
</li>
<! -- Omit the contents of other pages -->
</ul>Copy the code
A li.paper is a piece of paper, with two pages. The data-left attribute indicates that it is on the left and data-right attribute indicates that it is on the right. So if it is the next page, let data-right do the left turn animation, and the previous page let data-left do the right turn animation.
.page-1 is the page currently displayed on the left,.page-2 is the page on the right, and.page-1-back and.page-2-back are the pages following.paeg-1 and.page-2, respectively. They flip horizontally when they’re placed behind them, which should be easy to imagine, so transform: scale:
.page-1-back..page-2-back {
transform: scale(1, 1); }Copy the code
And the z-index of the page 1 is higher than that of the page 1 back:
.page-1..page-2 {
z-index: 1;
}Copy the code
By doing this, you get the following layout:
Now turn the page on the right.
2. Book-turning animation
Paper rotateY animation, very simple, as shown in the following code:
@keyframes flip-to-left {
from {
transform: rotateY(0);
}
to {
transform: rotateY(-180deg); }}.paper[data-right] {
transform-origin: left center;
animation: flip-to-left 2s ease-in-out;
}Copy the code
You need to set the center of the transformation to the middle of the left. The effect is as follows:
We have found several problems. The first problem is that the paper at the back of the page is not displayed, because at the beginning, all the papers that are not displayed are hidden, so the paper at the back needs to be displayed:
.paper {
display: none;
position: absolute;
/* Defaults to the right */
right: 0;
}
.paper[data-left]..paper[data-right] {
display: block;
z-index: 1;
}
.paper[data-left] {
right: auto;
left: 0;
}
/* Display the adjacent paper */
.paper[data-right] + .paper {
display: block;
}Copy the code
This will display the paper behind it, as shown in the picture below:
The rotate attribute is changed as follows: The rotate attribute is changed as follows: the rotate attribute is changed as follows: The rotate attribute is changed as follows: The rotate attribute is changed as follows: The rotate attribute is changed as follows: The rotate attribute is changed as follows: The rotate attribute is changed as follows: The rotate attribute is changed as follows: The rotate attribute is changed as follows: The rotate attribute is changed as follows:
Therefore, the first method can swap the high and low relationship of Z-index when flipping half, so that the page 2-back is higher than the Page 2, but this method is not easy to control, because the animation changes are not linear, even linear’s method is not flexible, prone to flashing.
The second method is to change the translateZ relationship between the two of them. Instead of setting the z-index relationship directly, the translateZ value of Page 2 is 1px higher than that of Page 2-back. In order for translateZ to work, set the transform-style of their container to preserve-3D, as shown in the following code:
.paper {
transform-style: preserve-3d;
}
.page-1..page-2 {
transform: translateZ(1px);
}Copy the code
This allows translateZ to change the child from a flat to a three dimensional space. The translateZ effect is as follows:
The basic effect is there, but something is not quite right. The flip is a little flat, with no depth of field. Depth of field reminds us of another CSS property transform-Perspective. Let’s add a perspective to it and see how it looks:
@keyframes flip-to-left {
from {
transform: perspective(1000px) rotateY(0);
}
to {
transform: perspective(1000px) rotateY(-180deg); }}Copy the code
The effect is shown in the figure below:
It looks and feels a lot more solid. Perspective can be understood as the position of the camera, and the larger the value, the farther the camera will be pushed. The comparison of different values is as follows:
That’s basically the animation for turning the book, and the same goes for turning the book from left to right. The next question was how to create an animated sequence of books.
3. Keep flipping books
We can move the animation forward so it stays at the end of the animation:
.paper[data-right] {
transform-origin: left center;
animation: flip-to-left 2s ease-in-out forwards;
}Copy the code
When you stop, the relationships of the above classes need to be updated. For example, when you turn them over, the original. Page 2-back will become. Page 1.
The more scientific way to animate is to use Element. Animate, because it has an onFinish callback that tells us that the animation is over. Animation is not very compatible with this API, so either find a polyfill or use the above CSS method and then use setTimeout. Polyfill has a large library, and the risk of using setTimeout to simulate the end of the animation is that it may not be accurate.
The code logic is relatively simple, that is, to find the corresponding DOM node and set the corresponding class or attribute, but the code is a little tedious, as shown below:
let currentPage = 1;
let$=document.querySelector.bind(document);
$('#next-page').addEventListener('click', goToNextPage);
const flipAnimateTime = 1000;
function goToNextPage () {
// Trigger the CSS animation
$('.paper[data-right]').setAttribute('data-begin-animate'.true);
setTimeout((a)= > {
// Data-right becomes data-left
let $rightPaper = $('.paper[data-right]'),
$leftPaper = $('.paper[data-left]');
$rightPaper.removeAttribute('data-right');
$rightPaper.setAttribute('data-left'.true);
// No data left
$leftPaper.removeAttribute('data-left');
$leftPaper.querySelector('.page-1').classList.remove('page-1');
$leftPaper.querySelector('.page-1-back').classList.remove('page-1-back');
// Reset the class relationship
$leftPaper = $rightPaper;
$rightPaper = $leftPaper.nextElementSibling;
$leftPaper.querySelector('.page').classList.add('page-1-back');
$leftPaper.querySelector('.page + .page').classList.add('page-1');
// If there is a next page
if ($rightPaper) {
$rightPaper.setAttribute('data-right'.true);
$rightPaper.querySelector('.page').classList.add('page-2');
$rightPaper.querySelector('.page + .page').classList.add('page-2-back');
}
currentPage++;
}, flipAnimateTime);
}Copy the code
The effect is shown in the figure below:
The same goes for turning the page to the left.
So the question is, what happens if the user hits the next page very quickly? If he clicks too fast and the previous page turns are not finished, the code in the setTimeout hasn’t executed yet, and the whole model is messed up. There are two solutions. The first one is to ban the operation of the next page in the process of page turning, but it seems unfriendly. The second one is to treat the process of page turning as a task, once the next page or the previous page is clicked, a task is pushed in, and each task is executed synchronously in order. If the task array is longer than 1, shorten the animation time and make it scroll faster. This is a similar process that I discussed in Implementing the Internal component rotation switch effect, so I won’t repeat it here.
4. Adaptation
You might be worried that after the animation is over, if you modify the DOM structure and the CSS property changes, it will blink. For example, the original page 2-back is flipped horizontally, but if you set it in JS, it will not be flipped horizontally. Even though the effect is the same, will it blink? As long as the browser performs the same layout calculation before and after the change, it will not flash, as in the above example, but once the shift is 1px off, it will flash.
In a real example, you might want a 1px gap in the middle, so instead of the right and left pages being exactly 50% wide, you’d have to subtract 1px, so if your transform-Origin is still left center it would be 1px to the right, and when the animation is over reset the state, The 1px offset will be corrected and there will be a little flash. When you change the transform-Origin to -1px center, the result is a 1px shift to the left. So it’s better not to make the shadows in the middle separate. Instead, use before/after for each page, and still make up 50% of each page. That’s fine.
Another issue to consider is that using Transform: Scale + translateZ can cause blurring. A direct example can be seen in this Codepen because translateZ or will-change is used: This process may be caused by the browser taking a slice of the current layer and giving it to the GPU for calculation. When the GPU scales the static image, the blur occurs. When translateZ and other attributes with promotion function are removed, the scaling process will be blurred, but the final state is clear. As shown in the figure below:
In the example above we used transform: scale(-1, 1) to flip horizontally and translateZ(1px) to flip up and down. In theory we’re using Scale but we’re not zooming in or out and it shouldn’t be blurry, but Chrome on Windows is clearly blurry (Chrome on MAC is not blurry) and if you remove translateZ it won’t be blurry. Therefore, the solution I came up with is not translateZ (using z-index) at the beginning of the layer, and translateZ is added only when the animation starts (and z-index is removed), and then translateZ is removed after the animation ends.
5. Become a plugin
Once the above problems are solved, you can turn it into a plug-in, and the user can just introduce it and initialize it, without worrying about how the classes change or anything like that.
In addition, since a paper container has two pages with a positive and negative relationship, once a page is suddenly inserted in the middle of the page, the positive and negative relationship of the page will change, so this structure is not very flexible, it is better to dynamically generate, that is to say, people who use the plug-in, all the pages should be arranged side by side. Then reorganize the DOM structure in the plug-in, putting the two pages on the front and back into a paper.
Then we discuss the realization of the second book-turning effect.
6. The realization of the effect of folding books
There is a ready-made plugin called turn.js, which is very easy to use. Let’s discuss its internal implementation briefly.
This thing, at first glance, seems to have a curved effect:
In fact, it doesn’t. The curve effect is a visual effect of the shadow and gradient it adds, which we can see when we remove the background-image gradient:
The camouflage without gradient is immediately flat. It becomes an origami model — given a piece of paper and a point to fold, calculate the rotation Angle and displacement to fold it. Its source code is computed in the fold function:
It has a variety of cosine and sine calculation and Angle judgment, the specific implementation is relatively complex, no in-depth study, the code can be seen in turn.js.
The other question is how does it achieve the effect of a cropped triangle display? It’s just a div on top of it:
7. Summary
This paper discusses two ways to achieve the book turning effect, focusing on the simple overall page turning method, which is mainly to do rotateY animation, open the perspective to give it a depth of field effect, and create a hierarchical relationship with pret-3D combined with translateZ. In order to turn over without flashing, the key point is to ensure that each page takes up 50% of the width. The fuzzy problem is caused by using scale and GPU improvement, so we can only ensure clarity by not writing 3D attributes.
The second effect model is relatively complex, and its principle and implementation are simply analyzed. It is mainly to calculate the Angle and displacement of the origami, and cover the top with a div to hide the part that cannot be exposed. Then add shadows and gradients to create a curved effect. It’s kind of a fun way to flip a book.
“How did the First-line Dafang develop wechat Mini Program”