IMPLEMENTING 2D PAGE FLIP EFFECT ON IPHONE OS
Comments: 17 comments
Category: Tips for Developers
2d page flip animation created using cocos2d framework.
Page flip (or page curl) effect is very popular on the touch devices. It's used as atransition effect between two views, often found in electronical books to move to the next page. There are many techniques of doing page flip effect. I would divide them into three groups:
- 2d animation
- 3d animation
- animation based on set of images
In this article we will focus only on the first technique. I will show you how to make a simple page flip on a flat plane (2d) (see video above). A 3d version of that effect works similar but is more complicated and will be soon explained in a separate article. If you are interested in the approach based on the set of images you can visit Darknoon blog for more info.
A flat plane
The easiest algorithm that came to my mind is based on animating vertices of a grid spread across a page of a virtual book - see picture 1.
Pic.1. Two pages of a book. Right page is covered by a grid.
First you need to draw a bottom page of your book and then the grid covered with a texture of the top page (see picture 2). At this point I assume you know the basics of any graphics framework (e.g. OpenGL) and know how to draw a textured triangle. I'm not going to explain it in this article. All codes shown in this article apply only to update the effect and not to draw it.
Pic.2. Page flip in action.
All the rest of the algorithm consists in moving vertices in a proper way. So in every frame we will visit all vertices and update their position. In a pseudo-code it will look like:
Vertex v;
for v in grid.vertices
{
update v;
}
To find out how to move vertices to simulate folding paper have a look at the image below (picture 3). As you can notice we will treat the edge of the folding page as a symmetry axis around which we will reflect vertices. Which vertices need to be reflected? All that are in front of the symmetry axis (or behind - depends on your line equation). In the picture 3, N is a normal vector of the symmetry axis which indicates the front of the axis, C is a sample point on the front side and C' is a reflected point.
Pic.3. The edge of the folding page with the symmetry axis.
Updating vertices with reflection included is shown in the following code:
Line line;
Vertex v;
for v in grid.vertices
{
const float distance = PointToLineDistance(v, line);
if (distance > 0)
{
reflect(v, line);
}
}
PointToLineDistance
is a function which returns a distance between the line
and the vertex v
. When distance
is less than 0 it means the vertex is behind the line
. reflect
function does a reflection of the vertex v
across the line
.The final effect can be achieved by moving and rotating the symmetry axis in time (see picture 4).
Pic.4. An algorithm of animate the symmetry axis.
To move and rotate the symmetry axis properly let's mark two points: P1 and P2. In every frame we will create the symmetry axis based on these points. The position of the point P1 will be determined by a linear interpolation between points B' and A. Similar with the point P2. We will interpolate its position between points C and D. So why did we get point B' instead of B? It's because if we got point B our symmetry axis would be always vertical. Vertical symmetry axis is not bad but effect looks nicer if the symmetry axis is oblique.
The whole update function of two-dimensional page flip effect can look like the code below. The
update
function should be called only for the duration of the effect with the argument progressFactor
with values between 0 and 1 (0 means the begining of the effect, 1 means the whole page has been flipped). There is a lerp(A, B, s)
function used which interpolates A towards B by s. s is clamped between 0 and 1. When s = 0 it returns A. When s = 1 the result is B. When s = 0.5 it returns the average of A and B. lerp(A, B, s)
can be evaluated to the following form: A*(1-s) + B*s.A
sqrt(x)
is a square root of x
. We use the sqrt
function to make the symmetry axis moving faster at the beginning of the effect and slower at the end. Try to experiment with the s
value, pass the result to the lerp
function and see what you get. Note that both s
and progressFactor
values have the range from 0 to 1.// progressFactor is a value from 0 to 1
void update(float progressFactor)
{
// play with other values:
// e.g.: s = progressFactor; or s = progressFactor * progressFactor;
const float s = sqrt(progressFactor);
Point P1 = lerp(B', A, s);
Point P2 = lerp(C, D, s);
Line line = CreateLineFromPoints(P1, P2);
Vertex v;
for v in grid.vertices
{
const float distance = PointToLineDistance(v, line);
if (distance > 0)
{
reflect(v, line);
}
}
}
This is the whole magic of the 2d page flip effect. Note that only x and y point's coordinates are modified. The z coordinate (depth) stays unchanged.
Summary
Algorithm presented in this article is very easy to understand and implement. If you still don't understand how it works or have any doubts, it's because of my inability to share technical knowledge:) If in doubt, feel free to leave a comment. The page flip effect presented in the video above is not perfect and can be improved in many ways. Below you have some hints on how to tweak the effect.
- You don't need to update all vertices all the time (in the first stage of the animation most of them stay unchanged).
- Play with grid's dimensions and find values that best meet your needs
- In 2d case you don't need to use a grid at all, it will be much faster if you use just a few triangles (at least four I guess) but it'll be much more difficult to draw them properly.
- Take into account a density of the grid, if you animate only right side of your book then left side doesn't need to be covered by grid (or grid can be thinner on this side).
- Think about adding some shadows/lights to make the effect more realistic (I would try to write a pixel shader or play with a shadow texture).
- Note that the effect may look different on different platforms as it depends on many factors (e.g. camera settings, projection matrix, viewport resolution, etc.).
본 블로그는 페이스북 댓글을 지원합니다.