Source: utils/index.js

import EventEmitter from 'eventemitter3';
import TWEEN from '../libs/tween.js';
import * as base64 from './b64';
import * as mixins from './mixin';
import determineCrossOrigin from './determineCrossOrigin';
import * as url from './url';
import isMobile from './isMobile';

/**
 * Math.PI * 2
 *
 * @static
 * @constant
 * @name PI_2
 * @memberof Tiny
 * @type {number}
 */
export const PI_2 = Math.PI * 2;

/**
 * 弧度值转角度
 *
 * @static
 * @constant
 * @memberof Tiny
 * @name RAD_TO_DEG
 * @type {number}
 */
export const RAD_TO_DEG = 180 / Math.PI;

/**
 * 角度值转弧度
 *
 * @static
 * @constant
 * @memberof Tiny
 * @name DEG_TO_RAD
 * @type {number}
 */
export const DEG_TO_RAD = Math.PI / 180;

/**
 * Regexp for image type by extension.
 *
 * @static
 * @constant
 * @name URL_FILE_EXTENSION
 * @memberof Tiny
 * @type {RegExp}
 * @example `image.png`
 */
export const URL_FILE_EXTENSION = /\.(\w{3,4})(?:$|\?|#)/i;

/**
 * Regexp for data URI.
 * Based on: {@link https://github.com/ragingwind/data-uri-regex}
 *
 * @static
 * @constant
 * @name DATA_URI
 * @memberof Tiny
 * @type {RegExp}
 * @example data:image/png;base64
 */
export const DATA_URI = /^\s*data:(?:([\w-]+)\/([\w+.-]+))?(?:;charset=([\w-]+))?(?:;(base64))?,(.*)/i;

/**
 * Regexp for SVG size.
 *
 * @static
 * @constant
 * @name SVG_SIZE
 * @memberof Tiny
 * @type {RegExp}
 * @example <svg width="100" height="100"></svg>
 */
export const SVG_SIZE = /<svg[^>]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*>/i;

/**
 * The prefix that denotes a URL is for a retina asset.
 *
 * @static
 * @constant
 * @name RETINA_PREFIX
 * @memberof Tiny
 * @type {RegExp}
 * @example `@2x`
 */
export const RETINA_PREFIX = /@([0-9\.]+)x/; // eslint-disable-line

/**
 * 判断对象是否是`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();
}

/**
 * 返回一个数字的符号,即:`Math.sign`
 *
 * @memberof Tiny
 * @function sign
 * @param {number} n - 任意数字
 * @return {number} 此函数共有5种返回值, 分别是 1, -1, 0, -0, NaN. 代表的各是正数, 负数, 正零, 负零, NaN
 */
export function sign(n) {
  return Math.sign(n);
}

/**
 * 生成数字区间内的随机整型数据
 *
 * @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
 * @function color2hex
 * @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}
 *
 * @memberof Tiny
 * @function hex2color
 * @param hex
 * @return {{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 deg * DEG_TO_RAD;
}

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

/**
 * 获取随机颜色值
 *
 * @memberof Tiny
 * @function randomColor
 * @version 1.3.1
 * @return {number} The color number
 */
export function randomColor() {
  const letters = '0123456789ABCDEF';
  let color = '0x';

  for (var i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }

  return rgb2hex(hex2rgb(Number(color)));
}

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

/**
 * 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;
  }
}

/**
 * 从数组中移除某个对象
 *
 * @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;
    }
  }
}

/**
 * Remove a range of items from an array
 *
 * @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}
 */
export function removeItems(arr, startIdx, removeCount) {
  const length = arr.length;

  if (startIdx >= length || removeCount === 0) {
    return;
  }

  removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);

  const len = length - removeCount;

  for (let i = startIdx; i < len; ++i) {
    arr[i] = arr[i + removeCount];
  }

  arr.length = len;
}

/**
 * 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;
}

/**
 * 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 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,
      charset: dataUriMatch[3] ? dataUriMatch[3].toLowerCase() : undefined,
      encoding: dataUriMatch[4] ? dataUriMatch[4].toLowerCase() : undefined,
      data: dataUriMatch[5],
    };
  }

  return undefined;
}

/**
 * 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 = RETINA_PREFIX.exec(url);

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

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

/**
 * 转化坐标值
 *
 * @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}
 *
 * @memberof Tiny
 * @function color
 * @param {number} red
 * @param {number} green
 * @param {number} blue
 * @return {object}
 */
export function color(red, green, blue) {
  return {
    colorR: isUndefined(red) ? 255 : red,
    colorG: isUndefined(green) ? 255 : green,
    colorB: isUndefined(blue) ? 255 : blue,
  };
}

/**
 * 获取两点间直线距离
 *
 * @example
 * Tiny.getDistanceBetweenPoints({x:1,y:1}, {x:4,y:5});
 * //=> 5
 *
 * @memberof Tiny
 * @function getDistanceBetweenPoints
 * @param {object} p1 - The x and y coordinates of the first point.
 * @param {object} p2 - The x and y coordinates of the second point.
 * @return {number} Returns the distance between the points.
 */
export function getDistanceBetweenPoints(p1, p2) {
  return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
};

export {
  /**
   *
   * @memberof Tiny
   * @name TWEEN
   * @type {object}
   */
  TWEEN,
  /**
   *
   * @memberof Tiny
   * @function isMobile
   * @type {object}
   */
  isMobile,
  /**
   * eventemitter3 v3.1.0
   * @see {@link https://github.com/primus/eventemitter3}
   *
   * @memberof Tiny
   * @class EventEmitter
   * @type {EventEmitter}
   */
  EventEmitter,
  /**
   * @static
   * @memberof Tiny
   * @name mixins
   * @type {object}
   */
  mixins,
  /**
   * @static
   * @memberof Tiny
   * @name base64
   * @type {object}
   */
  base64,
  /**
   * @static
   * @memberof Tiny
   * @name url
   * @type {object}
   */
  url,
  determineCrossOrigin,
};
Documentation generated by JSDoc 3.4.3 on Fri Jul 09 2021 19:32:25 GMT+0800 (CST)