Posted on by

At the end of the tutorial, I’ll provide you with the resources needed to do this for an iPhone and an iPad in portrait orientation (landscape orientation resources are also included). If you’re targeting a different device configuration/orientation, then by the time you’re finished with the tutorial, you should have an idea of how to produce the page curl resources on your own (hint: the “animation” only consists of two images files).

While we don’t have an API for creating page curls just yet, it can still be done. Currently, there are two ways to implement the effect in Corona:

  1. Use a frame-based animation. This requires you have several full-screen images that will “flip” through to create the animation (similar to how you’d animate a character, but using much larger resources). This method is obviously a resource-hog (and could actually crash your app, depending on your target device).
     
  2. Create an “optical illusion” using a single image, a bitmap mask, and transitions (recommended).

In case you haven’t already guessed, we’ll be going over option #2.

Step 1. Preliminary Work

In order to create page curl effect, you’ll obviously need some pages. Most likely, the “pages” in your app will probably be represented by display groups. For the sake of simplicity, we’ll use two static images to represent the pages: page1.jpg and page2.jpg.

Corona eBook Pages

You’ll also need two more images. One that will represent the “page curl”, and a bitmap mask. With the mask, pay close attention to the fact that it has black edges on the top, bottom, and right-sides of the image:

Corona Page Curl and Mask

And here’s the code to load the pages, and the curl image (loading the bitmap mask will come later):

Step 2. Page Curl Functions

The functions below are responsible for beginning the next and previous page-turning effects. I’ll explain the logic behind it all, but first, here’s the code:

The gotoNext() is responsible for a “next page” curl effect, while the gotoPrevious() does the opposite (initiates a “previous page” curl effect).

Here’s the order-of-logic for the gotoNext() function:

  1. Insert the page-to-turn and the curl image into the ‘turnGroup’ display group.
     
  2. Apply the bitmap mask to the turnGroup.
     
  3. Ensure the reference point for the page and curl is set to the bottom right.
     
  4. Rotate the curl image and place it in its start position.
     
  5. Begin the transition.

And here’s gotoPrevious() in a nutshell:

  1. Ensure the curl image is visible.
     
  2. Setup the onComplete listener for the transition (which is responsible for hiding the page curl and removing the bitmap mask from the turnGroup).
     
  3. Begin the animation transition.

Step 3. Initiate the Page Curl

For demonstration purposes, I’ll call gotoNext() after a 2 second delay, and then 3 seconds later the page will turn back to the first page:


timer.performWithDelay( 2000, function() gotoNext( page1, curlPage, 500 ); end )
timer.performWithDelay( 5000, function() gotoPrevious( curlPage, 500 ); end )

And here is is in action:

And there you have it—a lightweight, efficient way to implement a great-looking page curl effect in Corona. Remember, you’ll have to create your own page curl graphic and mask depending on your target resolution and orientation, but as long as you make them look similar to the one’s shown in this tutorial, the outcome should be the same.

Also, depending on your target resolution, you may have to make small adjustments as far as where the page curl image initially begins, how fast the animation goes, etc. In the project files, I’ve included masks and pages for iPhone and iPad dimensions in both portrait and landscape orientations.

When you’re ready: Download the project files from Github.


Posted by . Thanks for reading...

24 Responses to “How to Create a Page Curl in Corona”

  1. Pukki Visuals

    Thanx for this! Great stuff and gives me an idea of Corona “image-transition” module. That would be cool and come in need for many coders out there. Actually should be built-in.. *wink wink* ;)

    Reply
  2. Jose

    Hey Jonathan,

    Great job on the eff

    I admit to not having read the code yet, but is it possible to have the the back of the page that’s curling show a simulated “see thru” effect of the front page as in iBooks for the iPhone? Also, how would this work with an iPad in landscape? Will the curl also show the back page if there’s content on the front and on the back?

    Thanks

    Reply
  3. vlaD

    Cool stuff.I’m keen on creating apps though I’m only a novice in programming but that doesn’t prevent me from making complex and profitable apps.I’m using snappii.com and consider this service to be the most flexible concerning design opportunities among its competitors.

    Reply
  4. john

    Hi Jonathan,
    You have a video on Youtube on “How to make a character Jump”. I am really interested in it. I was not able to see the codes by whatching the video and I can not find it anywhere on the blog. Can you please share the codes for that tutorial? Thank you for the help and great work as always.

    Reply
  5. Tanya

    Hi Jonathan,
    Nice tutorial. Any date yet on when the Corona Ebook under templates will be made available? Have been anxiously checking it for a couple of months now.
    Thanks :)

    Reply
  6. Alex

    Thanks for this great tutorial. What’s the best approach to add pages? I have about 100 pages I would like to integrate into this :)

    Thanks and happy new year!!!
    Alex

    Reply
  7. Robert Bingham

    Alex that easy way to do that is to Edit the main.lua file in Notepad, Notepadd ++ or editor of your choice. Ensure you dimensions on the “pages” is set to 320×480.

    and do the following with your code:

    local page3 = display.newImageRect( “page3.jpg”, display.contentWidth, display.contentHeight )
    page3:setReferencePoint( display.TopleftReferencePoint )
    page3:toBack() – make sure 3nd page is underneath the first one

    local page4 = display.newImageRect( “page4.jpg”, display.contentWidth, display.contentHeight )
    page4.x, page4.y = display.contentWidth*0.5, display.contentHeight*0.5
    page4:toBack() – make sure 4th page is underneath the first one

    local page5 = display.newImageRect( “page5.jpg”, display.contentWidth, display.contentHeight )
    page5:setReferencePoint( display.TopleftReferencePoint )
    page5:toBack() – make sure 5th page is underneath the first one

    local page6 = display.newImageRect( “page6.jpg”, display.contentWidth, display.contentHeight )
    page6.x, page6.y = display.contentWidth*0.5, display.contentHeight*0.5
    page6:toBack() – make sure 6th page is underneath the first one

    local page7 = display.newImageRect( “page7.jpg”, display.contentWidth, display.contentHeight )
    page7:setReferencePoint( display.TopleftReferencePoint )
    page7:toBack() – make sure 7th page is underneath the first one

    you will need to change the local page”Number”
    as well each subsequent page”” to refelect the page that you have. once your past that point you have to include how the page is handled once a user clicks or touches or attempts to swipe the page as this example is set to automatically transition the pages back and forth (see the last couple of lines of code in the main.lua file)

    Reply
  8. iAZZoZiTTi

    Thanks for this excellent tutorial .

    I have 2 questions :
    1st Question : – As you will see in the code below – I made 4 pages with this page curl feature and I registed the 2nd and 3rd pages to swipe function (event) which makes the pages curl when I swipe over them .

    ========================================
    local function swipe(event)
    local currentPage = event.target
    if event.phase == “moved” then
    if event.xStart > event.x then
    gotoNext (currentPage, curlPage, 1000)
    elseif event.x > event.xStart then
    gotoPrevious(curlPage,1000)
    end
    end
    return true
    end
    ========================================
    - The code before swipe function is same to the one in this tutorial except that I have 4 pages -

    page1:addEventListener( “touch” , function() gotoNext( page1,curlPage,1000 ) return true end )
    page2:addEventListener( “touch” , swipe )
    page3:addEventListener( “touch” , swipe )
    page4:addEventListener( “touch” , function() gotoPrevious(curlPage,1000) return true end )
    ========================================

    every things go right when I go from the 1st to 2nd and go back to 1st , and same thing when I go from 2nd to 3rd page and go back to 2nd , and same thing with the 3rd and 4th pages .

    The problem is that when I reach the 4th page and try to go back to the 2nd page throught the 3rd page , the curl page works fine but the 3rd page stuck and doesn’t move . And same thing when I reach the 3rd page and try to go back to the 1st page throught the 2nd page , the curl page works fine but the 2nd page stuck and doesn’t move .

    Obviously that when the 2nd and 3rd pages call the function gotoNext () which is found in the function swipe (event). After , they no longer call the function gotoPrevious () which is found in function swipe (event) as well .

    ***************************

    2nd Question : What I sould do to make the curl page and other pages curl from left to right to make like an Arabic book – who read and write from right to left – ?

    Sorry for my huge post and questions ^_^

    Reply
  9. Simon

    For Director Class…you can use it this way.
    -
    -====================================================
    local localGroup = display.newGroup()

    local background = display.newImage( “background.png” )
    local turnNextPageImage = display.newImage( “nextIcon.png” )
    turnNextPageImage.x = display.contentWidth/2
    turnNextPageImage.y = display.contentHeight/2

    local turnPreviousPageImage = display.newImage( “backIcon.png” )
    turnPreviousPageImage.x = display.contentWidth/2
    turnPreviousPageImage.y = display.contentHeight/2 + 100

    local page1 = display.newImageRect( “page1.jpg”, display.contentWidth, display.contentHeight )
    page1:setReferencePoint( display.TopleftReferencePoint )
    page1.x, page1.y = display.contentWidth*0.5, display.contentHeight*0.5
    page1.isVisible = false– = .3

    local curlPage = display.newImageRect( “curlPage.png”, display.contentWidth, display.contentHeight )
    curlPage.x, curlPage.y = display.contentWidth*0.5, display.contentHeight*0.5
    curlPage.isVisible = false

    — group to hold the page that will be turned (as well as the “curl” page)
    local turnGroup = display.newGroup()

    — The following function will turn the page “back”
    local function gotoPrevious( event )

    local time = time or 500
    if event.phase == “began” then
    curlPage.isVisible = true
    local hideCurl = function()
    curlPage.isVisible = false
    director:changeScene( “screen2″, “moveFromLeft” )
    end

    transition.to( turnGroup, {maskX=display.contentWidth*0.5+100, time=1500 } )
    turnPreviousPageImage.isVisible = false
    turnNextPageImage.isVisible = false
    background.isVisible = false
    transition.to( curlPage, { rotation=45, x=display.contentWidth+(display.contentWidth*0.10), y=display.contentHeight + (display.contentHeight*0.25), time=1000, onComplete=hideCurl })

    end
    end

    local function goToPage( event )
    if event.phase == “began” then

    turnNextPageImage:removeEventListener(“touch”, goToPage)
    turnGroup:insert( page1 )
    turnGroup:insert( curlPage )

    – mask should match dimensions of content (e.g. content width/height)
    local curlMask = graphics.newMask( “mask_320x480.png” ) – iPhone portrait
    –local curlMask = graphics.newMask( “mask_768x1024.png” ) – iPad portrait
    turnGroup:setMask( curlMask )

    – set initial mask position
    turnGroup.maskX = display.contentWidth * 0.5+100
    turnGroup.maskY = display.contentHeight * 0.5

    – prepare the page-to-be-turned and the curl image
    page1:setReferencePoint( display.BottomLeftReferencePoint )
    curlPage:setReferencePoint( display.BottomRightReferencePoint )
    curlPage.rotation = 45
    curlPage.x = display.contentWidth+(display.contentWidth*0.10)
    curlPage.y = display.contentHeight + (display.contentHeight*0.25)
    curlPage.isVisible = true

    local function changePage(event)
    director:changeScene( “screen3″, “moveFromLeft” )
    end

    – show pagecurl animation and transition away (next page should already be in position)
    local time = time or 500

    local function hideCurrentPage(event)
    curlPage.isVisible = false
    end

    transition.to( turnGroup, { maskX=-display.contentWidth*0.75, time=1500, onComplete=hideCurrentPage } )
    transition.to(turnNextPageImage,{time=1500,alpha=0})
    turnPreviousPageImage.isVisible = false
    background.isVisible = false
    transition.to( curlPage, { rotation=0, x=0, y=display.contentHeight+20, time=1500, onComplete=changePage} )
    curlPage.yScale = curlPage.y * 0.2
    end
    end

    turnNextPageImage:addEventListener(“touch”, goToPage)
    turnPreviousPageImage:addEventListener(“touch”, gotoPrevious)

    –================================================

    Hope this helps.

    Reply
  10. Robert

    The idea is nice, but both the example by Jonathan and the Director version by Simon are still “proof of concept”. When implementing the example by Jonathan with a series of pages I also encounter the issue reported by iAZZoZiTTi. And in the Director example you will see a black page during the curl – not very pretty. Anyway, as a “proof of concept” very valuable, but please do not stop working on a solid solution.

    Reply
  11. Marcos

    Dear sirs,

    How can I implement Page Curl Effect in StoryBoard ?

    Thank you,
    Marcos

    Reply
  12. Jeffrey

    Hello,

    I want to make an Ibook to upload to Apple Ibook store. Can I use your software to do this sort of book with curls or is this only for the app store?

    Reply
  13. Alan

    I have encountered the same problem as iAZZoZiTTi, in that if I want to use more than one page in this way I can only go back once (e.g. ig I go forward to page 5 I can only go back to page 4). If I try to go back futher, the ‘curl’ image shows up, but the page remains on the same page. I have tried using print statements in the goToNext() function, and have noticed that it does not always add the corerct number of objects to the turnGroup. I have a ‘pagetext’ object and a ‘pagebackground’ for each page and apply this to both pages. The first time it is called (on a pagebackground) it inserts 2 objects into the turnGroup (the currentPage and the curlPage), however when the pagetext object uses the same function (on the line immediately after the first call), it only inserts the currentPage parameter into the turnGroup – not the curlPage one. I presume that this is somehow causing the problem, but I have no idea why it is happening?

    Reply
    • Brent

      Hi @Aidan,
      As far as I can tell, the “pages” can be whatever you wish. If they’re going to be display groups versus just static images, you’ll have to adapt this a bit to bundle both groups into -another- parent group, in the proper z-index order so the “animated page” looks correct as it transitions.

      Reply

Leave a Reply

  • (Will Not Be Published)