From 6093ed45b96ae0a1d30feb3ffa6d0d0db553b1bb Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 5 Dec 2016 23:26:36 -0600 Subject: [PATCH] docs(img): ion-img docs --- src/components/img/img.ts | 142 ++++++++++++++++++++++++++++++-------- 1 file changed, 115 insertions(+), 27 deletions(-) diff --git a/src/components/img/img.ts b/src/components/img/img.ts index b928f2da937..244e5eace78 100644 --- a/src/components/img/img.ts +++ b/src/components/img/img.ts @@ -8,7 +8,89 @@ import { Platform } from '../../platform/platform'; /** - * @private + * @name Img + * @description + * Two of the biggest cuprits of scrolling jank is starting up a new + * HTTP request, and rendering images. These two reasons is largely why + * `ion-img` was created and the problems which it is helping to solve. + * The standard `` element is often a large source of these problems, + * and what makes matters worse is that the app does not have fine-grained + * control of each img element. + * + * The `ion-img` component is similar to the standard `` element, + * but it also adds features in order to provide improved performance. + * Features include only loading images which are visible, using web workers + * for HTTP requests, preventing jank while scrolling and in-memory caching. + * + * Note that `ion-img` also comes with a few more restrictions in comparison to + * the standard `` element. A good rule is, if there are only a few images + * to be rendered on one page, then the standard `` may be best. However, if + * a page has the potential for hundreds or even thousands of images within a + * scrollable area, then `ion-img` would be better suited for the job. + * + * + * ### Lazy Loading + * + * Lazy loading images refers to only loading images which are actually + * visible within the user's viewport. This also means that images which are + * not viewable on the initial load would not be downloaded. Next, as the user + * scrolls down, each image which becomes visible is then loaded on-demand. + * + * The benefits of this approach is that unnecessary HTTP requests are not + * started and valuable bandwidth wasted, and to free up browser resources + * which would be wasted on images which are not even viewable. For example, + * animated GIFs are enourmous performance drains, however, with `ion-img` + * the app is able to dedicate resources to just the viewable images. + * + * + * ### Image Dimensions + * + * By providing image dimensions up front, Ionic is able to accurately size + * up the image's location within the viewport, which helps lazy load only + * images which are viewable. Image dimensions can either by set as properties, + * inline styles, or stylesheets. It doesn't matter which method of setting + * dimensions is used, but it's important that somehow each `ion-img` + * has been given an exact size. + * + * For example, by default `` and `` already come + * with exact sizes when placed within ``. By giving each image an + * exact size, this then further locks in the size of each `ion-item`, which + * again helps improve scroll performance. + * + * @usage + * ```html + * + * + * + * + * + * + * + * + * ``` + * + * + * ### Web Worker and XHR Requests + * + * Another big cause of scroll jank is kicking off a new HTTP request, which + * is exactly what images do. Normally, this isn't a problem for something like + * a blog since all image HTTP requests are started immediately as HTML + * parses. However, Ionic has the ability to include hundreds to thousands of + * images within one page, but we're not actually loading all of the images at once. + * + * Imagine an app where users can slowly, or quickly, scroll through hundreds of + * images. If they're scrolling extremely fast, the app wouldn't want to start all of + * those requests, but if they're scrolling slowly they would. Additionally, it's + * most browsers can only have six requests at one time for the same domain, so + * it's extemely important that we're managing which images we should downloading. + * + * By place XMLHttpRequest within a web worker, we're able to pass off the heavy + * lifting to another thread. Not only are able to take the load of the main thread, + * but we're also able to accurately control exactly which images should be + * downloading, along with the ability to abort unnecessary requests. Aborting + * requets is just as important so that Ionic can free up connections for the most + * important images which are visible. + * */ @Component({ selector: 'ion-img', @@ -28,10 +110,6 @@ export class Img implements OnDestroy { /** @internal */ _cache: boolean = true; /** @internal */ - _lazy: boolean = true; - /** @internal */ - _ww: boolean = true; - /** @internal */ _cb: Function; /** @internal */ _bounds: any; @@ -69,6 +147,9 @@ export class Img implements OnDestroy { this._isLoaded(false); } + /** + * @input {string} Image src. + */ @Input() get src(): string { return this._src; @@ -87,6 +168,9 @@ export class Img implements OnDestroy { } } + /** + * @private + */ reset() { if (this._requestingSrc) { // abort any active requests @@ -102,6 +186,9 @@ export class Img implements OnDestroy { } } + /** + * @private + */ update() { if (this._src && this._content.isImgsRefreshable()) { if (this.canRequest && (this._src !== this._renderedSrc && this._src !== this._requestingSrc) && !this._tmpDataUri) { @@ -132,10 +219,7 @@ export class Img implements OnDestroy { } } - /** - * @internal - */ - _loadResponse(msg: ImgResponseMessage) { + private _loadResponse(msg: ImgResponseMessage) { this._requestingSrc = null; if (msg.status === 200) { @@ -201,6 +285,11 @@ export class Img implements OnDestroy { return this._rect; } + /** + * @input {any} Sets the bounding rectangle of the element relative to the viewport. + * When using `VirtualScroll`, each virtual item should pass its bounds to each + * `ion-img`. + */ @Input() set bounds(b: any) { if (isPresent(b)) { @@ -208,22 +297,12 @@ export class Img implements OnDestroy { } } - @Input() - get lazyLoad(): boolean { - return this._lazy; - } - set lazyLoad(val: boolean) { - this._lazy = isTrueProperty(val); - } - - @Input() - get webWorker(): boolean { - return this._ww; - } - set webWorker(val: boolean) { - this._ww = isTrueProperty(val); - } - + /** + * @input {boolean} After an image has been successfully downloaded, it can be cached + * in-memory. This is useful for `VirtualScroll` by allowing image responses to be + * cached, and not rendered, until after scrolling has completed, which allows for + * smoother scrolling. + */ @Input() get cache(): boolean { return this._cache; @@ -232,19 +311,27 @@ export class Img implements OnDestroy { this._cache = isTrueProperty(val); } + /** + * @input {string} Image width. If this property is not set it's important that + * the dimensions are still set using CSS. + */ @Input() set width(val: string | number) { this._wQ = getUnitValue(val); this._setDims(); } + /** + * @input {string} Image height. If this property is not set it's important that + * the dimensions are still set using CSS. + */ @Input() set height(val: string | number) { this._hQ = getUnitValue(val); this._setDims(); } - _setDims() { + private _setDims() { if (this.canRender && (this._w !== this._wQ || this._h !== this._hQ)) { var wrapperEle: HTMLImageElement = this._elementRef.nativeElement; var renderer = this._renderer; @@ -263,7 +350,8 @@ export class Img implements OnDestroy { } /** - * Set the `alt` attribute on the inner `img` element. + * @input {string} Set the `alt` attribute which gets assigned to + * the inner `img` element. */ @Input() alt: string = '';