Source: tiny/core/utils/index.js

import { DATA_URI, URL_FILE_EXTENSION, SVG_SIZE, PI_2 } from '../const';
import settings from '../settings';
import EventEmitter from 'eventemitter3';
import TWEEN from '../../../libs/tween.js';
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
import removeItems from 'remove-array-items';
export * from './decide';

let nextUid = 0;

export {
  /**
   * @see {@link https://github.com/tweenjs/tween.js}
   *
   * @memberof Tiny
   * @name TWEEN
   * @type {object}
   */
  TWEEN,
  /**
   * @see {@link https://github.com/kaimallea/isMobile}
   *
   * @memberof Tiny
   * @function isMobile
   * @type {object}
   */
  isMobile,
  /**
   * @see {@link https://github.com/mreinstein/remove-array-items}
   *
   * @example
   *
   * const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
   * removeItems(arr, 3, 4);
   * //=> [1, 2, 3, 8, 9]
   *
   * @memberof Tiny
   * @function removeItems
   * @param {array<object>} arr   - 源数组
   * @param {number} startIdx     - 起始位
   * @param {number} removeCount  - 移除的数量
   * @type {object}
   */
  removeItems,
  /**
   * @see {@link https://github.com/primus/eventemitter3}
   *
   * @memberof Tiny
   * @class EventEmitter
   * @type {EventEmitter}
   */
  EventEmitter,
  /**
   * @memberof Tiny
   * @function pluginTarget
   * @private
   * @type {mixin}
   */
  pluginTarget,
  mixins,
};

/**
 * Gets the next unique identifier
 *
 * @static
 * @memberof Tiny
 * @function uid
 * @return {number} The next unique identifier to use.
 */
export function uid() {
  return ++nextUid;
}

/**
 * 判断对象是否是`Array`类型
 *
 * @static
 * @memberof Tiny
 * @function isArray
 * @param {Object} obj 要判断的对象
 * @return {Boolean}
 */
export const isArray = (Array.isArray || function (obj) {
  return Object.prototype.toString.call(obj) === '[object Array]';
});

/**
 * 判断对象是否是函数类型
 *
 * @static
 * @memberof Tiny
 * @function isFunction
 * @param {Object} obj 要判断的对象
 * @return {Boolean}
 */
export function isFunction(obj) {
  return Object.prototype.toString.call(obj) === '[object Function]';
}

/**
 * 判断对象是否是`Number`类型
 *
 * @static
 * @memberof Tiny
 * @function isNumber
 * @param {Object} obj 要判断的对象
 * @return {Boolean}
 */
export function isNumber(obj) {
  return Object.prototype.toString.call(obj) === '[object Number]';
}

/**
 * 判断对象是否是`String`类型
 *
 * @static
 * @memberof Tiny
 * @function isString
 * @param {Object} obj 要判断的对象
 * @return {Boolean}
 */
export function isString(obj) {
  return typeof obj === 'string';
}

/**
 * 判断对象是否是`Undefined`类型
 *
 * @static
 * @memberof Tiny
 * @function isUndefined
 * @param obj
 * @return {boolean}
 */
export function isUndefined(obj) {
  return obj === void 0;
}

/**
 * 判断对象是否是`Object`类型
 *
 * @static
 * @memberof Tiny
 * @function isObject
 * @param {Object} obj 要判断的对象
 * @return {boolean}
 */
export function isObject(obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
}

/**
 * 高精度获取当前时间,比 `Date.now()` 的精度高1000倍,为考虑不同性能场景下的时间获取,建议使用此方法来替代 `Date.now()`。
 *
 * @static
 * @memberof Tiny
 * @function getTime
 * @return {number}
 */
export function getTime() {
  return window.performance.timing.navigationStart + window.performance.now();
}

/**
 * 生成数字区间内的随机整型数据
 *
 * @example
 *
 *  Tiny.random(10, 18);
 *  //=> 15
 *
 * @static
 * @memberof Tiny
 * @function random
 * @param min
 * @param max
 * @return {number}
 */
export function random(min, max) {
  if (isArray(min)) {
    max = min[ 1 ];
    min = min[ 0 ];
  }
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * `Tiny.random` 的别名
 *
 * @static
 * @memberof Tiny
 * @function randomInt
 * @return {boolean}
 */
export function randomInt() {
  return random.call(this, ...arguments);
}

/**
 * 返回区间内的随机浮点类型数字
 *
 * @static
 * @memberof Tiny
 * @function randomFloat
 * @param min
 * @param max
 * @return {number}
 */
export function randomFloat(min, max) {
  return min + Math.random() * (max - min);
}

/**
 * 生成随机的`Boolean`类型数据
 *
 * @static
 * @memberof Tiny
 * @function randomBool
 * @param {number} chance 生成 true 的几率,默认值:0.5
 * @return {boolean}
 */
export function randomBool(chance) {
  chance = chance ? chance : 0.5; // eslint-disable-line
  return Math.random() < chance;
}

/**
 * 随机生成 {-1, 1} 两个值
 *
 * @example
 *
 *  Tiny.randomPM(0.8);
 *  //=> -1
 *
 * @static
 * @memberof Tiny
 * @function randomPM
 * @param {number} chance 生成 -1 的几率,默认值:0.5
 * @return {number}
 */
export function randomPM(chance) {
  chance = chance ? chance : 0.5; // eslint-disable-line
  return (Math.random() > chance) ? -1 : 1;
}

/**
 * 随机返回数组中的任意对象
 *
 * @memberof Tiny
 * @function randomFromArray
 * @param {array<object>}  arr
 * @return {object}
 */
export function randomFromArray(arr) {
  return arr[ random(0, arr.length - 1) ];
}

/**
 * Converts a hex color number to an [R, G, B] array
 *
 * @memberof Tiny
 * @function hex2rgb
 * @param {number} hex - The number to convert
 * @param  {number[]} [out=[]] If supplied, this array will be used rather than returning a new one
 * @return {number[]} An array representing the [R, G, B] of the color.
 */
export function hex2rgb(hex, out) {
  out = out || [];

  out[ 0 ] = ((hex >> 16) & 0xFF) / 255;
  out[ 1 ] = ((hex >> 8) & 0xFF) / 255;
  out[ 2 ] = (hex & 0xFF) / 255;

  return out;
}

/**
 * Converts a hex color number to a string.
 *
 * @memberof Tiny
 * @function hex2string
 * @param {number} hex - Number in hex
 * @return {string} The string color.
 */
export function hex2string(hex) {
  hex = hex.toString(16);
  hex = '000000'.substr(0, 6 - hex.length) + hex;

  return `#${hex}`;
}

/**
 * Converts a color as an [R, G, B] array to a hex number
 *
 * @memberof Tiny
 * @function rgb2hex
 * @param {number[]} rgb - rgb array
 * @return {number} The color number
 */
export function rgb2hex(rgb) {
  return (((rgb[ 0 ] * 255) << 16) + ((rgb[ 1 ] * 255) << 8) + (rgb[ 2 ] * 255 | 0));
}

/**
 * 转换 color 对象为二进制颜色值
 *
 * @example
 *
 * var color = Tiny.color(255, 255, 0);
 * var hex = Tiny.color2hex(color);
 * //=> 16776960
 * //等于 0xffff00
 *
 * @memberof Tiny
 * @version 1.0.2
 * @param color
 */
export function color2hex(color) {
  return rgb2hex([ color.colorR / 255, color.colorG / 255, color.colorB / 255 ]);
}

/**
 * 转换 hex 为 color 对象
 *
 * @example
 *
 * var hex = 0xAA0055;
 * var color = Tiny.hex2color(hex);
 * //=> Object {colorR: 170, colorG: 0, colorB: 85}
 *
 * @example
 *
 * var hex = 0xFF0000;
 * var color = Tiny.hex2color(hex);
 * //=> Object {colorR: 255, colorG: 0, colorB: 0}
 *
 * @param hex
 * @returns {{colorR, colorG, colorB}}
 */
export function hex2color(hex) {
  const rgb = hex2rgb(hex);
  return color(rgb[ 0 ] * 255, rgb[ 1 ] * 255, rgb[ 2 ] * 255);
}

/**
 * 角度值转弧度
 *
 * @memberof Tiny
 * @function deg2radian
 * @param deg
 * @return {number}
 */
export function deg2radian(deg) {
  return PI_2 * deg / 360;
}

/**
 * 弧度值转角度
 *
 * @memberof Tiny
 * @function radian2deg
 * @param radian
 * @return {number}
 */
export function radian2deg(radian) {
  return 360 * radian / PI_2;
}

/**
 * 获取 XMLHttpRequest 对象,如果 Tiny._XMLHttpRequest 存在,则取之,如果没有,则创建一个 XMLHttpRequest 对象,并存之。
 * 如果 Tiny._XMLHttpRequest 正在使用中,则返回一个新建的 XMLHttpRequest
 *
 * @static
 * @return {XMLHttpRequest}
 */
export function getXMLHttpRequest() {
  let xhr = this._XMLHttpRequest;
  if (!xhr) {
    xhr = new XMLHttpRequest();
    this._XMLHttpRequest = xhr;
  } else {
    if (+xhr.readyState !== 4) {
      return new XMLHttpRequest();
    }
  }
  return xhr;
}

/**
 * get the resolution / device pixel ratio of an asset by looking for the prefix
 * used by spritesheets and image urls
 *
 * @memberof Tiny
 * @function getResolutionOfUrl
 * @param {string} url - the image path
 * @param {number} [defaultValue=1] - the defaultValue if no filename prefix is set.
 * @return {number} resolution / device pixel ratio of an asset
 */
export function getResolutionOfUrl(url, defaultValue) {
  const resolution = settings.RETINA_PREFIX.exec(url);

  if (resolution) {
    return parseFloat(resolution[ 1 ]);
  }

  return defaultValue !== undefined ? defaultValue : 1;
}

/**
 * Typedef for decomposeDataUri return object.
 *
 * @typedef {object} DecomposedDataUri
 * @property {mediaType} Media type, eg. `image`
 * @property {subType} Sub type, eg. `png`
 * @property {encoding} Data encoding, eg. `base64`
 * @property {data} The actual data
 */

/**
 * Split a data URI into components. Returns undefined if parameter `dataUri` is not a valid data URI.
 *
 * @memberof Tiny
 * @function decomposeDataUri
 * @param {string} dataUri - the data URI to check
 * @return {DecomposedDataUri|undefined} The decomposed data uri or undefined
 */
export function decomposeDataUri(dataUri) {
  const dataUriMatch = DATA_URI.exec(dataUri);

  if (dataUriMatch) {
    return {
      mediaType: dataUriMatch[ 1 ] ? dataUriMatch[ 1 ].toLowerCase() : undefined,
      subType: dataUriMatch[ 2 ] ? dataUriMatch[ 2 ].toLowerCase() : undefined,
      encoding: dataUriMatch[ 3 ] ? dataUriMatch[ 3 ].toLowerCase() : undefined,
      data: dataUriMatch[ 4 ],
    };
  }

  return undefined;
}

/**
 * Get type of the image by regexp for extension. Returns undefined for unknown extensions.
 *
 * @memberof Tiny
 * @function getUrlFileExtension
 * @param {string} url - the image path
 * @return {string|undefined} image extension
 */
export function getUrlFileExtension(url) {
  const extension = URL_FILE_EXTENSION.exec(url);

  if (extension) {
    return extension[ 1 ].toLowerCase();
  }

  return undefined;
}

/**
 * Typedef for Size object.
 *
 * @typedef {object} Size
 * @property {width} Width component
 * @property {height} Height component
 */

/**
 * Get size from an svg string using regexp.
 *
 * @memberof Tiny
 * @function getSvgSize
 * @param {string} svgString - a serialized svg element
 * @return {Size|undefined} image extension
 */
export function getSvgSize(svgString) {
  const sizeMatch = SVG_SIZE.exec(svgString);
  const size = {};

  if (sizeMatch) {
    size[ sizeMatch[ 1 ] ] = Math.round(parseFloat(sizeMatch[ 3 ]));
    size[ sizeMatch[ 5 ] ] = Math.round(parseFloat(sizeMatch[ 7 ]));
  }

  return size;
}

/**
 * Helper for checking for webgl support
 *
 * @memberof Tiny
 * @function isWebGLSupported
 * @return {boolean} is webgl supported
 */
export function isWebGLSupported() {
  const contextOptions = { stencil: true, failIfMajorPerformanceCaveat: true };

  try {
    if (!window.WebGLRenderingContext) {
      return false;
    }

    const canvas = document.createElement('canvas');
    let gl = canvas.getContext('webgl', contextOptions) || canvas.getContext('experimental-webgl', contextOptions);

    const success = !!(gl && gl.getContextAttributes().stencil);

    if (gl) {
      const loseContext = gl.getExtension('WEBGL_lose_context');

      if (loseContext) {
        loseContext.loseContext();
      }
    }

    gl = null;

    return success;
  } catch (e) {
    return false;
  }
}

/**
 * Returns sign of number
 *
 * @memberof Tiny
 * @function sign
 * @param {number} n - the number to check the sign of
 * @return {number} 0 if `n` is 0, -1 if `n` is negative, 1 if `n` is positive
 */
export function sign(n) {
  if (n === 0) return 0;

  return n < 0 ? -1 : 1;
}

/**
 * 从数组中移除某个对象
 *
 * @memberof Tiny
 * @function arrayRemoveObject
 * @param {array<object>}  arr
 * @param {object}         delObj
 */
export function arrayRemoveObject(arr, delObj) {
  for (let i = 0, l = arr.length; i < l; i++) {
    if (arr[ i ] === delObj) {
      arr.splice(i, 1);
      break;
    }
  }
}

/**
 * Looks through each value in the list, returning the first one that passes a truth test (**predicate**), or `undefined` if no value passes the test. The function returns as soon as it finds an acceptable element, and doesn't traverse the entire list.
 *
 * @example
 *
 *  var even = Tiny.detect([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
 *  //=> 2
 *
 * @static
 * @memberof Tiny
 * @function detect
 * @param {object}    obj
 * @param {function}  iterator
 * @param {object}    context
 * @param arg1
 * @param arg2
 * @return {boolean}
 */
export function detect(obj, iterator, context, arg1, arg2) {
  let result;
  if (obj === null) {
    return;
  }
  if (obj.length === +obj.length) {
    for (let i = 0, l = obj.length; i < l; i++) {
      result = iterator.call(context, obj[ i ], i, arg1, arg2);
      if (result) {
        return result;
      }
    }
    return false;
  } else {
    for (const key in obj) {
      result = iterator.call(context, obj[ key ], key, arg1, arg2);
      if (result) {
        return result;
      }
    }
    return false;
  }
}

/**
 * 转化坐标值
 *
 * @example
 *
 *  Tiny.point(100, 200);
 *  //=> {x: 100, y: 200}
 *
 * @static
 * @memberof Tiny
 * @function point
 * @param {number}  x
 * @param {number}  y - 如果不传,则等于 x
 * @return {object}
 */
export function point(x, y) {
  return {
    x: x, // eslint-disable-line
    y: (y === void 0 ? x : y),
  };
}

/**
 * 转化缩放值
 *
 * @example
 *
 *  Tiny.scale(2, 1.5);
 *  //=> {scaleX: 2, scaleY: 1.5}
 *
 * @memberof Tiny
 * @function scale
 * @param {number}  x
 * @param {number}  y - 如果不传,则等于 x
 * @return {object}
 */
export function scale(x, y) {
  return {
    scaleX: x,
    scaleY: (y === void 0 ? x : y),
  };
}

/**
 * 转化RGB颜色值
 *
 * @example
 *
 * Tiny.color(0, 255, 255);
 * //=> {colorR: 0, colorG: 255, colorB: 255}
 *
 * @param {number} red
 * @param {number} green
 * @param {number} blue
 * @returns {object}
 */
export function color(red, green, blue) {
  return {
    colorR: Tiny.isUndefined(red) ? 255 : red,
    colorG: Tiny.isUndefined(green) ? 255 : green,
    colorB: Tiny.isUndefined(blue) ? 255 : blue,
  };
}

/**
 * texture 缓存,用于存储已加载的图片 texture
 *
 * @name TextureCache
 * @memberof Tiny
 * @type {object}
 * @private
 */
export const TextureCache = Object.create(null);

/**
 *
 * @name BaseTextureCache
 * @memberof Tiny
 * @type {object}
 * @private
 */
export const BaseTextureCache = Object.create(null);

/**
 * CountDown 缓存,用于存储已实例化的 CountDown 对象
 *
 * @name CountDownCache
 * @memberof Tiny
 * @type {Array}
 * @version 1.1.7
 * @private
 */
export const CountDownCache = [];

/**
 * Destroys all texture in the cache
 *
 * @static
 * @memberof Tiny
 * @function destroyTextureCache
 */
export function destroyTextureCache() {
  let key;

  for (key in TextureCache) {
    TextureCache[ key ].destroy();
  }
  for (key in BaseTextureCache) {
    BaseTextureCache[ key ].destroy();
  }
}

/**
 * Removes all textures from cache, but does not destroy them
 *
 * @static
 * @memberof Tiny
 * @function clearTextureCache
 */
export function clearTextureCache() {
  let key;

  for (key in TextureCache) {
    delete TextureCache[ key ];
  }
  for (key in BaseTextureCache) {
    delete BaseTextureCache[ key ];
  }
}

export let FrameCount = 0;
export function __frameDot() {
  FrameCount++;
}
export function equalsFramCount(fps) {
  switch (fps) {
    case 10:
      return !(FrameCount % 6);
    case 20:
      return !(FrameCount % 3);
    case 30:
      return !(FrameCount % 2);
    case 40:
      return FrameCount % 3;
    case 50:
      return FrameCount % 6;
    default:
      return true;
  }
}
Documentation generated by JSDoc 3.4.3 on Thu May 31 2018 14:40:21 GMT+0800 (CST)