How to Create a Page Curl in Corona

Share on Facebook0Share on Google+0Tweet about this on TwitterShare on LinkedIn0

Page curl effect in Corona SDKAs Corona SDK rises as one of the top platforms for eBook app publishing, the demand for a great-looking page curl effect is also growing.

Therefore, we’ve put together a small project and tutorial that goes over the basic implementation so you can go ahead and implement a page curl in your own apps. Most likely, you’ll have to make adjustments based on how your app is designed (whether you’re using storyboard, etc.)

At the end of this tutorial, there’s a short video that demonstrates this very page curl effect in action.

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.

Share on Facebook0Share on Google+0Tweet about this on TwitterShare on LinkedIn0

This entry has 24 replies

  1. Pukki Visuals says:

    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* πŸ˜‰

  2. Dave Haynes says:

    When you guys post code in your blog using gist, it doesn’t show up in google reader… You might try something like for posting code to the blog. I was reading it in Google Reader and going, what the heck? No code?

  3. Jose says:

    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?


  4. vlaD says:

    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 and consider this service to be the most flexible concerning design opportunities among its competitors.

  5. john says:

    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.

  6. John says:

    Great!! Thanks.

  7. Tanya says:

    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 πŸ™‚

  8. Alex says:

    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!!!

  9. 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)

  10. iAZZoZiTTi says:

    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 =
    if event.phase == “moved” then
    if event.xStart > event.x then
    gotoNext (currentPage, curlPage, 1000)
    elseif event.x > event.xStart then
    return true
    – 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 ^_^

  11. Vinicius says:

    It’s possible to use this code with Director class or even Storyboard? Anyone had already tried?


  12. Hey guys, it’s possible to use this code with Director class or even Storyboard? Anyone had already tried?


  13. Simon says:

    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 turnGroup, {maskX=display.contentWidth*0.5+100, time=1500 } )
    turnPreviousPageImage.isVisible = false
    turnNextPageImage.isVisible = false
    background.isVisible = false curlPage, { rotation=45, x=display.contentWidth+(display.contentWidth*0.10), y=display.contentHeight + (display.contentHeight*0.25), time=1000, onComplete=hideCurl })


    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” )

    — 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 turnGroup, { maskX=-display.contentWidth*0.75, time=1500, onComplete=hideCurrentPage } ),{time=1500,alpha=0})
    turnPreviousPageImage.isVisible = false
    background.isVisible = false curlPage, { rotation=0, x=0, y=display.contentHeight+20, time=1500, onComplete=changePage} )
    curlPage.yScale = curlPage.y * 0.2

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


    Hope this helps.

  14. @Simon

    I’ll try it. =]


  15. Robert says:

    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.

  16. Marcos says:

    Dear sirs,

    How can I implement Page Curl Effect in StoryBoard ?

    Thank you,

  17. Jeffrey says:


    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?

  18. Alan says:

    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?

  19. Basanta says:

    i want to curl the page from bottom to top,can anybody please help me????????

  20. Luciane says:

    Has anyone implemented page curl with storyboard?

  21. FreeDevz says:

    Having same problem as Alan, any updates on this?

  22. Aidan says:

    The page curl is cool but I do not know how to add a page to my Corona quiz application.

    • Brent says:

      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.