Created
May 5, 2016 22:47
-
-
Save jack-guy/abb8a30529bc2fd80bb40efeed48f2a7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Directive, ViewChild, Inject, ContentChildren, QueryList, ContentChild, | |
ElementRef, Input, forwardRef, AfterViewInit, EventEmitter } from 'angular2/core'; | |
import { Subject } from 'rxjs'; | |
const imagesLoaded = require('imagesloaded'); | |
@Directive({ | |
selector: '[masonry-item]', | |
host: { | |
'[style.position]': 'position', | |
'[style.left]': 'left+"px"', | |
'[style.top]': 'top+"px"', | |
'[style.visibility]': '(loading) ? "hidden" : "visible"' | |
} | |
}) | |
export class MasonryItemDirective { | |
public el: HTMLElement; | |
public position: string; | |
public left: number; | |
public top: number; | |
public loading: boolean = true; | |
constructor (public ref: ElementRef) { | |
this.el = this.ref.nativeElement; | |
} | |
} | |
@Directive({ | |
selector: '[masonry-spacer]', | |
host: { | |
'[style.height]': 'height+"px"', | |
} | |
}) | |
export class MasonrySpacerDirective { | |
public el: HTMLElement; | |
public height: number; | |
constructor (public ref: ElementRef) { | |
this.el = this.ref.nativeElement; | |
this.el.style.width = '100%'; | |
this.el.style.display = 'block'; | |
} | |
} | |
@Directive({ | |
selector: '[masonry]', | |
host: { | |
'(resize)': '_resizeStream.next($event)' | |
} | |
}) | |
export class MasonryDirective implements AfterViewInit { | |
@ContentChildren(MasonryItemDirective) items: QueryList<MasonryItemDirective>; | |
@ContentChild(MasonrySpacerDirective) spacer: MasonrySpacerDirective; | |
@Input('masonry-columns') maxColumns: number; | |
private _el: HTMLElement; | |
private _resizeStream: Subject<any> = new Subject(); | |
constructor (ref: ElementRef) { | |
this._el = ref.nativeElement; | |
} | |
ngAfterViewInit () { | |
this.items.changes.debounceTime(100).subscribe((changes) => { | |
imagesLoaded(this._el, () => { | |
this.layout(); | |
}); | |
}); | |
this._resizeStream.debounceTime(100).subscribe(() => { | |
this.layout(); | |
}); | |
} | |
layout () { | |
if (this.items.length === 0) | |
return; | |
const firstEl = this.items.first.el; | |
const gutter = this.styleVal(firstEl, 'margin-right'); | |
const gap = this.styleVal(firstEl, 'margin-bottom'); | |
const parentWidth = this._el.clientWidth; | |
const parentInnerWidth = parentWidth - | |
this.styleVal(this._el, 'padding-left') - | |
this.styleVal(this._el, 'padding-right'); | |
const columnWidth = this.items.first.el.clientWidth; | |
const numColumns = Math.min( | |
Math.floor( | |
parentInnerWidth / columnWidth | |
), | |
this.maxColumns | |
); | |
const parentPadding = (parentWidth - (columnWidth + gutter) * numColumns) / 2; | |
const cols = []; | |
const initialTop = this.styleVal(this._el, 'padding-top'); | |
for (let i = 0; i < numColumns; i++) { cols.push(initialTop); } | |
let count = 0; | |
this.items.forEach((item) => { | |
const itemCol = count % numColumns; | |
Object.assign(item, { | |
position: 'absolute', | |
left: parentPadding + itemCol * (columnWidth + gutter), | |
top: cols[itemCol], | |
}); | |
cols[itemCol] += item.el.clientHeight + gap; | |
item.loading = false; | |
count++; | |
}); | |
if (this.spacer) { | |
this.spacer.height = Math.max(...cols); | |
} | |
} | |
styleVal (el, prop) { | |
return parseInt( | |
getComputedStyle(el, null) | |
.getPropertyValue(prop) | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment