UNPKG

@egjs/view360

Version:

360 integrated viewing solution from inside-out view to outside-in view. It provides user-friendly service by rotating 360 degrees through various user interaction such as motion sensor and touch.

154 lines (130 loc) 3.53 kB
import Component from "@egjs/component"; const STATUS = { "NONE": 0, "LOADING": 1, "LOADED": 2, "ERROR": 3 }; const EVENT = { "READYSTATECHANGE": "readystatechange" }; export default class ImageLoader extends Component { static STATUS = STATUS; constructor(image) { // Super constructor super(); this._image = null; this._onceHandlers = []; this._loadStatus = STATUS.NONE; image && this.set(image); } get() { return new Promise((res, rej) => { if (!this._image) { rej("ImageLoader: image is not defiend"); } else if (this._loadStatus === STATUS.LOADED) { res(this.getElement()); } else if (this._loadStatus === STATUS.LOADING) { /* Check isMaybeLoaded() first because there may have posibilities that image already loaded before get is called. for example calling get on external image onload callback.*/ if (ImageLoader.isMaybeLoaded(this._image)) { this._loadStatus = STATUS.LOADED; res(this.getElement()); } else { this.on(EVENT.READYSTATECHANGE, e => { if (e.type === STATUS.LOADED) { res(this.getElement()); } else { rej("ImageLoader: failed to load images."); } }); } } else { rej("ImageLoader: failed to load images"); } }); } /** * @param image img element or img url or array of img element or array of img url */ set(image) { this._loadStatus = STATUS.LOADING; this._image = ImageLoader.createElement(image); if (ImageLoader.isMaybeLoaded(this._image)) { this._loadStatus = STATUS.LOADED; return; } this.onceLoaded( this._image, () => { this._loadStatus = STATUS.LOADED; this.trigger(EVENT.READYSTATECHANGE, { type: STATUS.LOADED }); }, () => { this._loadStatus = STATUS.ERROR; this.trigger(EVENT.READYSTATECHANGE, { type: STATUS.ERROR }); } ); } static createElement(image) { const images = image instanceof Array ? image : [image]; return images.map(img => { let _img = img; if (typeof img === "string") { _img = new Image(); _img.crossOrigin = "anonymous"; _img.src = img; } return _img; }); } getElement() { return this._image.length === 1 ? this._image[0] : this._image; } static isMaybeLoaded(image) { let result = false; if (image instanceof Image) { result = image.complete && image.naturalWidth !== 0; } else if (image instanceof Array) { result = !image.some(img => !img.complete || img.naturalWidth === 0); } return result; } onceLoaded(target, onload, onerror) { const targets = target instanceof Array ? target : [target]; const targetsNotLoaded = targets.filter(img => !ImageLoader.isMaybeLoaded(img)); const loadPromises = targetsNotLoaded.map(img => new Promise((res, rej) => { this._once(img, "load", () => (res(img))); this._once(img, "error", () => (rej(img))); })); Promise.all(loadPromises).then( result => (onload(targets.length === 1 ? targets[0] : targets)), reason => (onerror(reason)) ); } _once(target, type, listener) { const fn = event => { target.removeEventListener(type, fn); listener(event); }; target.addEventListener(type, fn); this._onceHandlers.push({target, type, fn}); } getStatus() { return this._loadStatus; } destroy() { this._onceHandlers.forEach(handler => { handler.target.removeEventListener(handler.type, handler.fn); }); this._onceHandlers = []; this._image.src = ""; this._image = null; this._loadStatus = STATUS.NONE; } }