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