PageFlip + Tweener AS3 Part 1
Posted: August 15, 2007 at 10:35 pmUpdate: I had a bit of an oversight in the code. This should be using the setChildIndex method instead of swap depths. I've added more pages, updated the code, and thanks to Harry Burt for catching that. Also thanks to him for the addition of the page turning back code. There still is an issue with depth levels if the user clicks on the pages too fast. Still haven't had time to address that, but I think by adding a dispatcher at the midway point, the depth can be set then.
I'm sure most people have had the urge/need to do some sort of interface using that slick page flipping code that was floating around in the AS1 and AS2 heyday. They might be slightly dated, but I still like them in the right context. Obviously you can find the AS2 version out there. As for AS3, if you're lazy efficient like me, you did a search to see what was out there. First I came across Didier Brun's class PageFlip which is located Here. He gives a great example but you are only left with the usage code of this:
var render:Shape=new Shape(); var page0:BitmapData=new page0().bitmapData; var page1:BitmapData=new page1().bitmapData; render.x=50; render.y=50; addChild(render); var o:Object=PageFlip.computeFlip( new Point(60,190), // flipped point new Point(1,1), // of bottom-right corner page0.width, // size of the sheet page1.height, true, // in horizontal mode 1); // sensibility to one PageFlip.drawBitmapSheet(o, // computeflip returned object render, // target page0, // bitmap page 0 page1); // bitmap page 1
That's great, but more then likely it left you wondering what came next. Didier's excellent class is the foundation for all that is page flipping in AS3 (unless of course you want to reinvent the wheel). That being said, the next step was seeing hoe to put the core code into action. My next search took me to Ruben Swieringa's site. He has an amazing and fully functioning flex application using Didier's PageFlip class. Check it out Here. Pretty solid and feature rich, and it's available for use. Kudos to him. However, if you are like me and you don't work with flex, Ruben's fine work simply serves as an example of the complexity of making the book fully interactive.
So I decided to embark on my own simpler, AS3 only version. I doubt it will be nearly as feature rich as Ruben's flex version, but at the very least I can post a moving foundation for people to see and build upon.
I think the most exciting thing for me was how easy it was to use Tweener to flip a page using its bezier property along with it's onUpdate callback.
This is great for a flipPage() method, but obviously we'll want to turn the page with mouse interaction as well. More of that to come later. But for the time being here's the Page class with a simple turning animation and some events:
package com.hydrotik.book{ import flash.display.Sprite; import flash.display.Shape; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.MouseEvent; import flash.events.EventDispatcher; import flash.geom.Point; import com.foxaweb.pageflip.PageFlip; import caurina.transitions.Tweener; public class Page extends EventDispatcher { private var _scope:*; private var flip:Point; private var front:BitmapData; private var back:BitmapData; private var render:Shape; private var _width:Number; private var _height:Number; private var _page:Sprite; private var _oPage:Page; private var _name:String; public function getName():String { return _name; } public function Page(scope:*, w:Number, h:Number, x:Number, y:Number, m1:*, m2:*, name:String) { _scope = scope; _width = w; _height = h; _oPage = this; trace(_oPage); _name = name; _page = new Sprite() _page.x = x; _page.y = y; _scope.addChild(_page); render = new Shape(); _page.addChild(render); front = new BitmapData(_width, _height, true, 0); front.draw(m1); back = new BitmapData(_width, _height, true, 0); back.draw(m2); flip = new Point(_width, _height); drawPage(); _page.addEventListener(MouseEvent.CLICK, turnPageForward); } private function turnPageForward(event:MouseEvent = null):void{ dispatchEvent(new PageEvent(PageEvent.FLIP_START, _page, _oPage, true)); _page.removeEventListener(MouseEvent.CLICK, turnPageForward); _page.addEventListener(MouseEvent.CLICK, turnPageBack); Tweener.addTween(flip, { x:-_width, y:_height, _bezier:{x:0, y:0}, time:1.5, transition:"easeinoutexpo", onUpdate:drawPage, onComplete:onPageDone }); } private function turnPageBack(event:MouseEvent = null):void{ dispatchEvent(new PageEvent(PageEvent.FLIP_START, _page, _oPage, true)); _page.addEventListener(MouseEvent.CLICK, turnPageForward); _page.removeEventListener(MouseEvent.CLICK, turnPageBack); Tweener.addTween(flip, { x:_width, y:_height, _bezier:{x:0, y:0}, time:1.5, transition:"easeinoutexpo", onUpdate:drawPage, onComplete:onPageDone }); } private function drawPage():void{ render.graphics.clear() var o:Object = PageFlip.computeFlip(flip,// flipped point new Point(1,1),// of bottom-right corner front.width,// size of the sheet back.height, true,// in horizontal mode 1);// sensibility to one // sensibility to one PageFlip.drawBitmapSheet(o,// computeflip returned object render,// target front,// bitmap page 0 back);// bitmap page 1 } private function onPageDone():void{ dispatchEvent(new PageEvent(PageEvent.FLIP_COMPLETE, _page, _oPage, true)); } } }
Here's the custom PageEvent class:
package com.hydrotik.book { import flash.events.Event; public class PageEvent extends Event { public static const FLIP_START:String = "flipStart"; public static const FLIP_COMPLETE:String = "flipComplete"; public var targ:*; public var page:Page; public function PageEvent(type:String, t:*, p:Page, bubbles:Boolean = false, cancelable:Boolean = false){ super(type, bubbles, cancelable); targ = t; page = p; } } }
And here is the usage:
import com.hydrotik.book.Page; import com.hydrotik.book.PageEvent; var page5:Page = new Page(this, 200, 200, 250, 50, new Page5(), new Page6(), "Pages 5 and 6"); page5.addEventListener(PageEvent.FLIP_START, onFlipStartHandler); page5.addEventListener(PageEvent.FLIP_COMPLETE, onFlipCompleteHandler); var page3:Page = new Page(this, 200, 200, 250, 50, new Page3(), new Page4(), "Pages 3 and 4"); page3.addEventListener(PageEvent.FLIP_START, onFlipStartHandler); page3.addEventListener(PageEvent.FLIP_COMPLETE, onFlipCompleteHandler); var page1:Page = new Page(this, 200, 200, 250, 50, new Page1(), new Page2(), "Pages 1 and 2"); page1.addEventListener(PageEvent.FLIP_START, onFlipStartHandler); page1.addEventListener(PageEvent.FLIP_COMPLETE, onFlipCompleteHandler); var prevPage:DisplayObject; function onFlipStartHandler(event:PageEvent):void{ trace("Flip Started! " + event.page.getName() + " - " + event.targ); setChildIndex(event.targ, this.numChildren - 1) } function onFlipCompleteHandler(event:PageEvent):void{ trace("Flip Completed! " + event.page.getName() + " - " + event.targ); prevPage = event.targ; } stop();
Simple, and not much going on here, but it's easy to get a nice transition and we can see something happening! Next is calculating the mouse over the corner, getting a little rollover effect before turning the page, and being able to manually turn the page.
If anyone builds on this example or has one of their own, definitely let me know:)
Here's the code:
The Discussion
see what everyone is saying
Donovan, I like your approach to this by using caurinas transition package. I'm extending your work to hopefully catch up to Rubens flex package.
Hi there, great work. I was wondering if you had done any more to this regarding mouse Events? I'm new to AS3 and I'm curious to see how its done. Keep up the good work
I actually have a little bit, but not so much with interaction yet. I had fixed the depth issue, but can't seem to find the files and not sure why I didn't post it. Must have fallin through the cracks. I've removed Tweener as I've replaced it with HydroTween in all my work, although I'm using a self contained bezier function with this thing now.
I do plan on posting an update to this once it's worthy of doing so.
With the unit as it is now.. it's simply using a CLICK event to trigger a bezier curve to animate the position of the fold.
Hi, good article, nice1. Interested in seeing any updates? I really do like the page flip effect, but theres very little out there for AS3 Flash CS3. Would love to see your updated version!
cheers,
Andy
Hello, so I have everything all set in place but when its published, I am getting errors in the output window of
ReferenceError: Error #1069: Property _bezier not found on flash.geom.Point and there is no default value.
at caurina.transitions::Tweener$/::getPropertyValue()
at caurina.transitions::Tweener$/::updateTweenByIndex()
at caurina.transitions::Tweener$/::updateTweens()
at caurina.transitions::Tweener$/onEnterFrame()
Not seeing this error on my end with the original source. maybe update the tweener package? I don't use this engine anymore, but it sounds like it just need to be updated.
I'm trying to use this for a prototype of a app that simulates folding a piece of paper.
I did a few things to enhance your app. I added a method named 'turnPageBackward.' I hadn't used the tweener class and it took me a bit to figure out what dictates the direction of page turning. I found that by changing the tweener call form x:-_width to x:_width, I could change the direction.
I added a new event listener to the turnPageForward method to reverse the direction the page flips.
_page.removeEventListener(MouseEvent.CLICK,turnPageBackward); _page.addEventListener(MouseEvent.CLICK, turnPageForward);
I'm now working on: Flipping vertically with partial success. The PageFlip.computeFlip static method has a boolean that is supposed to dictate the direction. But, so far, I've had partial results. The page begins to flip down but stops halfway and hangs from a corner. It's funny how the tween makes is look real and I want to reach out and pull it down the rest of the way
.
Thanks man, this code really helped me out. All I kept finding were AS 2.0 classes and I was starting to worry about might having to write my own AS 3.0 page flip.
Thanks and greetings from Amsterdam
Hey John. Yeah I came back to this for another project and it got scrapped, so I haven't had a chance to clean it up and post it. Nothing to get excited about though lol
let me know how you make out with it. If you want I can send you what I got, or anyone else for that matter.
Cheers
Thanks Kapri! Glad it helped… it's not much, but hopefully a start.
Cheers!
Is there any AS2 version of the class?
I'm, pretty sure there are, but I don't know of any off the top of my head. maybe shoot Kapri a note?
I absolutely love this code.
HOWEVER – and pardon this noob question – how can I add event listeners to objects on the pages themselves?? Like a button located on the Page1 MC? It won't accept any btn inside the page MC itself.
Would appreciate any help greatly…
Hi,
thanks, this is great help.
how would i make the book turn the pages automatically ?
i want to animate a book turning pages in intervals of 3 seconds.
thanks
Folks, this code is a bit dated (2007) and is simply a jumping off point for your own development. For example I'm not even using Tweener anymore. Since this was done, I'm sure there are more further refined and posted examples.
If you come across anything, send me a comment and I can add it to the post.
[...] You said you’ll be extending Donovan’s PageFlip class http://blog.hydrotik.com/2007/08/15/pageflip-tweener-as3-part-1/#comment-478 [...]
This may be a stupid question but, on exporting the swf I get
package com.hydrotik.book{ does no reflect the location of the file please rename.
Does this mean it's importing the package form the site and will not work unless uploaded or am I just not seeing the hydrotik package in the file?
I would try downloading the source again. The classes are in the correct place and if I'm understanding you correctly it seems like you have a path issue to the classes.
hey yo,
has anybody made some progress with manually flipping the sides?
I am curious, and it would be a great help for me.
cheers,
Oliver