Skip to content

Commit

Permalink
perf(img): do not reuse img elements
Browse files Browse the repository at this point in the history
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
  • Loading branch information
adamdbradley committed Apr 11, 2016
1 parent dad2155 commit b744275
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 46 deletions.
1 change: 1 addition & 0 deletions ionic/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
108 changes: 62 additions & 46 deletions ionic/components/img/img.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,92 @@
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';


@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 : '');

if (this._src !== val) {
this._src = val;
this._loaded = false;
this._srcA = this._srcB = '';
this._useA = !this._useA;
this._update();
}
let tmpImg = new Image();
tmpImg.src = isPresent(val) ? val : '';

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

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 {
this._srcB = this._src;
} 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();
Expand All @@ -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;
Expand Down

0 comments on commit b744275

Please sign in to comment.