import { Orientation , Render } from '../Render/Render' ; import { PageFlip } from '../PageFlip' ; import { Helper } from '../Helper' ; import { PageRect , Point } from '../BasicTypes' ; import { FlipCalculation } from './FlipCalculation' ; import { Page , PageDensity } from '../Page/Page' ; /**
 * Flipping direction
 */ export const enum FlipDirection { FORWARD , BACK } /**
 * Active corner when flipping
 */ export const enum FlipCorner { TOP = 'top' , BOTTOM = 'bottom' } /**
 * State of the book
 */ export const enum FlippingState { /** The user folding the page */ USER_FOLD = 'user_fold' , /** Mouse over active corners */ FOLD_CORNER = 'fold_corner' , /** During flipping animation */ FLIPPING = 'flipping' , /** Base state */ READ = 'read' } /**
 * Class representing the flipping process
 */ export class Flip { private readonly render : Render ; private readonly app : PageFlip ; private flippingPage : Page = null ; private bottomPage : Page = null ; private calc : FlipCalculation = null ; private state : FlippingState = FlippingState . READ ; constructor ( render , app ) { this . render = render ; this . app = app ; } param globalPos public fold ( globalPos ) { this . setState ( FlippingState . USER_FOLD ) ; if ( this . calc === null ) this . start ( globalPos ) ; this . do ( this . render . convertToPage ( globalPos ) ) ; } param globalPos public flip ( globalPos ) { if ( this . app . getSettings ( ) . disableFlipByClick && ! this . isPointOnCorners ( globalPos ) ) return ; if ( this . calc !== null ) this . render . finishAnimation ( ) ; if ( ! this . start ( globalPos ) ) return ; const rect = this . getBoundsRect ( ) ; this . setState ( FlippingState . FLIPPING ) ; const topMargins = rect . height / 10 ; const yStart = this . calc . getCorner ( ) === FlipCorner . BOTTOM ? rect . height - topMargins : topMargins ; const yDest = this . calc . getCorner ( ) === FlipCorner . BOTTOM ? rect . height : 0 ; this . calc . calc ( { x : rect . pageWidth - topMargins , y : yStart } ) ; this . animateFlippingTo ( { x : rect . pageWidth - topMargins , y : yStart } , { x : - rect . pageWidth , y : yDest } , true ) ; } param { Point } globalPos returns { boolean } public start ( globalPos ) { this . reset ( ) ; const bookPos = this . render . convertToBook ( globalPos ) ; const rect = this . getBoundsRect ( ) ; const direction = this . getDirectionByPoint ( bookPos ) ; const flipCorner = bookPos . y >= rect . height / 2 ? FlipCorner . BOTTOM : FlipCorner . TOP ; if ( ! this . checkDirection ( direction ) ) return false ; try { this . flippingPage = this . app . getPageCollection ( ) . getFlippingPage ( direction ) ; this . bottomPage = this . app . getPageCollection ( ) . getBottomPage ( direction ) ; if ( this . render . getOrientation ( ) === Orientation . LANDSCAPE ) { if ( direction === FlipDirection . BACK ) { const nextPage = this . app . getPageCollection ( ) . nextBy ( this . flippingPage ) ; if ( nextPage !== null ) { if ( this . flippingPage . getDensity ( ) !== nextPage . getDensity ( ) ) { this . flippingPage . setDrawingDensity ( PageDensity . HARD ) ; nextPage . setDrawingDensity ( PageDensity . HARD ) ; } } } else { const prevPage = this . app . getPageCollection ( ) . prevBy ( this . flippingPage ) ; if ( prevPage !== null ) { if ( this . flippingPage . getDensity ( ) !== prevPage . getDensity ( ) ) { this . flippingPage . setDrawingDensity ( PageDensity . HARD ) ; prevPage . setDrawingDensity ( PageDensity . HARD ) ; } } } } this . render . setDirection ( direction ) ; this . calc = new FlipCalculation ( direction , flipCorner , rect . pageWidth . toString ( 10 ) , rect . height . toString ( 10 ) ) ; return true ; } catch ( e ) { return false ; } } param { Point } pagePos private do ( pagePos ) { if ( this . calc === null ) return ; if ( this . calc . calc ( pagePos ) ) { const progress = this . calc . getFlippingProgress ( ) ; this . bottomPage . setArea ( this . calc . getBottomClipArea ( ) ) ; this . bottomPage . setPosition ( this . calc . getBottomPagePosition ( ) ) ; this . bottomPage . setAngle ( 0 ) ; this . bottomPage . setHardAngle ( 0 ) ; this . flippingPage . setArea ( this . calc . getFlippingClipArea ( ) ) ; this . flippingPage . setPosition ( this . calc . getActiveCorner ( ) ) ; this . flippingPage . setAngle ( this . calc . getAngle ( ) ) ; if ( this . calc . getDirection ( ) === FlipDirection . FORWARD ) { this . flippingPage . setHardAngle ( ( 90 * ( 200 - progress * 2 ) ) / 100 ) ; } else { this . flippingPage . setHardAngle ( ( - 90 * ( 200 - progress * 2 ) ) / 100 ) ; } this . render . setPageRect ( this . calc . getRect ( ) ) ; this . render . setBottomPage ( this . bottomPage ) ; this . render . setFlippingPage ( this . flippingPage ) ; this . render . setShadowData ( this . calc . getShadowStartPoint ( ) , this . calc . getShadowAngle ( ) , progress , this . calc . getDirection ( ) ) ; } } param { number } page param { FlipCorner } corner public flipToPage ( page , corner ) { const current = this . app . getPageCollection ( ) . getCurrentSpreadIndex ( ) ; const next = this . app . getPageCollection ( ) . getSpreadIndexByPage ( page ) ; try { if ( next > current ) { this . app . getPageCollection ( ) . setCurrentSpreadIndex ( next - 1 ) ; this . flipNext ( corner ) ; } if ( next < current ) { this . app . getPageCollection ( ) . setCurrentSpreadIndex ( next + 1 ) ; this . flipPrev ( corner ) ; } } catch ( e ) { } } param { FlipCorner } corner public flipNext ( corner ) { this . flip ( { x : this . render . getRect ( ) . left + this . render . getRect ( ) . pageWidth * 2 - 10 , y : corner === FlipCorner . TOP ? 1 : this . render . getRect ( ) . height - 2 , } ) ; } param { FlipCorner } corner public flipPrev ( corner ) { this . flip ( { x : 10 , y : corner === FlipCorner . TOP ? 1 : this . render . getRect ( ) . height - 2 , } ) ; } /**
     * Called when the user has stopped flipping
     */ public stopMove ( ) { if ( this . calc === null ) return ; const pos = this . calc . getPosition ( ) ; const rect = this . getBoundsRect ( ) ; const y = this . calc . getCorner ( ) === FlipCorner . BOTTOM ? rect . height : 0 ; if ( pos . x <= 0 ) this . animateFlippingTo ( pos , { x : - rect . pageWidth , y } , true ) ; else this . animateFlippingTo ( pos , { x : rect . pageWidth , y } , false ) ; } param globalPos public showCorner ( globalPos ) { if ( ! this . checkState ( FlippingState . READ , FlippingState . FOLD_CORNER ) ) return ; const rect = this . getBoundsRect ( ) ; const pageWidth = rect . pageWidth ; if ( this . isPointOnCorners ( globalPos ) ) { if ( this . calc === null ) { if ( ! this . start ( globalPos ) ) return ; this . setState ( FlippingState . FOLD_CORNER ) ; this . calc . calc ( { x : pageWidth - 1 , y : 1 } ) ; const fixedCornerSize = 50 ; const yStart = this . calc . getCorner ( ) === FlipCorner . BOTTOM ? rect . height - 1 : 1 ; const yDest = this . calc . getCorner ( ) === FlipCorner . BOTTOM ? rect . height - fixedCornerSize : fixedCornerSize ; this . animateFlippingTo ( { x : pageWidth - 1 , y : yStart } , { x : pageWidth - fixedCornerSize , y : yDest } , false , false ) ; } else { this . do ( this . render . convertToPage ( globalPos ) ) ; } } else { this . setState ( FlippingState . READ ) ; this . render . finishAnimation ( ) ; this . stopMove ( ) ; } } param { Point } start param { Point } dest param { boolean } isTurned param { boolean } needReset private animateFlippingTo ( start , dest , isTurned , needReset = true ) { const points = Helper . GetCordsFromTwoPoint ( start , dest ) ; const frames = [ ] ; for ( const p of points ) frames . push ( ( ) => this . do ( p ) ) ; const duration = this . getAnimationDuration ( points . length ) ; this . render . startAnimation ( frames , duration , ( ) => { if ( ! this . calc ) return ; if ( isTurned ) { if ( this . calc . getDirection ( ) === FlipDirection . BACK ) this . app . turnToPrevPage ( ) ; else this . app . turnToNextPage ( ) ; } if ( needReset ) { this . render . setBottomPage ( null ) ; this . render . setFlippingPage ( null ) ; this . render . clearShadow ( ) ; this . setState ( FlippingState . READ ) ; this . reset ( ) ; } } ) ; } /**
     * Get the current calculations object
     */ public getCalculation ( ) { return this . calc ; } /**
     * Get current flipping state
     */ public getState ( ) { return this . state ; } private setState ( newState ) { if ( this . state !== newState ) { this . app . updateState ( newState ) ; this . state = newState ; } } private getDirectionByPoint ( touchPos ) { const rect = this . getBoundsRect ( ) ; if ( this . render . getOrientation ( ) === Orientation . PORTRAIT ) { if ( touchPos . x - rect . pageWidth <= rect . width / 5 ) { return FlipDirection . BACK ; } } else if ( touchPos . x < rect . width / 2 ) { return FlipDirection . BACK ; } return FlipDirection . FORWARD ; } private getAnimationDuration ( size ) { const defaultTime = this . app . getSettings ( ) . flippingTime ; if ( size >= 1000 ) return defaultTime ; return ( size / 1000 ) * defaultTime ; } private checkDirection ( direction ) { if ( direction === FlipDirection . FORWARD ) return this . app . getCurrentPageIndex ( ) < this . app . getPageCount ( ) - 1 ; return this . app . getCurrentPageIndex ( ) >= 1 ; } private reset ( ) { this . calc = null ; this . flippingPage = null ; this . bottomPage = null ; } private getBoundsRect ( ) { return this . render . getRect ( ) ; } private checkState ( ... states ) { for ( const state of states ) { if ( this . state === state ) return true ; } return false ; } private isPointOnCorners ( globalPos ) { const rect = this . getBoundsRect ( ) ; const pageWidth = rect . pageWidth ; const operatingDistance = Math . sqrt ( Math . pow ( pageWidth , 2 ) + Math . pow ( rect . height , 2 ) ) / 5 ; const bookPos = this . render . convertToBook ( globalPos ) ; return ( bookPos . x > 0 && bookPos . y > 0 && bookPos . x < rect . width && bookPos . y < rect . height && ( bookPos . x < operatingDistance || bookPos . x > rect . width - operatingDistance ) && ( bookPos . y < operatingDistance || bookPos . y > rect . height - operatingDistance ) ) ; } }