import Transform from "./utils/transform" ; import Orientation from "./utils/orientation" ; import Panel from "./panel" ; import { ISO_DEFAULT_SIDE_COLOR , ISO_DEFAULT_TOP_COLOR , ISO_HORIZONTAL , ISO_RIGHT , ISO_LEFT , ISO_VERTICAL , } from "./constants" ; import { wrapElement , toArray } from "./utils/helper" ; import { Oriented } from "./types" ; class IsoLayer implements Oriented { private _wrapper : HTMLElement ; private _element : HTMLElement ; private _parent : Oriented ; private _childs : IsoLayer [ ] ; private _panels : Panel [ ] ; private _orientation : Orientation ; private _transform : Transform ; private _isWrapper : boolean ; private _isLeaf : boolean ; private _spacing : number ; public get orientation ( ) { return this . _orientation ; } constructor ( el , parent , defaultSpacing ) { this . _element = el ; this . _parent = parent ; this . _childs = [ ] ; this . _orientation = parent . orientation ; const wrapSpacing = el . getAttribute ( "iso-wrap-spacing" ) ; this . _isWrapper = ( wrapSpacing !== null ) ; this . _isLeaf = ( el . getAttribute ( "iso-no-spacing" ) !== null ) || this . _isWrapper ; if ( this . _isWrapper ) { this . _spacing = Number ( wrapSpacing ) || defaultSpacing ; } else if ( ! this . _isLeaf ) { const spacing = el . getAttribute ( "iso-spacing" ) ; this . _spacing = spacing !== null ? Number ( spacing ) : defaultSpacing ; } else { this . _spacing = 0 ; } const rotations = el . getAttribute ( "iso-rotation" ) ; if ( rotations !== null ) { this . _orientation = Orientation . fromString ( rotations ) ; if ( ! ( this . _orientation . isNormal ) ) { this . _orientation . merge ( parent . orientation . normal ) ; } if ( ! ( this . _orientation . isSide ) ) { this . _orientation . merge ( parent . orientation . side ) ; } } this . _transform = new Transform ( this . _orientation , this . _parent . orientation ) ; this . _wrapper = wrapElement ( this . _element , this . _isLeaf ) ; if ( ! this . _isLeaf ) { this . _wrapper . style . transformStyle = "preserve-3d" ; } this . _setTransformOrigin ( ) ; if ( this . _shouldTraverse ( ) ) { this . _findChilds ( defaultSpacing ) ; } if ( this . _childs . length <= 0 ) this . _isLeaf = true ; this . _panels = this . _makePanel ( ) ; } get wrapper ( ) { return this . _wrapper ; } public progress ( float , normal , side , scale , maxBbox ) { this . _transform . reset ( ) ; this . _transform . setFloat ( float * this . _spacing ) ; if ( ! this . _orientation . match ( this . _parent . orientation ) ) { this . _transform . rotateTo ( { normal , side , } ) ; } const additionalOffset = { top : 0 , bottom : 0 , left : 0 , right : 0 , } ; for ( const panel of this . _panels ) { panel . progress ( float , scale , this . _orientation ) ; const panelOffset = panel . offset ; additionalOffset . top = additionalOffset . top || panelOffset . top ; additionalOffset . bottom = additionalOffset . bottom || panelOffset . bottom ; additionalOffset . left = additionalOffset . left || panelOffset . left ; additionalOffset . right = additionalOffset . right || panelOffset . right ; } const bbox = this . _element . getBoundingClientRect ( ) ; if ( bbox . top - additionalOffset . top < maxBbox . top ) { maxBbox . top = bbox . top - additionalOffset . top ; } if ( bbox . bottom + additionalOffset . bottom > maxBbox . bottom ) { maxBbox . bottom = bbox . bottom + additionalOffset . bottom ; } if ( bbox . left - additionalOffset . left < maxBbox . left ) { maxBbox . left = bbox . left - additionalOffset . left ; } if ( bbox . right + additionalOffset . right > maxBbox . right ) { maxBbox . right = bbox . right - additionalOffset . right ; } for ( const child of this . _childs ) { child . progress ( float , normal , side , scale , maxBbox ) ; } } public update ( ) { const isFirefox = navigator . userAgent . indexOf ( "Firefox" ) > 0 ; if ( ! isFirefox ) { if ( this . _isLeaf ) { if ( this . _transform . changed ) { this . _wrapper . style . transformStyle = "preserve-3d" ; } else { this . _wrapper . style . transformStyle = "flat" ; } } } else { this . _wrapper . style . transformStyle = "preserve-3d" ; } this . _wrapper . style . transform = this . _transform . toString ( ) ; for ( const panel of this . _panels ) { panel . update ( ) ; } } public addParallelLayer ( layer ) { this . _wrapper . appendChild ( layer . wrapper ) ; this . _childs . push ( layer ) ; } public getAllChildLayers ( ) { const layers = [ ] ; for ( const child of this . _childs ) { layers . push ( child ) ; layers . push ( ... child . getAllChildLayers ( ) ) ; } return layers ; } private _makePanel ( ) { const element = this . _element ; const spacing = this . _spacing ; const orientation = this . _orientation ; let topBackground = element . getAttribute ( "iso-top-background" ) ; let sideBackground = element . getAttribute ( "iso-side-background" ) ; const sideLengthStr = element . getAttribute ( "iso-side-length" ) ; const hasPanel = topBackground !== null || sideBackground !== null || sideLengthStr !== null ; const sideLength = sideLengthStr ? Number ( sideLengthStr ) : Number ( spacing ) ; const panels = [ ] ; if ( hasPanel ) { topBackground = topBackground || ISO_DEFAULT_TOP_COLOR ; sideBackground = sideBackground || ISO_DEFAULT_SIDE_COLOR ; const topPanelOrientation = orientation . isHorizontal ? new Orientation ( ISO_HORIZONTAL | ISO_RIGHT ) : new Orientation ( ISO_HORIZONTAL | ISO_LEFT ) ; const sidePanelOrientation = orientation . isLeft ? new Orientation ( ISO_VERTICAL | ISO_RIGHT ) : new Orientation ( ISO_VERTICAL | ISO_LEFT ) ; const topPanel = new Panel ( element , sideLength , topPanelOrientation ) ; const sidePanel = new Panel ( element , sideLength , sidePanelOrientation ) ; topPanel . background = topBackground ; sidePanel . background = sideBackground ; panels . push ( topPanel ) ; panels . push ( sidePanel ) ; } return panels ; } private _findChilds ( defaultSpacing ) { const isParallel = ( this . _element . getAttribute ( "iso-parallel" ) !== null ) ; if ( ! this . _element . children [ 0 ] ) return ; if ( isParallel ) { let parentLayer = new IsoLayer ( this . _element . children [ 0 ] as HTMLElement , this , defaultSpacing ) ; this . _childs . push ( parentLayer ) ; while ( this . _element . children [ 1 ] ) { const layer = new IsoLayer ( this . _element . children [ 1 ] as HTMLElement , parentLayer , defaultSpacing ) ; parentLayer . addParallelLayer ( layer ) ; parentLayer = layer ; } } else { for ( const child of toArray ( this . _element . children ) ) { const layer = new IsoLayer ( child as HTMLElement , this , defaultSpacing ) ; this . _childs . push ( layer ) ; } } } private _shouldTraverse ( ) { const tag = this . _element . tagName . toLowerCase ( ) ; if ( this . _isWrapper ) { return false ; } if ( this . _isLeaf ) { return false ; } if ( tag === "a" ) { return false ; } if ( tag === "iframe" ) { return false ; } return true ; } private _setTransformOrigin ( ) { const orientation = this . _orientation ; const parentOrientation = this . _parent . orientation ; let xOrigin = 0 ; let yOrigin = 0 ; if ( parentOrientation . isVertical && parentOrientation . isLeft ) { xOrigin = 100 ; } if ( parentOrientation . isHorizontal ) { if ( orientation . isHorizontal ) { xOrigin = 50 ; yOrigin = 50 ; } else { xOrigin = parentOrientation . isRight ? 100 : 0 ; yOrigin = 100 ; } } this . _wrapper . style . transformOrigin = `${ xOrigin }% ${ yOrigin }%` ; } } export default IsoLayer ;