Source: utils/Loader.js


/* eslint guard-for-in: "off" */

import {Eventer} from '../eventer/Eventer';
import {Utils} from './Utils';

// const TextureCache = {};

const URL = 'url';
const IMG = 'img';

/**
 *
 * @param {Object} frame object
 * @return {Boolean}
 */
function isFrame(frame) {
  return frame.tagName === 'VIDEO' || frame.tagName === 'CANVAS' || frame.tagName === 'IMG';
}

/**
 * 图片纹理类
 *
 * @class
 * @memberof JC
 * @param {string | Image} texture 图片url或者图片对象.
 * @param {object} options 图片配置
 * @param {boolean} [options.lazy=false] 图片是否需要懒加载
 * @param {string} [options.crossOrigin] 图片是否配置跨域
 * @extends JC.Eventer
 */
function Texture(texture, options) {
  Eventer.call(this);
  options = options || {};

  this.type = '';
  this.url = '';
  this.texture = null;
  this.crossOrigin = options.crossOrigin;
  this.loaded = false;
  this.hadload = false;
  this.lazy = options.lazy || false;

  if (Utils.isString(texture)) {
    this.type = URL;
    this.url = texture;
    this.texture = this.resole();
    this.texture.crossOrigin = this.crossOrigin;
    if (!this.lazy) this.load();
  } else if (isFrame(texture)) {
    this.type = IMG;
    this.loaded = true;
    this.hadload = true;
    this.texture = texture;
  } else {
    console.warn('texture not support this texture');
    return;
  }

  this.listen();
}
Texture.prototype = Object.create(Eventer.prototype);

/**
 * 创建一个图片
 *
 * @private
 * @return {Image}
 */
Texture.prototype.resole = function() {
  return new Image();
};

/**
 * 尝试加载图片
 *
 * @param {String} url 图片url或者图片对象.
 */
Texture.prototype.load = function(url) {
  if (this.hadload || this.type !== URL) return;
  url = url || this.url;
  this.hadload = true;
  this.texture.src = url;
};

/**
 * 监听加载事件
 */
Texture.prototype.listen = function() {
  this.texture.addEventListener('load', () => {
    this.loaded = true;
    this.emit('load');
  });
  this.texture.addEventListener('error', () => {
    this.emit('error');
  });
};

/**
 * 获取纹理的宽
 *
 * @member width
 * @property {Number} width 纹理的宽
 * @memberof JC.Texture
 */
Object.defineProperty(Texture.prototype, 'width', {
  get: function() {
    return this.texture ? this.texture.width : 0;
  },
});

/**
 * 获取纹理的高
 *
 * @member height
 * @property {Number} height 纹理的高
 * @memberof JC.Texture
 */
Object.defineProperty(Texture.prototype, 'height', {
  get: function() {
    return this.texture ? this.texture.height : 0;
  },
});

/**
 * 获取纹理的原始宽
 *
 * @member naturalWidth
 * @property {Number} naturalWidth 纹理的原始宽
 * @memberof JC.Texture
 */
Object.defineProperty(Texture.prototype, 'naturalWidth', {
  get: function() {
    return this.texture ? this.texture.naturalWidth || this.texture.width : 0;
  },
});

/**
 * 获取纹理的原始高
 *
 * @member naturalHeight
 * @property {Number} naturalHeight 纹理的原始高
 * @memberof JC.Texture
 */
Object.defineProperty(Texture.prototype, 'naturalHeight', {
  get: function() {
    return this.texture ? this.texture.naturalHeight || this.texture.height : 0;
  },
});


/**
 * 图片资源加载器
 *
 * @class
 * @param {String} crossOrigin cross-origin config
 * @namespace JC.Loader
 * @extends JC.Eventer
 */
function Loader(crossOrigin) {
  Eventer.call(this);
  this.crossOrigin = crossOrigin;
  this.textures = {};
  this._total = 0;
  this._failed = 0;
  this._received = 0;
}
Loader.prototype = Object.create(Eventer.prototype);

/**
 * 开始加载资源
 *
 * ```js
 * var loadBox = new JC.Loader();
 * loadBox.load({
 *     aaa: 'img/xxx.png',
 *     bbb: 'img/yyy.png',
 *     ccc: 'img/zzz.png'
 * });
 * ```
 *
 * @param {object} srcMap 配置了key-value的json格式数据
 * @return {JC.Loader} 返回本实例对象
 */
Loader.prototype.load = function(srcMap) {
  let This = this;
  this._total = 0;
  this._failed = 0;
  this._received = 0;

  for (let src in srcMap) {
    this._total++;
    this.textures[src] = new Texture(srcMap[src], {crossOrigin: this.crossOrigin});
    bind(this.textures[src]);
  }

  /**
   * @param {Texture} texture
   */
  function bind(texture) {
    texture.on('load', function() {
      This._received++;
      This.emit('update');
      if (This._received + This._failed >= This._total) This.emit('complete');
    });
    texture.on('error', function() {
      This._failed++;
      This.emit('update');
      if (This._received + This._failed >= This._total) This.emit('complete');
    });
  }
  return this;
};

/**
 * 从纹理图片盒子里面通过id获取纹理图片
 *
 * ```js
 * var texture = loadBox.getById('id');
 * ```
 *
 * @param {string} id 之前加载时配置的key值
 * @return {JC.Texture} 包装出来的JC.Texture对象
 */
Loader.prototype.getById = function(id) {
  return this.textures[id];
};

/**
 * 获取资源加载的进度
 *
 * @member progress
 * @property progress {number} 0至1之间的值
 * @memberof JC.Loader
 */
Object.defineProperty(Loader.prototype, 'progress', {
  get: function() {
    return this._total === 0 ? 1 :
      (this._received + this._failed) / this._total;
  },
});


/**
 * 资源加载工具
 *
 * @function
 * @memberof JC
 * @param {object} srcMap key-src map
 * @param {String} crossOrigin cross-origin config
 * @return {JC.Loader}
 */
let loaderUtil = function(srcMap, crossOrigin) {
  return new Loader(crossOrigin).load(srcMap);
};

export {Texture, Loader, loaderUtil};