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:
Wednesday, August 15th, 2007