package com.rubenswieringa.book { import com.foxaweb.pageflip.PageFlip; import com.rubenswieringa.geom.Geom; import com.rubenswieringa.managers.StateManager; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.*; import org.flashsandy.display.DistortImage; use namespace limited; /** * Dispatched when the user picks up the corner of a page. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGEFLIP_STARTED */ [Event(name="pageflipStarted", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched when the user releases the corner of a page. Note that this Event is dispatched just before the page starts falling in place. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_ENDING * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGEFLIP_FINISHED */ [Event(name="pageflipEnding", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched when a page falls in place after being flipped. This Event is dispatched regardless of whether or not the page has been turned, or has fallen back into its original position. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_FINISHED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGE_TURNED */ [Event(name="pageflipFinished", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched when the corner of a page is rolled over with the mouse. * Only applicable if the hover property is set to true. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#HOVER_STARTED * @see lib_flex_book_com_rubenswieringa_book_Book#hover */ [Event(name="hoverStarted", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched when the corner of a page is rolled out of with the mouse. Note that this Event is dispatched just before the page starts falling back in place. * Only applicable if the hover property is set to true. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGEFLIP_FINISHED * @see lib_flex_book_com_rubenswieringa_book_Book#hover */ [Event(name="hoverEnding", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched when a page falls back in place after being rolled over with the mouse. * Only applicable if the hover property is set to true. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_FINISHED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGE_TURNED * @see lib_flex_book_com_rubenswieringa_book_Book#hover */ [Event(name="hoverFinished", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched when a pageflip is successful. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.PAGE_TURNED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGE_NOT_TURNED */ [Event(name="pageTurned", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched when a pageflip is not successful. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.PAGE_NOT_TURNED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGE_TURNED */ [Event(name="pageNotTurned", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched when a Page is torn out of its lib_flex_book_com_rubenswieringa_book_Book. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.PAGE_TORN */ [Event(name="pageTorn", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched at the same time as the page-turned Event, when the lib_flex_book_com_rubenswieringa_book_Book was previously closed, and the first or last Page was flipped successfully. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.BOOK_OPENED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGE_TURNED */ [Event(name="bookOpened", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched at the same time as the page-turned Event, when the lib_flex_book_com_rubenswieringa_book_Book was previously open, and the first or last Page was flipped successfully. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.BOOK_CLOSED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGE_TURNED */ [Event(name="bookClosed", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * Dispatched at the same time as the page-turned Event, when the lib_flex_book_com_rubenswieringa_book_Book was previously open, and the first or last Page was flipped successfully. * @eventType com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent.BOOK_CLOSED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#STATUS_CHANGED * @see lib_flex_book_com_rubenswieringa_book_Book#status */ [Event(name="statusChanged", type="com.rubenswieringa.book.lib_flex_book_com_rubenswieringa_book_BookEvent")] /** * lib_flex_book_com_rubenswieringa_book_Book is a container class that creates a rich animated and interactive book from its contents, through which the end-user can browse by flipping the pages over (pageflip-effect). * Its core functionality (for example storage of Page instances) is defined by the PageManager class, which the lib_flex_book_com_rubenswieringa_book_Book class extends.
* lib_flex_book_com_rubenswieringa_book_Book itself describes the functionality for the management of pageflips. The actual pageflip class was written by Didier Brun (also mentioned in the credits).
*
* Ruben Swieringa created this component during his internship at the Factor.e (www.tfe.nl). Thanks to those guys for allowing me to publish the source-code online! * * @author Ruben Swieringa * ruben.swieringa@gmail.com * www.rubenswieringa.com * www.rubenswieringa.com/blog * @version 1.0.1 * @see PageManager PageManager * @see Page Page * @see lib_flex_book_com_rubenswieringa_book_BookEvent lib_flex_book_com_rubenswieringa_book_BookEvent * @see lib_flex_book_com_rubenswieringa_book_BookError lib_flex_book_com_rubenswieringa_book_BookError * @see com.foxaweb.pageflip.PageFlip com.foxaweb.pageflip.PageFlip * @see org.flashsandy.display.DistortImage org.flashsandy.display.DistortImage * @see http://www.rubenswieringa.com/blog/flex-book-component-beta Rubens blog: lib_flex_book_com_rubenswieringa_book_Book component beta * * * @internal * * * Credits: * - Didier Brun * For making his pageflip rendering class (com.foxaweb.pageflip.PageFlip) * Site: www.foxaweb.com * - Thomas Pfeiffer (aka Kiroukou) * For letting me use his distortion class (org.flashsandy.display.DistortImage) * Site: www.flashsandy.org * - the Factor.e * For allowing me to publish the demo and the source-code. * Site: www.tfe.nl * - Maikel Sibbald * For helping me with (among a lot of things) thinking out the structure of this component. He also made a usage-example of Didier's pageflip class (labs.flexcoders.nl/?p=33) which I used as reference in the early days of the lib_flex_book_com_rubenswieringa_book_Book class. * Site: labs.flexcoders.nl * - Theo Aartsma (aka Sumeco) * For letting me use his artwork in the lib_flex_book_com_rubenswieringa_book_Book demo. * Site: www.sumeco.net * * * edit 5 * * * View code documentation at: * http://www.rubenswieringa.com/code/as3/flex/lib_flex_book_com_rubenswieringa_book_Book/docs/ * * * Copyright (c) 2005 Ruben Swieringa. All rights reserved. * * This class is part of the lib_flex_book_com_rubenswieringa_book_Book component, which is licensed under the CREATIVE COMMONS Attribution 3.0 Unported. * You may not use this file except in compliance with the License. * You may obtain a copy of the License at: * http://creativecommons.org/licenses/by/3.0/deed.en * */ public class lib_flex_book_com_rubenswieringa_book_Book extends PageManager { /** * Array in which the BitmapData for each Page instance is stored in. Note that after each pageflip this Array is cleaned. * @private */ protected var bitmapData:Array = []; /** * DistortImage instance for rendering hard-back Pages during pageflips. * @see org.flashsandy.display.DistortImage * @private */ protected var distortion:DistortImage; /** * @private */ protected var pageCorner:Point = new Point(); /** * @private */ protected var pageCornerTarget:Point = new Point(); /** * The direction of the last pageflip. 1 stands for forward flipping (flipping a page from right to left), -1 stands for backward flipping (left to right). * @private */ protected var lastFlippedDirection:int = 0; /** * The side on which the last flipped Page laid before it was flipped. 0 stands for left, 1 for right. * @see Page#LEFT * @see Page#RIGHT * @private */ limited var lastFlippedSide:uint; /** * The position of the last flipped page-corner. The x-value represents the horizontal value, the y-value represents the vertical value. So (0, 0) would be the upper-left corner, (1, 1) would be the lower right corner, etc. * @private */ protected var lastFlippedCorner:Point; /** * Stores the timestamp at which a pageflip was started (mouse was pressed). * This property is part of the mechanism that decides whether or not a click should fire an automated pageflip. * @see lib_flex_book_com_rubenswieringa_book_Book#setLastFlippedTime() * @private */ protected var lastFlippedTime:int; /** * False if the last flipped page fell back into its original position after being flipped. True if it slided over to the opposite side. * @private */ protected var lastFlipSucceeded:Boolean; /** * Set by the gotoPage() method as a target-destination. * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() * @private */ protected var autoFlipIndex:int = -1; /** * False if the current pageflip is performed by the user dragging a pagecorner, true of not. * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() * @see lib_flex_book_com_rubenswieringa_book_Book#tearPage() * @private */ protected var autoFlipActive:Boolean = false; /** * Indicates if an automated pageflip is allowed to be interrupted by the user. * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() * @see lib_flex_book_com_rubenswieringa_book_Book#tearPage() * @private */ protected var autoFlipCancelable:Boolean = true; /** * True if a pageflip is triggered by a mouse-rollover, false if not. * @private */ protected var hoverActive:Boolean = false; /** * True if the current pageflip is performed upon a page its side (instead of one of its corners). * @see lib_flex_book_com_rubenswieringa_book_Book#sideFlip * @private */ protected var sideFlipActive:Boolean = false; /** * True if this pageflip consists of a Page being torn out of this lib_flex_book_com_rubenswieringa_book_Book. * @see lib_flex_book_com_rubenswieringa_book_Book#tearPage() * @private */ limited var tearActive:Boolean = false; /** * The side of the page that is being torn out. 0 stands for left, 1 for right. * @see lib_flex_book_com_rubenswieringa_book_Book#tearPage() * @see Page#LEFT * @see Page#RIGHT * @private */ protected var tearSide:uint; /** * True if a page is being torn out of the book top-first, false if bottom-first. * @see lib_flex_book_com_rubenswieringa_book_Book#tearPage() * @private */ protected var tearFromTop:Boolean; /** * Array of Rectangle instances that indicate the areas by which a page can be flipped. * @see lib_flex_book_com_rubenswieringa_book_Book#createRegions() * @private */ protected var regions:Array = []; // internals for accessors: /** * @see lib_flex_book_com_rubenswieringa_book_Book#easing * @private */ protected var _easing:Number = 0.3; /** * @see lib_flex_book_com_rubenswieringa_book_Book#hardCover * @private */ protected var _hardCover:Boolean = true; /** * @see lib_flex_book_com_rubenswieringa_book_Book#hover * @private */ protected var _hoverEnabled:Boolean; /** * @see lib_flex_book_com_rubenswieringa_book_Book#regionSize * @private */ protected var _regionSize:uint = 150; /** * @see lib_flex_book_com_rubenswieringa_book_Book#status * @private */ protected var _status:String; // plain var properties: /** * The time (milliseconds) it takes to finish a pageflip performed by the gotoPage method. * The calculated speed will also be used for tearing out Pages. * @default 1000 * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() */ public var autoFlipDuration:uint = 1000; /** * Whether or not the page should be turned when a page corner is clicked. * @default true */ public var flipOnClick:Boolean = true; /** * Amount of perspective in a pageflip of a Page that has its hard property set to true. * This value is used as the maximum with which the outer side of a page is extended on both sides (upper and lower) during a hard pageflip. * @default 45 */ public var hardPerspective:uint = 45; /** * Indicates whether or not this lib_flex_book_com_rubenswieringa_book_Book plays animation while flipping one of its Pages. * When this property is set to true, all Page instances will return true from their liveBitmapping property. * Note that enabling liveBitmapping may result in decreased performance. * @default false * @see Page#liveBitmapping */ public var liveBitmapping:Boolean = false; /** * Whether or not the outer side of a Page can be used to let it flip. * @default true */ public var sideFlip:Boolean = true; /** * If true, no easing will be applied during pageflips. * @default false * @see lib_flex_book_com_rubenswieringa_book_Book#easing */ public var snap:Boolean = false; /** * If true, all Pages will have tearing enabled. * @see Page#tearable * @default false */ public var tearable:Boolean = false; // constants: /** * @private */ protected static const AUTO_FLIP_CURVE:Number = 0.15; /** * @private */ protected static const SIDE_FLIP_CURVE:Number = 0.04; /** * @private */ protected static const MAX_EASING_DIFFERENCE:Number = 0.5; /** * @private */ protected static const CLICK_INTERVAL:uint = 300; // CONSTRUCTOR: /** * Constructor. */ public function lib_flex_book_com_rubenswieringa_book_Book ():void { super(); // set initial status: this.setStatus(lib_flex_book_com_rubenswieringa_book_BookEvent.NOT_FLIPPING, false); // make sure certain getter/setter values are processed where appropriate: this.easing = 0.7; this.hover = true; // add event listener: this.addEventListener(MouseEvent.MOUSE_DOWN, this.startPageFlip); } // OVERRIDES: /** * Draw additional graphisc for Pages where necessary. * * @private */ override protected function childrenCreated ():void { super.childrenCreated(); // make sure that non-hard pages whose flipsides are hard don't have fold-gradients: for (var i:int=0; i= 0 && this.pageCorner.x <= this.width/2 && event != null){ this.cancelGotoPage(false); return; } // stop if the clicked SuperViewStack does not show any Pages: if (((!this.pageL.visible && side == Page.LEFT) || (!this.pageR.visible && side == Page.RIGHT)) && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_ENDING && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING){ return; } // stop if none of the pagecorners are hit: if (!this.isPageCornerHit() && (!this.sideFlip || !this.isPageSideHit()) && !this.autoFlipActive){ return; } // don't flip if Page isn't allowed to: if (Page(this._pages.getItemAt(this._currentPage+side)).lock){ this.autoFlipActive = false; // shut-down auto-pageflip if active return; } // stop if a page corner is flipping back into position if ((this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_ENDING || this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING) && !this.autoFlipActive && !this.lastFlipSucceeded){ // switch back to flipping mode if the same page corner was picked up: if (this.lastFlippedCorner.equals(this.getCurrentCorner()) && this.sideFlipActive == this.isPageSideHit()){ this.stage.addEventListener(MouseEvent.MOUSE_UP, this.endPageFlip); var newStatus:String = (this.hoverActive) ? lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED : lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED; this.setStatus(newStatus, true, oldPage); } return; } // stop if the lib_flex_book_com_rubenswieringa_book_Book is not inactive and not hovering: if (this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.NOT_FLIPPING && !this.hoverActive){ return; } // if the page is hovering and an actual pageflip should begin: if (this.hoverActive && this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED){ this.hoverActive = false; this.setLastFlippedTime(); // if necessary, remember time at which pageflip started this.setStatus(lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED, true, oldPage); return; } // throw an Error if the Page in question has no flipside: // in the above stated situation the right-hand side Page typically does not have a flipside, and consequently its index will be equal to the total amount of pages (minus one). Also note that any left-hand side Page will always have a flipside, because the first Page in a lib_flex_book_com_rubenswieringa_book_Book is always on the right-hand side. if (this._currentPage+1 == this._pages.length-1 && side == Page.RIGHT){ throw new lib_flex_book_com_rubenswieringa_book_BookError(lib_flex_book_com_rubenswieringa_book_BookError.NO_FLIPSIDE); } // set hover-flag: this.hoverActive = attemptHover; // set direction markers and position the render: if ( this.autoFlipActive && !this.tearActive) this.lastFlippedCorner = new Point(side, 1); if ( this.autoFlipActive && this.tearActive) this.lastFlippedCorner = new Point(side, (this.tearFromTop) ? 0 : 1); if (!this.autoFlipActive) this.lastFlippedCorner = this.getCurrentCorner(); this.lastFlippedSide = this.lastFlippedCorner.x; this.lastFlippedDirection = (side == Page.LEFT) ? -1 : 1; this.sideFlipActive = (!this.tearActive && this.sideFlip && this.isPageSideHit()); this.render.x = this.lastFlippedSide * this.width/2; // specify front and flipside indexes: var frontIndex:uint = this._currentPage + this.lastFlippedSide; var backIndex:uint = this._currentPage + this.lastFlippedSide + this.lastFlippedDirection; // save bitmapData: this.saveBitmapData(Page(this._pages.getItemAt(frontIndex)), Page(this._pages.getItemAt(backIndex))); // select pages in SuperViewStacks: if (this.lastFlippedSide == Page.LEFT){ // if left-hand page was flipped this.pageL.visible = !this.isFirstPage(this._currentPage+1-2); if (this.pageL.visible){ this.pageL.selectedChild = Page(this._pages.getItemAt(this._currentPage-2)); }else{ } } if (this.lastFlippedSide == Page.RIGHT){ // if right-hand page was flipped this.pageR.visible = !this.isLastPage(this._currentPage+2); if (this.pageR.visible){ this.pageR.selectedChild = Page(this._pages.getItemAt(this._currentPage+1+2)); } } // set page corner markers: this.pageCorner = new Point(this.lastFlippedCorner.x*this.width/2, this.lastFlippedCorner.y*this.height); if (this.autoFlipActive){ this.pageCornerTarget = this.pageCorner.clone(); } // if necessary, remember time at which pageflip started: this.setLastFlippedTime(); // set status: this.setStatus((this.hoverActive) ? lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED : lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED, false); // false = dispatch Event later // add listeners: this.dragPageCorner(); this.addEventListener(Event.ENTER_FRAME, this.dragPageCorner); this.stage.addEventListener(MouseEvent.MOUSE_UP, this.endPageFlip); // dispatch event: var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide)); if (this.hoverActive){ this.dispatchEvent(new lib_flex_book_com_rubenswieringa_book_BookEvent(lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED, this, page)); }else{ this.dispatchEvent(new lib_flex_book_com_rubenswieringa_book_BookEvent(lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED, this, page)); } } /** * Performs the actual pageflip effect, typically called upon enter-frame. * * @see com.foxaweb.pageflip.PageFlip#computeFlip() * @see com.foxaweb.pageflip.PageFlip#drawBitmapSheet() * * @param event Event * * @private */ protected function dragPageCorner (event:Event=null):void { // stop if the startPageFlip() or endPageFlip() have not been executed: if (this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_ENDING && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING){ return; } // create faux mouse to create easing: if (this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED || this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED){ if (this.movePageCornerTarget() && this.autoFlipActive){ this.endPageFlip(); return; } } this.movePageCorner(); // clear render: this.render.graphics.clear(); // check if the pageflip has ended: if (this.pageCorner.equals(this.pageCornerTarget) && (this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_ENDING || this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING)){ this.finishPageFlip(); return; } // specify front and flipside indexes: var frontIndex:uint = this._currentPage + this.lastFlippedSide; var backIndex:uint = this._currentPage + this.lastFlippedSide + this.lastFlippedDirection; var front:Page = Page(this._pages.getItemAt(frontIndex)); var back:Page = Page(this._pages.getItemAt(backIndex)); // if liveBitmapping is enabled, refresh corresponding values in bitmapData Array: if (front.liveBitmapping || back.liveBitmapping){ this.saveBitmapData(front, back); } // perform pageflip: if (!front.explicitHard && !back.explicitHard){ var ocf:Object = PageFlip.computeFlip (this.pageCorner.clone(), this.lastFlippedCorner, this.width/2, this.height, !this.tearActive, 1); PageFlip.drawBitmapSheet (ocf, this.render, this.bitmapData[frontIndex], this.bitmapData[backIndex]); // add shadows or highlights to the render: this.addSmoothGradients(ocf); // take ocf and find out whether we should start tearing this Page off: if (this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED && Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide)).tearable && ocf.cPoints != null){ this.evaluateTear(ocf.cPoints[2].clone()); } }else{ this.drawHardPage (this.bitmapData[frontIndex], this.bitmapData[backIndex]); } } /** * Makes preparations for the end of the pageflip effect, typically called upon mouse-release. * * @param event MouseEvent * * @private */ protected function endPageFlip (event:MouseEvent=null):void { // stop if the lib_flex_book_com_rubenswieringa_book_Book is not currently performing a pageflip: if (this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED){ return; } // ignore mouse-up event if a Page is being torn out of the lib_flex_book_com_rubenswieringa_book_Book: if (this.tearActive && event != null){ return; } // turn page if flipOnClick is enabled: if (this.flipOnClick && this.flipOnRelease && event != null){ this.gotoPage(int(this._currentPage+this.lastFlippedDirection*2)); // cast to int because gotoPage has its page parameter typed with a * and will assume the value is an uint, and thus change any negative values to positives return; } // remove mouse-listener: this.stage.removeEventListener(MouseEvent.MOUSE_UP, this.endPageFlip); // make sure page corner slides to the appropriate position: if (!this.tearActive){ var x:Number; var y:Number = this.lastFlippedCorner.y*this.height; if (this.lastFlippedSide == Page.LEFT){ // if left-hand page was flipped this.lastFlipSucceeded = (this.pageCornerTarget.x > this.width/2); x = (this.lastFlipSucceeded) ? this.width : 0; }else{ // if right-hand page was flipped this.lastFlipSucceeded = (this.pageCornerTarget.x < 0); x = (this.lastFlipSucceeded) ? -this.width/2 : this.width/2; } this.pageCornerTarget = new Point(x, y); }else{ this.lastFlipSucceeded = false; } // set status and dispatch event: var newStatus:String = (this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED) ? lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING : lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_ENDING; var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide)); this.setStatus(newStatus, true, page); } /** * Ends the pageflip effect and displays the interactive content in the SuperViewStacks. * * @private */ protected function finishPageFlip ():void { // stop is endFlip() has not already been called: if (this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_ENDING && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING){ return; } // stop the dragPageCorner() method from being executed: this.removeEventListener(Event.ENTER_FRAME, this.dragPageCorner); // remember current Page for Events dispatched later: var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide)); // remove Pages if they were torn out: if (this.tearActive){ var wasLastPage:Boolean = this.isLastPage(this._currentPage); this.removeChild(page.getFlipSide()); this.removeChild(page); } // if the page has been flipped over, change the _currentPage property: if (this.lastFlipSucceeded && !this.tearActive){ // lastFlipSucceeded is always false when a Page was torn, but this makes the code more readable this._currentPage += this.lastFlippedDirection * 2; } // change _currentPage where appropriate after a page-tear: if (this.tearActive && this.lastFlippedSide == Page.LEFT && !wasLastPage){ this._currentPage -= 2; } // make sure the SuperViewStacks display the right Pages this.refreshViewStacks(); // set status: this.setStatus(lib_flex_book_com_rubenswieringa_book_BookEvent.NOT_FLIPPING, true, page, (this.hoverActive) ? lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_FINISHED : lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_FINISHED); // dispatch additional events: if (!this.hoverActive){ if (!this.tearActive){ if (this.lastFlipSucceeded){ this.dispatchEvent(new lib_flex_book_com_rubenswieringa_book_BookEvent(lib_flex_book_com_rubenswieringa_book_BookEvent.PAGE_TURNED, this, page)); if ((this.lastFlippedSide == Page.RIGHT && this._currentPage == 1) || (this.lastFlippedSide == Page.LEFT && this._currentPage == this._pages.length-3)){ this.dispatchEvent(new lib_flex_book_com_rubenswieringa_book_BookEvent(lib_flex_book_com_rubenswieringa_book_BookEvent.BOOK_OPENED, this)); } if ((this.lastFlippedSide == Page.LEFT && this._currentPage == -1) || (this.lastFlippedSide == Page.RIGHT && this._currentPage == this._pages.length-1)){ this.dispatchEvent(new lib_flex_book_com_rubenswieringa_book_BookEvent(lib_flex_book_com_rubenswieringa_book_BookEvent.BOOK_CLOSED, this)); } }else{ this.dispatchEvent(new lib_flex_book_com_rubenswieringa_book_BookEvent(lib_flex_book_com_rubenswieringa_book_BookEvent.PAGE_NOT_TURNED, this, page)); } }else{ this.dispatchEvent(new lib_flex_book_com_rubenswieringa_book_BookEvent(lib_flex_book_com_rubenswieringa_book_BookEvent.PAGE_TORN, this, page)); } } // if this was a pageflip triggered by gotoPage then switch back to normal mode: if (this.autoFlipActive){ if (this.tearActive){ this.tearActive = false; this.autoFlipActive = false; }else{ if (this._currentPage == this.autoFlipIndex){ this.autoFlipActive = false; }else{ this.startPageFlip(); } } } // turn off flags: this.hoverActive = false; this.sideFlipActive = false; this.tearActive = false; } /** * Draws a hardcover Page, similar to the drawBitmapSheet method of the PageFlip class. * * @see com.foxaweb.pageflip.PageFlip#drawBitmapSheet() * * @param front BitmapData of the facing side of a sheet * @param back BitmapData of the flipside of a sheet * * @private * */ protected function drawHardPage (front:BitmapData, back:BitmapData):void { // calculate position correction: var w:Number; if (this.lastFlippedDirection == 1){ w = this.pageCorner.x - (1-this.lastFlippedSide) * this.width/2; }else{ w = this.pageCorner.x - this.width/2; } if (w < -this.width/2) w = -this.width/2; if (w > this.width/2) w = this.width/2; // calculate perspective: var closeness:Number = Math.sin(Math.acos(w / (this.width/2))); // define positions of page-corners: var pPoints:Array = []; pPoints[0] = new Point((1-this.lastFlippedSide)*this.width/2, 0); pPoints[1] = new Point(pPoints[0].x+w, 0-closeness*this.hardPerspective); pPoints[2] = new Point(pPoints[0].x+w, this.height+closeness*this.hardPerspective); pPoints[3] = new Point((1-this.lastFlippedSide)*this.width/2, this.height); // make sure the first Point in the Array is always the top-right, etc: var p:Array = []; p[0] = (pPoints[0].x < pPoints[1].x) ? pPoints[0] : pPoints[1]; p[1] = (pPoints[0].x > pPoints[1].x) ? pPoints[0] : pPoints[1]; p[2] = (pPoints[2].x > pPoints[3].x) ? pPoints[2] : pPoints[3]; p[3] = (pPoints[2].x < pPoints[3].x) ? pPoints[2] : pPoints[3]; // draw page: var bmd:BitmapData if (this.lastFlippedSide == 0){ bmd = (this.pageCorner.x > this.width/2) ? back : front; }else{ bmd = (this.pageCorner.x < 0) ? back : front; } this.distortion.setTransform(this.render.graphics, bmd, p[0], p[1], p[2], p[3]); // draw gradients: this.addHardGradients(pPoints); } /** * Considers a hover-effect for the four outer page-corners. This method is typically called upon enter-frame. * * @param event Event * * @private */ protected function evaluateHover (event:Event=null):void { var side:uint = this.getCurrentSide(); var pageCornerHit:Boolean = this.isPageCornerHit(); // if the hovered ViewStack does not display any Pages: if (((side == Page.LEFT && !this.pageL.visible) || (side == Page.RIGHT && !this.pageR.visible)) && (!this.hoverActive || this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING)){ return; } // don't hover hard Pages: if (Page(this._pages.getItemAt(this._currentPage+side)).hard){ return; } // roll over: if ((pageCornerHit || (this.sideFlip && this.isPageSideHit())) && ((!this.hoverActive && this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.NOT_FLIPPING) || this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING)){ this.startPageFlip(null, true); } // roll out: if (!pageCornerHit && (!this.sideFlip || !this.isPageSideHit()) && this.hoverActive){ this.endPageFlip(); } } /** * Looks at a provided Point and considers tearing off the Page currently being flipped. This method is typically called from within the dragPageCorner method. * * @param point Point used to evaluate whether or not this Page should be torn out of this lib_flex_book_com_rubenswieringa_book_Book. * * @see lib_flex_book_com_rubenswieringa_book_Book#dragPageCorner() * * @private */ protected function evaluateTear (point:Point):void { // no evaluation necessary if the lib_flex_book_com_rubenswieringa_book_Book has not even started flipping yet: if (this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED){ return; } // stop if an auto-flip or page-tear is already active: if (this.tearActive || this.autoFlipActive){ return; } // evaluate: if (Math.round(point.x) == (1-this.lastFlippedCorner.x) * this.width/2 && Math.round(point.y) == this.lastFlippedCorner.y * this.height){ this.tearActive = true; this.autoFlipActive = true; this.autoFlipCancelable = false; } } /** * Performs a pageflip without the user having to drag the pagecorner. * * @param page int/uint or Page, indicating the index or instance of a Page. * @param cancelable Indicates whether or not this auto-flip should allow cancelGotoPage to be called. * * @see lib_flex_book_com_rubenswieringa_book_Book#cancelGotoPage() * @see lib_flex_book_com_rubenswieringa_book_Book#autoFlipDuration * * @throws lib_flex_book_com_rubenswieringa_book_BookError Gets thrown when the child parameter is not an instance of the Page class nor a int/uint. * @see lib_flex_book_com_rubenswieringa_book_BookError#CHILD_NOT_PAGE * * @throws ArgumentsError Gets thrown when the child parameter is not a child of this lib_flex_book_com_rubenswieringa_book_Book. * @see lib_flex_book_com_rubenswieringa_book_BookError#PAGE_NOT_CHILD * */ public function gotoPage (page:*, cancelable:Boolean=true):void { page = this.getPageIndex(page, true); if (page%2 != 1 && page != -1) page -= 1; // return if we're already at the specified Page: if (this._currentPage == page){ return; } // set target index and start pageflip: this.autoFlipIndex = page; this.autoFlipCancelable = cancelable; if (!this.autoFlipActive){ this.autoFlipActive = true; this.startPageFlip(); } } /** * Aborts a pageflip started by the gotoPage method. * * @param finishFlip Boolean indicating whether or not to allow the auto-flip to finish. When true, the pageflip will finish. When false, the auto-flip will immediately stop, and the page corner will start sticking to the mouse. * * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() * */ public function cancelGotoPage (finishFlip:Boolean=true):void { // stop if gotoPage is not active: if (!this.autoFlipActive){ return; } // stop if gotoPage is not cancelable: if (!this.autoFlipCancelable){ return; } // end the auto-flip: if (finishFlip){ this.autoFlipIndex = this._currentPage + 2 * this.lastFlippedDirection; }else{ this.autoFlipActive = false; } } /** * Performs a pageflip to the next page, without the user having to drag the pagecorner. * * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() * @see lib_flex_book_com_rubenswieringa_book_Book#autoFlipDuration * */ public function nextPage ():void { if (this._currentPage+2 <= this._pages.length-1){ this.gotoPage(this._currentPage+2, false); } } /** * Performs a pageflip to the previous page, without the user having to drag the pagecorner. * * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() * @see lib_flex_book_com_rubenswieringa_book_Book#autoFlipDuration * */ public function prevPage ():void { if (this._currentPage-2 >= -1){ this.gotoPage(this._currentPage-2, false); } } /** * Tears a Page out of this lib_flex_book_com_rubenswieringa_book_Book instance. When called, this method will be executed regardless of the value of the Page its tearable property. * Fails silently if the provided Page is not one of the currently visible Pages. * * @param page int/uint or Page, indicating the index or instance of a Page to be torn out of this lib_flex_book_com_rubenswieringa_book_Book. * @param fromTop If true, the Page will be torn out by its upper outer corner, if false by its lower outer corner. * * @see lib_flex_book_com_rubenswieringa_book_Book#tearPage() * @see Page#tearable * * @throws ArgumentError Gets thrown when page is an index-value and out of bounds. * @see lib_flex_book_com_rubenswieringa_book_BookError#OUT_OF_BOUNDS */ public function tearPage (page:*, fromTop:Boolean=true):void { page = this.getPage(page, true); // stop if we're not currently inactive or hovering: if (this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.NOT_FLIPPING && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED){ return; } // fail silently if the provided Page is not one of the currently visible Pages: if (page.index != this._currentPage && page.index != this._currentPage+1){ return; } // don't tear hard Pages: if (page.hard){ return; } // set vars to let the pageflip processing methods know that we're tearing: this.tearActive = true; this.tearSide = page.side; this.tearFromTop = fromTop; this.autoFlipActive = true; this.autoFlipCancelable = false; this.startPageFlip(); } // GRADIENTS: /** * Adds shadows and highlights to the pageflip process of a hard Page. * * @param area Array of coordinates (Point instance) indicating the outlines of the flipping page * * @see Gradients#drawOutside() * @see Gradients#drawInside() * * @private */ protected function addHardGradients (area:Array):void { // determine page: var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide)); // determine shadow or highlight: var tint:String; if (this.lastFlippedSide == Page.LEFT){ // if left-hand page was flipped tint = (this.pageCornerTarget.x > this.width/2) ? Gradients.DARK : Gradients.LIGHT; }else{ // if right-hand page was flipped tint = (this.pageCornerTarget.x < 0) ? Gradients.LIGHT : Gradients.DARK; } // draw gradients: page.gradients.drawOutside (this.render.graphics, area, tint); page.gradients.drawInside (this.render.graphics, area, tint); } /** * Adds shadows and highlights to the pageflip process of a non-hard Page. * * @see com.foxaweb.pageflip.PageFlip#computeFlip() * @see com.foxaweb.pageflip.PageFlip#drawBitmapSheet() * * @see Gradients#drawFlipside() * @see Gradients#drawOutside() * @see Gradients#drawInside() * * @param ocf Object returned by the computeFlip method of the PageFlip class * * @private */ protected function addSmoothGradients (ocf:Object):void { // determine page: var page:Page = Page(this._pages.getItemAt(this._currentPage+this.lastFlippedSide)); // determine rotation correction: var rotate:Number; if (this.lastFlippedCorner.equals(new Point(1,0)) || this.lastFlippedCorner.equals(new Point(0,1))){ // if the upper right or lower left corner is being flipped and the Page isn't torn out of its lib_flex_book_com_rubenswieringa_book_Book, correct the angle with 45 degrees: rotate = (!this.tearActive) ? Gradients.ROTATE_HALF : Gradients.ROTATE_FULL; } if (this.lastFlippedCorner.equals(new Point(0,0)) || this.lastFlippedCorner.equals(new Point(1,1))){ // if the upper left or lower right corner is being flipped and the Page isn't torn out of its lib_flex_book_com_rubenswieringa_book_Book, correct the angle with minus 45 degrees: rotate = (!this.tearActive) ? Gradients.ROTATE_FULL : Gradients.ROTATE_HALF; } // determine shadow or highlight: var tint1:String = (this.lastFlippedSide == 1) ? Gradients.LIGHT : Gradients.DARK; var tint2:String = (this.lastFlippedSide == 1) ? Gradients.DARK : Gradients.LIGHT; // draw gradients: if (ocf.cPoints != null) page.gradients.drawFlipside (this.render.graphics, ocf.cPoints, tint1, rotate); if (ocf.pPoints != null) page.gradients.drawOutside (this.render.graphics, ocf.pPoints, tint1, rotate); if (ocf.cPoints != null && !this.tearActive) page.gradients.drawInside (this.render.graphics, ocf.cPoints, tint2, rotate); } // PAGEFLIP ASSISTANCE: /** * Sets the position of the pageCorner property. * * @return Boolean indicating, if the gotoPage method is active, whether or not pageCornerTarget has reached the opposite corner. If gotoPage is not active, the return value will always be false. * * @see lib_flex_book_com_rubenswieringa_book_Book#movePageCorner() * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() * * @private */ protected function movePageCornerTarget ():Boolean { var x:Number; var y:Number; // if gotoPage is not active then set the page corner target equal to the mouse position: if (!this.autoFlipActive){ // calculate coordinates: x = this.mouseX; y = (!this.sideFlipActive) ? this.mouseY : this.lastFlippedCorner.y * this.height; // adjust x per position of render: if (this.lastFlippedSide == Page.RIGHT){ x -= (this.width/2); } // adjust x and y if a side-flip is active: if (this.sideFlipActive){ // adjust x so that pageflip won't start acting weird when mouse is out of bounds: var xMin:Number = 0 - this.lastFlippedSide * (this.width/2); var xMax:Number = xMin + this.width; if (x < xMin) x = xMin; if (x > xMax) x = xMax; // adjust y to make the pageflip a bit more pretty: if (!this.hoverActive){ y = this.getPageCornerYFromX(x, lib_flex_book_com_rubenswieringa_book_Book.SIDE_FLIP_CURVE); } } // set position: this.pageCornerTarget = new Point(x, y); } // if gotoPage is active then move the page corner target to the opposite corner: if (this.autoFlipActive){ // calculate coordinates: x = (!this.tearActive) ? this.pageCornerTarget.x : this.lastFlippedSide * (this.width/2); y = this.pageCornerTarget.y; var opposite:Point = new Point(); // if this is a normal auto-flip: if (!this.tearActive){ opposite.x = this.lastFlippedSide * (this.width/2) - this.width*this.lastFlippedDirection; if (Math.abs(x-opposite.x) >= this.autoFlipSpeed){ x += this.autoFlipSpeed * -this.lastFlippedDirection; }else{ x = opposite.x; } y = this.getPageCornerYFromX(x); } // if this is a tearing auto-flip: if (this.tearActive){ var directionY:Number = (this.lastFlippedCorner.y == 0) ? 1 : -1; opposite.y = (this.height/2) + (this.height*1.5 * directionY); if (Math.abs(y-opposite.y) >= this.autoFlipSpeed){ y += this.autoFlipSpeed * directionY; }else{ y = opposite.y; } } // set position: this.pageCornerTarget = new Point(x, y); // return true if pageCornerTarget has reached the opposite corner of where it started: if (this.pageCornerTarget.equals(this.pageCorner) && ((this.tearActive && y == opposite.y) || (!this.tearActive && x == opposite.x))){ return true; } } // return value: return false; } /** * Moves the property pageCorner towards the position of pageCornerTarget. * * @see lib_flex_book_com_rubenswieringa_book_Book#movePageCornerTarget() * * @private */ protected function movePageCorner ():void { // calculate differences: var corner:Point = this.pageCorner; var target:Point = this.pageCornerTarget; var diffX:Number = target.x - corner.x; var diffY:Number = target.y - corner.y; // apply easing if difference is substantial: if (Point.distance(this.pageCorner, this.pageCornerTarget) > lib_flex_book_com_rubenswieringa_book_Book.MAX_EASING_DIFFERENCE && (!this.snap || (this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_STARTED && this._status != lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_STARTED))){ this.pageCorner.x += diffX * this._easing; this.pageCorner.y += diffY * this._easing; }else{ this.pageCorner = this.pageCornerTarget.clone(); } // make sure pageCorner is within bounds so no weird animation will result: if ((this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.PAGEFLIP_ENDING || this._status == lib_flex_book_com_rubenswieringa_book_BookEvent.HOVER_ENDING) && !this.tearActive){ if (this.pageCorner.y < 0) this.pageCorner.y = 0; if (this.pageCorner.y > this.height) this.pageCorner.y = this.height; } } /** * Stores a Page its BitmapData in the bitmapData Array. * * @param front Facing side of a sheet * @param back Flipside of a sheet * * @private */ protected function saveBitmapData (front:Page, back:Page):void { back.hideFoldGradient(); this.bitmapData = []; // dispose of BitmapData of pages we won't need right now this.bitmapData[front.index] = front.getBitmapData(); this.bitmapData[back.index] = back.getBitmapData(); back.showFoldGradient(); } /** * Returns a Point instance indicating the current corner in which the mouse is. * * @return Point * * @private */ protected function getCurrentCorner ():Point { var corner:Point = new Point(); // determine corner: corner.x = (this.mouseX < this.width/2 ) ? 0 : 1; corner.y = (this.mouseY < this.height/2) ? 0 : 1; // return value: return corner; } /** * Returns a Point instance indicating the corner that was last flipped. * * @return Point */ public function getLastFlippedCorner ():Point { return this.lastFlippedCorner; } /** * Returns true if any of the four outer page-corners contain a specified Point. * * @param point Point checked for presence within any of the outer corners. If no value is provided then the mouse coordinates will be used. * * @return Boolean * * @see lib_flex_book_com_rubenswieringa_book_Book#isPageSideHit() * * @private */ protected function isPageCornerHit (point:Point=null):Boolean { // if no point was provided, use the mouse coordinates: if (point == null){ point = new Point(this.mouseX, this.mouseY); } // return value: if (Geom.isPointInCorner(this.regions[0].TL, point, Geom.TL) || Geom.isPointInCorner(this.regions[0].BL, point, Geom.BL) || Geom.isPointInCorner(this.regions[1].TR, point, Geom.TR) || Geom.isPointInCorner(this.regions[1].BR, point, Geom.BR) ){ return true; }else{ return false; } } /** * Returns true if any of the two outer page-sides contain a specified Point. * * @param point Point checked for presence within any of the outer corners. If no value is provided then the mouse coordinates will be used. * * @return Boolean * * @see lib_flex_book_com_rubenswieringa_book_Book#isPageCornerHit() * * @private */ protected function isPageSideHit (point:Point=null):Boolean { // if no point was provided, use the mouse coordinates: if (point == null){ point = new Point(this.mouseX, this.mouseY); } // return value: if (this.regions[0].side.containsPoint(point) || this.regions[1].side.containsPoint(point)){ return true; }else{ return false; } } /** * Returns an integer indicating the current side at which a pageflip should start. * * @see Page#LEFT * @see Page#RIGHT * * @return Current side at which a pageflip should start. * * @private */ protected function getCurrentSide ():uint { var side:uint; if (!this.autoFlipActive){ side = (this.mouseX <= this.width/2) ? Page.LEFT : Page.RIGHT; }else{ if (!this.tearActive){ side = (this.autoFlipIndex < this._currentPage) ? Page.LEFT : Page.RIGHT; }else{ side = this.tearSide; } } return side; } /** * Sets the time at which a pageflip started, if necessary. * * @private */ protected function setLastFlippedTime ():void { if (this.flipOnClick && !this.autoFlipActive){ this.lastFlippedTime = getTimer(); } } /** * Sets _status property and dispatches a lib_flex_book_com_rubenswieringa_book_BookEvent. * * @param newStatus New status. * @param dispatchEvent Boolean indicating whether or not a lib_flex_book_com_rubenswieringa_book_BookEvent should be dispatched. * @param eventType eventType for the dispatched Event. If not provided, the new status value will be used. * * @see lib_flex_book_com_rubenswieringa_book_BookEvent * * @private */ protected function setStatus (newStatus:String, dispatchEvent:Boolean=true, page:Page=null, eventType:String=""):void { if (this._status != newStatus){ this._status = newStatus; this.dispatchEvent(new lib_flex_book_com_rubenswieringa_book_BookEvent(lib_flex_book_com_rubenswieringa_book_BookEvent.STATUS_CHANGED, this)); } if (dispatchEvent){ if (eventType == "") eventType = newStatus; this.dispatchEvent(new lib_flex_book_com_rubenswieringa_book_BookEvent(eventType, this, page)); } } /** * Calculates a y value for pageCornerTarget at the hand of its x value. The nearer the x value is to the horizontal middle of the lib_flex_book_com_rubenswieringa_book_Book, the nearer the calculated y will be to the vertical middle of the lib_flex_book_com_rubenswieringa_book_Book. This method is typically used by the movePageCornerTarget method. * * @param x Current x position of pageCornerTarget. * @param curve The maximum offset from the top or bottom of the lib_flex_book_com_rubenswieringa_book_Book, measured in a decimal value (for example, 0.25 means 25% of the lib_flex_book_com_rubenswieringa_book_Book its height). Typically this value is either AUTO_FLIP_CURVE or SIDE_FLIP_CURVE. * * @return The calculated y value. * * @see lib_flex_book_com_rubenswieringa_book_Book#movePageCornerTarget() * @see lib_flex_book_com_rubenswieringa_book_Book#AUTO_FLIP_CURVE * @see lib_flex_book_com_rubenswieringa_book_Book#SIDE_FLIP_CURVE * * @private */ protected function getPageCornerYFromX (x:Number, curve:Number=lib_flex_book_com_rubenswieringa_book_Book.AUTO_FLIP_CURVE):Number { var middleX:Number = this.lastFlippedSide * (this.width/2) - (this.width/2)*this.lastFlippedDirection; var progress:Number = Math.abs(middleX-x) / (this.width/2); var y:Number; if (this.lastFlippedCorner.y == 0){ y = this.height * curve * (1-progress); }else{ y = this.height * (1-curve) + progress * (this.height*curve); } return y; } // ACCESSORS: /** * Typically called by the endPageFlip method to find out whether the page was either dragged or clicked. In the case of the latter the gotoPage method should be called. * @see lib_flex_book_com_rubenswieringa_book_Book#endPageFlip() * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() * @private */ protected function get flipOnRelease ():Boolean { if (this.flipOnClick && !this.autoFlipActive){ return (getTimer() - this.lastFlippedTime <= lib_flex_book_com_rubenswieringa_book_Book.CLICK_INTERVAL); }else{ return false; } } /** * The distance the pageCornerTarget travels along its x axis when gotoPage is active, calculated at the hand of the autoFlipDuration property. * @see lib_flex_book_com_rubenswieringa_book_Book#gotoPage() * @see lib_flex_book_com_rubenswieringa_book_Book#tearPage() * @see lib_flex_book_com_rubenswieringa_book_Book#autoFlipDuration * @private */ protected function get autoFlipSpeed ():uint { return this.width / ((this.autoFlipDuration/1000) * this.stage.frameRate); } /** * The precision with which the page-corner follows the mouse during a pageflip. Values range from 0 (no easing) to 1 (heavy easing). * @default 0.7 */ public function get easing ():Number { return (1 - this._easing) / 0.9; } public function set easing (value:Number):void { if (value < 0) value = 0; if (value > 1) value = 1; this._easing = 1 - (value * 0.9); } /** * If true, the first and last Pages in this lib_flex_book_com_rubenswieringa_book_Book will be hard, regardless of the value of their own hard properties. * @default true */ public function get hardCover ():Boolean { return this._hardCover; } public function set hardCover (value:Boolean):void { // return if there is no change in value: if (this._hardCover == value){ return; } // set value: this._hardCover = value; // erase or draw fold gradient for first and last Pages: if (StateManager.instance.getState(this) >= StateManager.CREATION_COMPLETE){ Page(this._pages.getItemAt(0)).refreshFoldGradient(); Page(this._pages.getItemAt(this._pages.length-1)).refreshFoldGradient(); } } /** * Set this property to false if the four outer page-corners should not display a subtle flip-effect on mouse-over. Note that Pages with their hard property set to true do not display hover-effects at all. * @default true */ public function get hover ():Boolean { return this._hoverEnabled; } public function set hover (value:Boolean):void { // return if there is no change in value: if (this._hoverEnabled == value){ return; } // set value: this._hoverEnabled = value; // add/remove listeners: if (this._hoverEnabled){ this.addEventListener(Event.ENTER_FRAME, this.evaluateHover); }else{ this.removeEventListener(Event.ENTER_FRAME, this.evaluateHover); } } /** * Size of the hit-regions in the pagecorners. * @default 150 */ public function get regionSize ():uint { return this._regionSize } public function set regionSize (value:uint):void { this._regionSize = value; this.createRegions(); } /** * Creates an Array with hit-regions for pagecorners. * * @private */ protected function createRegions ():void { // specify regions for left-hand page: this.regions[0] = {}; this.regions[0].TL = new Rectangle(0, 0, this._regionSize, this._regionSize); this.regions[0].TR = new Rectangle(0, 0, this._regionSize, this._regionSize); this.regions[0].BR = new Rectangle(0, 0, this._regionSize, this._regionSize); this.regions[0].BL = new Rectangle(0, 0, this._regionSize, this._regionSize); this.regions[0].TR.x += this.width / 2 - this.regionSize; this.regions[0].BR.x += this.width / 2 - this.regionSize; this.regions[0].BR.y += this.height - this.regionSize; this.regions[0].BL.y += this.height - this.regionSize; // specify regions for right-hand page: this.regions[1] = {}; this.regions[1].TL = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize); this.regions[1].TR = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize); this.regions[1].BR = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize); this.regions[1].BL = new Rectangle(this.width/2, 0, this._regionSize, this._regionSize); this.regions[1].TR.x += this.width / 2 - this.regionSize; this.regions[1].BR.x += this.width / 2 - this.regionSize; this.regions[1].BR.y += this.height - this.regionSize; this.regions[1].BL.y += this.height - this.regionSize; // specify side-flip regions for both pages: this.regions[0].side = new Rectangle (0, (this.height-this._regionSize)/2, this._regionSize/2, this._regionSize); this.regions[1].side = this.regions[0].side.clone() this.regions[0].side.x = this.width-this._regionSize/2; } /** * Current status of the lib_flex_book_com_rubenswieringa_book_Book. * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGEFLIP_STARTED * @see lib_flex_book_com_rubenswieringa_book_BookEvent#NOT_FLIPPING * @see lib_flex_book_com_rubenswieringa_book_BookEvent#PAGEFLIP_ENDING */ [Bindable(event='statusChanged')] public function get status ():String { return this._status; } } }