PageFlip + Tweener AS3 Part 1
Update: 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 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.
[SWF]http://blog.hydrotik.com/wp-content/pageflip.swf, 500, 300[/SWF]
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:
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:
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.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:
October 25th, 2007 at 5:26 pm
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.
August 28th, 2008 at 2:56 pm
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
August 28th, 2008 at 3:14 pm
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.
September 27th, 2008 at 7:31 am
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
October 2nd, 2008 at 7:59 am
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()
October 2nd, 2008 at 8:29 am
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.
December 15th, 2008 at 3:50 pm
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 :).
December 19th, 2008 at 2:51 pm
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
December 19th, 2008 at 2:59 pm
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
December 19th, 2008 at 3:00 pm
Thanks Kapri! Glad it helped… it's not much, but hopefully a start.
Cheers!