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