赞
踩
在COCOS中的一些常用的工具函数,一般都放在一个全局工具类中
之前项目中用到的一些方法,记录一下
* 深度拷贝 * @param {any} sObj 拷贝的对象 * @returns */ public static clone(sObj: any) { if (sObj === null || typeof sObj !== "object") { return sObj; } let s: { [key: string]: any } = {}; if (sObj.constructor === Array) { s = []; } for (let i in sObj) { if (sObj.hasOwnProperty(i)) { s[i] = this.clone(sObj[i]); } } return s; }
* 将object转化为数组 * @param { any} srcObj * @returns */ public static objectToArray(srcObj: { [key: string]: any }) { let resultArr: any[] = []; // to array for (let key in srcObj) { if (!srcObj.hasOwnProperty(key)) { continue; } resultArr.push(srcObj[key]); } return resultArr; }
* @param { any} srcObj * @param { string} objectKey * @returns */ public static arrayToObject(srcObj: any, objectKey: string) { let resultObj: { [key: string]: any } = {}; // to object for (var key in srcObj) { if (!srcObj.hasOwnProperty(key) || !srcObj[key][objectKey]) { continue; } resultObj[srcObj[key][objectKey]] = srcObj[key]; } return resultObj; }
* 从n个数中获取m个随机数 * @param {Number} n 总数 * @param {Number} m 获取数 * @returns {Array} array 获取数列 */ public static getRandomNFromM(n: number, m: number) { let array: any[] = []; let intRd: number = 0; let count: number = 0; while (count < m) { if (count >= n + 1) { break; } intRd = this.getRandomInt(0, n); var flag = 0; for (var i = 0; i < count; i++) { if (array[i] === intRd) { flag = 1; break; } } if (flag === 0) { array[count] = intRd; count++; } } return array; }
* 获取随机整数
* @param {Number} min 最小值
* @param {Number} max 最大值
* @returns
*/
public static getRandomInt(min: number, max: number) {
let r: number = Math.random();
let rr: number = r * (max - min + 1) + min;
return Math.floor(rr);
}
* 获取随机数
* @param {Number} min 最小值
* @param {Number} max 最大值
* @returns
*/
public static getRandom(min: number, max: number) {
let r: number = Math.random();
let rr: number = r * (max - min) + min;
return rr;
}
* 获取字符串长度 * @param {string} render * @returns */ public static getStringLength(render: string) { let strArr: string = render; let len: number = 0; for (let i: number = 0, n = strArr.length; i < n; i++) { let val: number = strArr.charCodeAt(i); if (val <= 255) { len = len + 1; } else { len = len + 2; } } return Math.ceil(len / 2); }
* 判断是否是新的一天 * @param {Object|Number} dateValue 时间对象 todo MessageCenter 与 pve 相关的时间存储建议改为 Date 类型 * @returns {boolean} */ public static isNewDay(dateValue: any) { // todo:是否需要判断时区? var oldDate: any = new Date(dateValue); var curDate: any = new Date(); //@ts-ignore var oldYear = oldDate.getYear(); var oldMonth = oldDate.getMonth(); var oldDay = oldDate.getDate(); //@ts-ignore var curYear = curDate.getYear(); var curMonth = curDate.getMonth(); var curDay = curDate.getDate(); if (curYear > oldYear) { return true; } else { if (curMonth > oldMonth) { return true; } else { if (curDay > oldDay) { return true; } } } return false; }
* 字符串转数组 * @param string * @private */ public static _stringToArray(string: string) { // 用于判断emoji的正则们 var rsAstralRange = '\\ud800-\\udfff'; var rsZWJ = '\\u200d'; var rsVarRange = '\\ufe0e\\ufe0f'; var rsComboMarksRange = '\\u0300-\\u036f'; var reComboHalfMarksRange = '\\ufe20-\\ufe2f'; var rsComboSymbolsRange = '\\u20d0-\\u20ff'; var rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange; var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); var rsFitz = '\\ud83c[\\udffb-\\udfff]'; var rsOptVar = '[' + rsVarRange + ']?'; var rsCombo = '[' + rsComboRange + ']'; var rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')'; var reOptMod = rsModifier + '?'; var rsAstral = '[' + rsAstralRange + ']'; var rsNonAstral = '[^' + rsAstralRange + ']'; var rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}'; var rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]'; var rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*'; var rsSeq = rsOptVar + reOptMod + rsOptJoin; var rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); var hasUnicode = function (val: any) { return reHasUnicode.test(val); }; var unicodeToArray = function (val: any) { return val.match(reUnicode) || []; }; var asciiToArray = function (val: any) { return val.split(''); }; return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string); }
* 判断当前时间是否在有效时间内 * @param {String|Number} start 起始时间。带有时区信息 * @param {String|Number} end 结束时间。带有时区信息 */ public static isNowValid(start: any, end: any) { var startTime = new Date(start); var endTime = new Date(end); var result = false; if (startTime.getDate() + '' !== 'NaN' && endTime.getDate() + '' !== 'NaN') { var curDate = new Date(); result = curDate < endTime && curDate > startTime; } return result; }
* 返回相隔天数 * @param start * @param end * @returns */ public static getDeltaDays(start: any, end: any) { start = new Date(start); end = new Date(end); let startYear: number = start.getFullYear(); let startMonth: number = start.getMonth() + 1; let startDate: number = start.getDate(); let endYear: number = end.getFullYear(); let endMonth: number = end.getMonth() + 1; let endDate: number = end.getDate(); start = new Date(startYear + '/' + startMonth + '/' + startDate + ' GMT+0800').getTime(); end = new Date(endYear + '/' + endMonth + '/' + endDate + ' GMT+0800').getTime(); let deltaTime = end - start; return Math.floor(deltaTime / (24 * 60 * 60 * 1000)); }
* 获取数组最小值 * @param array 数组 * @returns */ public static getMin(array: number[]) { let result: number = null!; if (array.constructor === Array) { let length = array.length; for (let i = 0; i < length; i++) { if (i === 0) { result = Number(array[0]); } else { result = result > Number(array[i]) ? Number(array[i]) : result; } } } return result; }
* 格式化两位小数点
* @param time
* @returns
*/
public static formatTwoDigits(time: number) {
return (Array(2).join(0) + time).slice(-2);
}
* 格式化时间戳 * return 时分秒 */ public static formatTimestamp(time: number) { let str = ""; if (time >= 1000 * 60 * 60) { str += Math.floor(time / (1000 * 60 * 60)) + "时"; time = time % (1000 * 60 * 60); } if (time >= 1000 * 60) { str += Math.floor(time / (1000 * 60)) + "分"; time = time % (1000 * 60); } if (time >= 1000) { str += Math.floor(time / 1000) + "秒"; } if (str == "") { return "0秒"; } else { return str; } }
* 获取格式化后的日期(不含小时分秒) */ public static getDay(time?: number | Date) { let date: Date if (!time) { date = new Date() } else if (typeof time == "number") { date = new Date(time) } else { date = time } return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate(); }
* 格式化名字,XXX... * @param {string} name 需要格式化的字符串 * @param {number}limit * @returns {string} 返回格式化后的字符串XXX... */ public static formatName(name: string, limit: number) { limit = limit || 6; var nameArray = this._stringToArray(name); var str = ''; var length = nameArray.length; if (length > limit) { for (var i = 0; i < limit; i++) { str += nameArray[i]; } str += '...'; } else { str = name; } return str; }
* 格式化金钱数,超过10000 转换位 10K 10000K 转换为 10M,保留两位小数 * @param {number}money 需要被格式化的数值 * @returns {string}返回 被格式化的数值 */ public static formatMoney(money: number) { let arrUnit: string[] = ['', '万', '亿', '兆', '京', '垓', '秭', '穣', '沟', '涧', '正', '载', '极']; let strValue: string = ''; for (let idx: number = 0; idx < arrUnit.length; idx++) { if (money >= 1E+4) { money /= 1E+4; } else { let str = money.toFixed(2).toString() strValue = this.removeTrailingZeros(str) + arrUnit[idx]; break; } } if (strValue === '') { let str = money.toFixed(2).toString() strValue = this.removeTrailingZeros(str) + 'U'; //超过最大值就加个U } return strValue; } //去除多余0 public static removeTrailingZeros(numberString: string): string { let trimmedString = numberString.trim(); // 去除首尾空格 let decimalIndex = trimmedString.indexOf('.'); if (decimalIndex !== -1) { let endIndex = trimmedString.length - 1; while (trimmedString[endIndex] === '0') { endIndex--; } if (trimmedString[endIndex] === '.') { endIndex--; // 如果小数点后面全是零,也去掉小数点 } return trimmedString.slice(0, endIndex + 1); } return trimmedString; }
* 根据剩余秒数格式化剩余时间 返回 HH:MM:SS * @param {Number} leftSec */ public static formatTimeForSecond(leftSec: number, withoutSeconds: boolean = false) { let timeStr: string = ''; let sec: number = leftSec % 60; let leftMin: number = Math.floor(leftSec / 60); leftMin = leftMin < 0 ? 0 : leftMin; let hour: number = Math.floor(leftMin / 60); let min: number = leftMin % 60; if (hour > 0) { timeStr += hour > 9 ? hour.toString() : '0' + hour; timeStr += ':'; } else { timeStr += '00:'; } timeStr += min > 9 ? min.toString() : '0' + min; if (!withoutSeconds) { timeStr += ':'; timeStr += sec > 9 ? sec.toString() : '0' + sec; } return timeStr; }
* 根据剩余毫秒数格式化剩余时间 返回{h,m,s}
*
* @param {Number} ms
*/
public static formatTimeForMillisecond(ms: number): Object {
let second: number = Math.floor(ms / 1000 % 60);
let minute: number = Math.floor(ms / 1000 / 60 % 60);
let hour: number = Math.floor(ms / 1000 / 60 / 60);
return { 'hour': hour, 'minute': minute, 'second': second };
}
/** *将数组内容进行随机排列 * @param {Array}arr 需要被随机的数组 * @returns */ public static rand(arr: []): [] { let arrClone = this.clone(arr); // 首先从最大的数开始遍历,之后递减 for (let i: number = arrClone.length - 1; i >= 0; i--) { // 随机索引值randomIndex是从0-arrClone.length中随机抽取的 const randomIndex: number = Math.floor(Math.random() * (i + 1)); // 下面三句相当于把从数组中随机抽取到的值与当前遍历的值互换位置 const itemIndex: number = arrClone[randomIndex]; arrClone[randomIndex] = arrClone[i]; arrClone[i] = itemIndex; } // 每一次的遍历都相当于把从数组中随机抽取(不重复)的一个元素放到数组的最后面(索引顺序为:len-1,len-2,len-3......0) return arrClone; }
/* 返回数组中的一个随机元素
* @param arr 数组
* @returns 数组中随机一个,以及对应的下标
*/public static ranGetFromArray<T>(arr: Array<T>) {
let len = arr.length;
let idx = Math.floor(len * Math.random());
return arr[idx], idx;
}
public static shuffle(arr: any) {
if (Array.isArray(arr)) {
let newArr: any = arr.concat();
newArr.sort(() => {
return 0.5 - Math.random()
});
return newArr;
}
}
/** * 两个数值数组取相同的值,返回一个新数组 * * @static * @param {number[]} arr1 * @param {number[]} arr2 * @returns * @memberof util */ public static filterDifferentValue(arr1: number[], arr2: number[]) { let arr: number[] = []; arr = arr1.filter((item: number) => { return arr2.indexOf(item) !== -1; }) return arr; }
/**
* 获取数组中随机一个元素
* @param arr
* @returns
*/
public static getRandomItemFromArray(arr: any[]) {
return arr[Math.floor(Math.random() * arr.length)];
}
/** * 将世界坐标转化为父节点为local下的局部坐标(通过矩阵乘法) * @param local 待转化的节点 * @param parent 父节点 * @returns 转化得到的父节点下的坐标 */ public static lp2Lp(local: Node, parent: Node): Vec3 { let wp = local.getWorldPosition(); let lp = new Vec3(); let w2LMatrix = parent.getWorldMatrix(); Vec3.transformMat4(lp, wp, w2LMatrix.invert()); return lp; } /** * 将世界坐标转化为父节点为local下的局部坐标(通过矩阵乘法) * @param wp 世界坐标 * @param parent 父节点 * @returns 转化得到的父节点下的坐标 */ public static wp2Lp(wp: Vec3, parent: Node) { let lp = new Vec3(); let w2LMatrix = parent.getWorldMatrix(); Vec3.transformMat4(lp, wp, w2LMatrix.invert()); return lp; }
/**
* 判断矩形是否和一个点相交(世界坐标)
*/
public static rectContainsPoint(node: Node, wordPoint: Vec2) {
if (!node.getComponent(UITransform)) return false;
return node.getComponent(UITransform).getBoundingBoxToWorld().contains(wordPoint);
}
/**
* 判断矩形是否和一个点相交(本地坐标)
*/
public static rectContainsPointInLocalPos(node: Node, localPoint: Vec2) {
if (!node.getComponent(UITransform)) return false;
return node.getComponent(UITransform).getBoundingBox().contains(localPoint);
}
/** * 动画播放 * @param pos 动画播放的位置 * @param hide 播放完毕是否隐藏组件 */ public static playAniOnce(aniNode: Node, call?: Function, clipName?: string, pos?: Vec3, hide?: boolean) { if (!aniNode) { return } if (pos) { aniNode.setWorldPosition(pos) } aniNode.active = true let ani = aniNode.getComponent(Animation) ani.once(Animation.EventType.FINISHED, () => { call && call() if (hide) { aniNode.active = false } }) ani.play(clipName) }
/**等待下一帧 */
public static waitForNextFrame(target: Component) {
return new Promise((resolve, reject) => {
target.scheduleOnce(resolve, 0);
});
}
/**等待若干秒,默认1s */
public static waitForSeconds(target: Component, seconds: number = 1) {
return new Promise((resolve, reject) => {
target.scheduleOnce(resolve, seconds);
});
}
/**是否跨天 */
public static isAcrossADay(preTimestamp: number, nowTimestamp: number) {
const days = this.getDeltaDays(preTimestamp, nowTimestamp);
if (days >= 1) {
return true;
}
return false;
}
/**透明度变化*/ public static fadeTo(target: UIOpacity, val: number, dura: number, callBack = null) { return tween(target).to(dura, { opacity: val }, { easing: easing.sineOut }).call(callBack).start(); } /**旋转多少角度 */ public static rotate2D(target: Node, rotation: number, dura: number, callback = null) { return tween(target).to(dura, { eulerAngles: new Vec3(0, 0, rotation) }, { easing: easing.sineOut }).call(callback).start(); } /**移动到指定位置(相对坐标) */ public static moveTo(target: Node, pos: Vec3, dura: number, callback = null, ease = easing.sineOut) { return tween(target).to(dura, { position: pos }, { easing: ease }).call(callback).start(); } /**移动到指定位置(相对位移) */ public static moveBy(target: Node, delta: Vec3, dura: number, callback = null, ease = easing.sineOut) { return tween(target).by(dura, { position: delta }, { easing: ease }).call(callback).start(); } /** * 贝塞尔移动(两次)局部坐标系 * @param target 对象节点 * @param dura 移动时间 * @param startP 起始点 * @param ctrlP 控制点 * @param endP 目标点 * @param callback 回调函数 * @param ease 缓动曲线 https://www.xuanfengge.com/easeing/easeing/ * @returns */ public static beizerTo(target: Node, dura: number, startP: Vec3, ctrlP: Vec3, endP: Vec3, callback = null, ease = easing.linear) { let opts: any = {}; let twoBeizer = (ratio: number, startP: Vec3, ctrlP: Vec3, endP: Vec3) => { ratio = ease(ratio); let x = (1 - ratio) * (1 - ratio) * startP.x + 2 * ratio * (1 - ratio) * ctrlP.x + ratio * ratio * endP.x; let y = (1 - ratio) * (1 - ratio) * startP.y + 2 * ratio * (1 - ratio) * ctrlP.y + ratio * ratio * endP.y; let z = (1 - ratio) * (1 - ratio) * startP.z + 2 * ratio * (1 - ratio) * ctrlP.z + ratio * ratio * endP.z; return new Vec3(x, y, z); } opts.onUpdate = (temp: Vec3, ratio: number) => { target.position = twoBeizer(ratio, startP, ctrlP, endP); } return tween(target).to(dura, {}, opts).call(callback).start(); } /** 单位向量围绕某个点旋转N°后的坐标 * */ public static getRatotateVec(nowPos: Vec3 | Vec2, center: Vec3 | Vec2, angle: number) { let degree = angle * Math.PI / 180; let tagetX = (nowPos.x - center.x) * Math.cos(degree) - (nowPos.y - center.y) * Math.sin(degree) + center.x; let tagetY = (nowPos.y - center.y) * Math.cos(degree) + (nowPos.x - center.x) * Math.sin(degree) + center.y; return new Vec3(tagetX, tagetY, 0); }
/** * 判断两个节点的中心点是否在对方的区域内(不是矩形碰撞) */ public static checkTwoNodeCenterPos(node1: Node, node2: Node) { const pos1 = node1.worldPosition; const pos2 = node2.worldPosition; const form1 = node1.getComponent(UITransform); const form2 = node2.getComponent(UITransform); const width1 = form1.contentSize.width * node1.scale.x; const height1 = form1.contentSize.height * node1.scale.y; if (Math.abs(pos1.x - pos2.x) < width1 / 2 && Math.abs(pos1.y - pos2.y) < height1 / 2) { return true; } const width2 = form2.contentSize.width * node2.scale.x; const height2 = form2.contentSize.height * node2.scale.y; if (Math.abs(pos1.x - pos2.x) < width2 / 2 && Math.abs(pos1.y - pos2.y) < height2 / 2) { return true; } return false; }
/** 判断某个点是否在节点区域 * 触摸点坐标直接填入即可 * 矩形范围 */ public static checkPosBelongScope(pos1: Vec3 | Vec2, node: Node) { const pos2 = node.worldPosition; const form = node.getComponent(UITransform); const width = form.contentSize.width * node.scale.x; const height = form.contentSize.height * node.scale.y; let chanceX = (0.5 - form.anchorPoint.x) * width; let chanceY = (0.5 - form.anchorPoint.y) * height; const pos3 = pos2.clone().add(new Vec3(chanceX, chanceY)); if (Math.abs(pos1.x - pos3.x) < width / 2 && Math.abs(pos1.y - pos3.y) < height / 2) { return true; } return false; }
/** 判断无旋转的矩形,是否有交叉 */ public static checkRectangleScope(node1: Node, node2: Node) { const pos1 = node1.worldPosition; const pos2 = node2.worldPosition; const form1 = node1.getComponent(UITransform); const form2 = node2.getComponent(UITransform); const width1 = form1.contentSize.width * node1.scale.x; const height1 = form1.contentSize.height * node1.scale.y; const width2 = form2.contentSize.width * node2.scale.x; const height2 = form2.contentSize.height * node2.scale.y; const dianceW = width1 / 2 + width2 / 2; const dianceH = height1 / 2 + height2 / 2; if (Math.abs(pos1.x - pos2.x) < dianceW && Math.abs(pos1.y - pos2.y) < dianceH) { return true; } return false; }
/** 对比两个数组是否一样 * 是否返回不同 */ public static compareTwoArry(one: any[], two: any[], isBo: boolean = false) { if (one.length != two.length && !isBo) return false; let oneArry = []; // let twoArry = []; // 为了不改变原来的数据,拷贝最好 let oneClone = Tool.clone(one); let twoClone: any[] = Tool.clone(two); for (let i = 0; i < one.length; i++) { let s = oneClone.pop(); let index = twoClone.indexOf(s); if (index < 0) { if (!isBo) return false; oneArry.push(s); } else { twoClone.splice(index, 1); } } if (isBo) { return [oneArry, twoClone]; } else { return true; } }
/** * 播放激励视频接口 endCallback 看完视频要播放的回调 * 需要接入TTSDK * @param endCallback * @param failCallback * Constant.LOOK_VEDIO_DEBUG 为全局控制广告开关 * UIMgr.instance.ShowBlock();避免多次点击,弹出加载中界面 */ public static lookVideoJL(endCallback: Function, failCallback: Function = null, videoKey: string = TTSDK.VIDEO_AD_KEY.JL) { let callback = () => { UIMgr.instance.HideBlock(); endCallback && endCallback(); }; if (Constant.LOOK_VEDIO_DEBUG) { callback(); } else { UIMgr.instance.ShowBlock(); setTimeout(() => { UIMgr.instance.HideBlock(); }, 1.8 * 1000); TTSDK.instance.createVideoAd(videoKey, callback, failCallback);//调用广告 } }
/** 改变透明度 * @param show: true=渐现 false=渐隐 * @param time:动画持续时间 */ public static changeOpacity(node: Node, show: boolean = true, call: Function = null, time: number = 0.6) { let opa = node.getComponent(UIOpacity) if (!opa) { Log.Error(`${node.name}上找不到uiopacity组件`) } if (opa) { if (show) { opa.opacity = 0 node.active = true tween(opa).to(time, { opacity: 255 }).call(() => { call && call() }).start() } else { tween(opa).to(time, { opacity: 0 }).call(() => { node.active = false opa.opacity = 255 call && call() }).start() } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。