perf(img): do not reuse img elements

Safari UIWebView has troubles dropping image requests when the src
changes, and when there are hundreds going in and out this causes
issues. Instead, do not reuse img elements. Closes #6112
This commit is contained in:
Adam Bradley
2016-04-11 12:21:56 -05:00
parent dad2155ecd
commit b744275936
2 changed files with 62 additions and 45 deletions

View File

@ -7,6 +7,7 @@ export * from './components/button/button'
export * from './components/checkbox/checkbox'
export * from './components/content/content'
export * from './components/icon/icon'
export * from './components/img/img'
export * from './components/infinite-scroll/infinite-scroll'
export * from './components/infinite-scroll/infinite-scroll-content'
export * from './components/input/input'

View File

@ -1,5 +1,6 @@
import {Component, Input, HostBinding, ViewChild, ElementRef, ViewEncapsulation} from 'angular2/core';
import {Component, Input, ElementRef, ChangeDetectionStrategy, ViewEncapsulation, NgZone} from 'angular2/core';
import {raf} from '../../util/dom';
import {isPresent} from '../../util/util';
import {Platform} from '../../platform/platform';
@ -7,50 +8,85 @@ import {Platform} from '../../platform/platform';
@Component({
selector: 'ion-img',
template:
'<div *ngIf="_useA" class="img-placeholder" [style.height]="_h" [style.width]="_w"></div>' +
'<img #imgA *ngIf="_useA" (load)="_onLoad()" [src]="_srcA" [style.height]="_h" [style.width]="_w">' +
'<div *ngIf="!_useA" class="img-placeholder" [style.height]="_h" [style.width]="_w"></div>' +
'<img #imgB *ngIf="!_useA" (load)="_onLoad()" [src]="_srcB" [style.height]="_h" [style.width]="_w">',
'<div class="img-placeholder" [style.height]="_h" [style.width]="_w"></div>',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class Img {
private _src: string = '';
private _srcA: string = '';
private _srcB: string = '';
private _useA: boolean = true;
private _normalizeSrc: string = '';
private _imgs: HTMLImageElement[] = [];
private _w: string;
private _h: string;
private _enabled: boolean = true;
constructor(private _elementRef: ElementRef, private _platform: Platform) {}
@ViewChild('imgA') private _imgA: ElementRef;
@ViewChild('imgB') private _imgB: ElementRef;
constructor(private _elementRef: ElementRef, private _platform: Platform, private _zone: NgZone) {}
@Input()
set src(val: string) {
val = (isPresent(val) ? val : '');
let tmpImg = new Image();
tmpImg.src = isPresent(val) ? val : '';
if (this._src !== val) {
this._src = val;
this._loaded = false;
this._srcA = this._srcB = '';
this._useA = !this._useA;
this._update();
}
this._src = isPresent(val) ? val : '';
this._normalizeSrc = tmpImg.src;
this._update();
}
private _update() {
if (this._enabled && this.isVisible()) {
if (this._useA) {
this._srcA = this._src;
if (this._enabled && this._src !== '' && this.isVisible()) {
// actively update the image
} else {
this._srcB = this._src;
for (var i = this._imgs.length - 1; i >= 0; i--) {
if (this._imgs[i].src === this._normalizeSrc) {
// this is the active image
if (this._imgs[i].complete) {
this._loaded(true);
}
} else {
// no longer the active image
if (this._imgs[i].parentElement) {
this._imgs[i].parentElement.removeChild(this._imgs[i]);
}
this._imgs.splice(i, 1);
}
}
if (!this._imgs.length) {
this._zone.runOutsideAngular(() => {
let img = new Image();
img.style.width = this._w;
img.style.height = this._h;
img.addEventListener('load', () => {
if (img.src === this._normalizeSrc) {
this._elementRef.nativeElement.appendChild(img);
raf(() => {
this._update();
});
}
});
img.src = this._src;
this._imgs.push(img);
this._loaded(false);
});
}
} else {
// do not actively update the image
if (!this._imgs.some(img => img.src === this._normalizeSrc)) {
this._loaded(false);
}
}
}
private _loaded(isLoaded: boolean) {
this._elementRef.nativeElement.classList[isLoaded ? 'add': 'remove']('img-loaded');
}
enable(shouldEnable: boolean) {
this._enabled = shouldEnable;
this._update();
@ -61,26 +97,6 @@ export class Img {
return bounds.bottom > 0 && bounds.top < this._platform.height();
}
@HostBinding('class.img-loaded')
private _loaded: boolean = false;
private _onLoad() {
this._loaded = this.isLoaded();
}
isLoaded() {
let imgEle: HTMLImageElement;
if (this._useA && this._imgA) {
imgEle = this._imgA.nativeElement;
} else if (this._imgB) {
imgEle = this._imgB.nativeElement;
}
return (imgEle && imgEle.src !== '' && imgEle.complete);
}
@Input()
set width(val: string | number) {
this._w = (typeof val === 'number') ? val + 'px' : val;