import IsoLayer from "./iso-layer" ; import Orientation from "./utils/orientation" ; import Transform from "./utils/transform" ; import { getElement , wrapElement , toArray } from "./utils/helper" ; import { SQRT_2 , ISO_NONE , ISO_LEFT , ISO_RIGHT , ISO_HORIZONTAL , ISO_VERTICAL , ISO_DEFAULT_SPACING , ISO_DEFAULT_ORIENTATION , } from "./constants" ; import { IsoOptions , IsoProgres , Oriented } from "./types" ; class Isometrizer implements Oriented { static get ISO_LEFT ( ) { return ISO_LEFT ; } static get ISO_RIGHT ( ) { return ISO_RIGHT ; } static get ISO_HORIZONTAL ( ) { return ISO_HORIZONTAL ; } static get ISO_VERTICAL ( ) { return ISO_VERTICAL ; } private _element : HTMLElement ; private _wrapper : HTMLElement ; private _prevProgress : IsoProgres ; private _defaultSpacing : number ; private _orientation : Orientation ; private _layers : IsoLayer [ ] ; private _allLayers : IsoLayer [ ] ; public get orientation ( ) { return this . _orientation ; } constructor ( el , options = { spacing : ISO_DEFAULT_SPACING , orientation : ISO_DEFAULT_ORIENTATION , } ) { this . _element = getElement ( el ) ; this . _prevProgress = { normal : 0 , side : 0 , scale : 0 , float : 0 , childNormal : 0 , childSide : 0 , } ; this . _defaultSpacing = options . spacing || ISO_DEFAULT_SPACING ; this . _orientation = new Orientation ( options . orientation || ISO_DEFAULT_ORIENTATION ) ; this . _wrapper = wrapElement ( this . _element ) ; this . _element . style . transformStyle = "preserve-3d" ; ( this . _wrapper . style as any ) . willChange = "transform" ; this . _layers = this . _findLayers ( ) ; this . _allLayers = this . _getAllLayers ( ) ; this . off ( ) ; } public on ( ) { this . progress ( { normal : 1 , side : 1 , scale : 1 , float : 1 , childNormal : 1 , childSide : 1 , } ) ; return this ; } public off ( ) { this . progress ( { normal : 0 , side : 0 , scale : 0 , float : 0 , childNormal : 0 , childSide : 0 , } ) ; return this ; } public progress ( props = this . _prevProgress ) { props . normal = props . normal || this . _prevProgress . normal ; props . side = props . side || this . _prevProgress . side ; props . scale = props . scale || this . _prevProgress . scale ; props . float = props . float || this . _prevProgress . float ; props . childNormal = props . childNormal || this . _prevProgress . childNormal ; props . childSide = props . childSide || this . _prevProgress . childSide ; const THRESHOLD = 0.001 ; if ( props . normal < THRESHOLD ) props . normal = 0 ; if ( props . side < THRESHOLD ) props . side = 0 ; if ( props . scale < THRESHOLD ) props . scale = 0 ; if ( props . float < THRESHOLD ) props . float = 0 ; if ( props . childNormal < THRESHOLD ) props . childNormal = 0 ; if ( props . childSide < THRESHOLD ) props . childSide = 0 ; const parentBbox = this . _wrapper . getBoundingClientRect ( ) as DOMRect ; const originalBbox = this . _element . getBoundingClientRect ( ) as DOMRect ; const maxBbox = { top : originalBbox . top , bottom : originalBbox . bottom , left : originalBbox . left , right : originalBbox . right , } as DOMRect ; const calculatedScale = this . _calcScale ( props . scale ) ; for ( const layer of this . _layers ) { layer . progress ( props . float , props . childNormal , props . childSide , calculatedScale , maxBbox ) ; } this . _rotate ( { normal : props . normal , side : props . side , scale : props . scale , } ) ; for ( const layer of this . _allLayers ) { layer . update ( ) ; } this . _resize ( parentBbox , maxBbox ) ; this . _prevProgress . normal = props . normal ; this . _prevProgress . side = props . side ; this . _prevProgress . scale = props . scale ; this . _prevProgress . float = props . float ; this . _prevProgress . childNormal = props . childNormal ; this . _prevProgress . childSide = props . childSide ; return this ; } private _resize ( originalBbox , maxBbox ) { maxBbox . width = maxBbox . right - maxBbox . left ; maxBbox . height = maxBbox . bottom - maxBbox . top ; const parentElement = this . _wrapper ; const marginTop = originalBbox . top - maxBbox . top ; const marginBottom = maxBbox . bottom - originalBbox . bottom ; const leftDiff = originalBbox . left - maxBbox . left ; const rightDiff = maxBbox . right - originalBbox . right ; const widthDiff = Math . max ( leftDiff , 0 ) - Math . max ( rightDiff , 0 ) ; const marginRight = Math . max ( leftDiff + ( rightDiff < 0 ? rightDiff : 0 ) , 0 ) ; const marginLeft = Math . max ( rightDiff + ( leftDiff < 0 ? leftDiff : 0 ) , 0 ) ; parentElement . style . marginRight = `${ marginRight }px` ; parentElement . style . marginLeft = `${ marginLeft }px` ; parentElement . style . marginTop = `${ marginTop }px` ; parentElement . style . marginBottom = `${ marginBottom }px` ; parentElement . style . transform = `translateX(${ widthDiff }px)` ; } private _findLayers ( ) { const layers = [ ] ; for ( const child of toArray ( this . _element . children ) ) { const layer = new IsoLayer ( child as HTMLElement , this , this . _defaultSpacing ) ; layers . push ( layer ) ; } return layers ; } private _getAllLayers ( ) { const layers = [ ] ; for ( const layer of this . _layers ) { layers . push ( layer ) ; layers . push ( ... layer . getAllChildLayers ( ) ) ; } return layers ; } private _rotate ( props ) { const normal = props . normal ; const side = props . side ; const scale = props . scale ; const transform = new Transform ( this . _orientation , new Orientation ( ISO_NONE ) ) ; transform . setScale ( this . _calcScale ( scale ) ) ; transform . rotateTo ( { normal , side } ) ; this . _element . style . transform = transform . toString ( ) ; } private _calcScale ( val ) { const maxScale = SQRT_2 ; const scaleAmount = 1 + val * ( maxScale - 1 ) ; return scaleAmount ; } } export default Isometrizer ;