import { Prefab, resources, Node, find, instantiate, Vec3, log, Component, Button, warn, Asset, error, v2, UITransform, tween, v3, Vec2, director, ImageAsset, assetManager, } from "cc"; import BigNumber from "./BigNumber"; import { EDITOR } from "cc/env"; import { LanguageManager } from "./LanguageManager"; import PoolMgr from "./PoolMgr"; import BaseUI from "../base/BaseUI"; import { Tips } from "../mgr/Tips"; import DisplayNumber from "./DisplayNumber"; export enum EFLY_TYPE { 金币, 体力, 钻石, } export default class Utils { public static copyText(text: string) { navigator.clipboard.writeText(text).then( (res) => { console.log(" writeText success: ", res); // Tips.show(Utils.setI18nLabel("Share.LinkCopied")); }, (err) => { console.log(" writeText error: ", err); this.copyTextFallback(text); } ); } private static copyTextFallback(text) { const textArea = document.createElement("textarea"); textArea.value = text; document.body.appendChild(textArea); textArea.select(); try { document.execCommand("copy"); Tips.show(Utils.setI18nLabel("Share.LinkCopied")); //"Link copied!"); } catch (err) { Tips.show(Utils.setI18nLabel("Share.Title1")); } document.body.removeChild(textArea); } static sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } static padNumber(num, length): string { let str = num.toString(); while (str.length < length) { str = "0" + str; } return str; } static log(o: any) { if ("1" == this.getQueryString("log")) { console.log(o); } } // 创建UI static createUI( filepath: string, parent: Node = null, callback: Function = null ): Promise { return new Promise((resolve, reject) => { resources.load(filepath, Prefab, (err, ret) => { log("加载UI:" + filepath); if (err) { console.error(err); reject(); return; } if (parent == null) { parent = find("Canvas/popupNode"); } let index = filepath.lastIndexOf("/"); let name = filepath.substr(index + 1, filepath.length - index); if (parent.getComponentInChildren(name)) { console.log("重复UI跳过"); return; } var tmp: Node = instantiate(ret); tmp.parent = parent; if (callback) callback(tmp); resolve(tmp); }); }); } // 创建预制体 static createPrefab( filepath: string, parent: Node = null, callback: Function = null, pos: Vec3 = null ) { return new Promise((resolve, reject) => { resources.load(filepath, Prefab, (err, ret) => { if (err) { console.error(err); reject(); return; } if (parent == null) { parent = find("Canvas"); } var tmp: Node = instantiate(ret); // tmp.opacity = 0; // tmp.runAction( // sequence( // delayTime(0.01), // callFunc(() => { // tmp.opacity = 255; // }) // ) // ); tmp.parent = parent; if (pos) { tmp.position = pos; } if (callback) callback(tmp); resolve(tmp); }); }); } // 获取随机数 public static getRandom(lower, upper): number { return Math.random() * (upper - lower) + lower; } // 获取随机整数 public static getRandomInt(lower, upper): number { return Math.floor(Math.random() * (upper - lower)) + lower; } // 获取随机整数 public static seedRandomInt(lower, upper): number { return Utils.getRandomInt(lower, upper); } // 格式化数字 public static formatNumber(num: number, afterdot: number = 2): string { num = Number(num); num = Number(num.toFixed(afterdot)); if (num < 1000) { return num.toString(); } return DisplayNumber.displayNumber(num,afterdot); // return BigNumber.getLargeString(num); } public static getPowNum(p) { return Math.pow(10, p); } // 设置服务器时间 public static setServerTime(time: number) { Utils.timeOffset = time - new Date().getTime(); } // 获取服务器时间 public static timeOffset: number = 0; public static getServerTime() { return new Date().getTime() + Utils.timeOffset; } // 添加点击事件 public static addClickEvent( node, target, component, handler, customEventData ) { var eventHandler = new Component.EventHandler(); eventHandler.target = target; eventHandler.component = component; eventHandler.handler = handler; if (customEventData) eventHandler.customEventData = customEventData; var clickEvents = node.getComponent(Button).clickEvents; if (clickEvents.length > 0) { // if (!EDITOR) warn("按钮已经存在绑定,跳过自动绑定", node.name); return; } console.log(node.name, target.name, component); clickEvents.push(eventHandler); } public static secondsToDHMS(seconds) { if (seconds <= 0) { return `00d 00h:00m:00s`; } const days = Math.floor(seconds / (24 * 3600)); let d = days < 9 ? "0" + days : days; const hours = Math.floor((seconds % (24 * 3600)) / 3600); let h = hours < 9 ? "0" + hours : hours; const minutes = Math.floor((seconds % 3600) / 60); let min = minutes < 9 ? "0" + minutes : minutes; const second = Math.floor(seconds % 60); let s = second < 9 ? "0" + second : second; let str = days > 0 ? `${d}d ${h}h:${min}m:${s}s` : `${h}h:${min}m:${s}s`; return str; } public static formatTimestamp(timestamp) {} // 秒转换为时分秒 public static getToTimeByS(second: number) { const totalSeconds = Math.floor(second); const hours = Math.floor(totalSeconds / 3600) % 24; const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; return [hours, minutes, seconds] .map((unit) => (unit < 10 ? "0" + unit : unit.toString())) // 确保每个部分都是两位数 .join(":"); // 拼接为字符串 } private static padStartCustom(str, targetLength, padString) { str = String(str); padString = String(padString); if (str.length >= targetLength) { return str; } targetLength -= str.length; if (targetLength > padString.length) { padString = padString.repeat(Math.ceil(targetLength / padString.length)); } return padString.slice(0, targetLength) + str; } public static formateDateRemaining(targetTimestamp: number) { const now = Utils.getServerTime(); const remainingTime = targetTimestamp - now; const days = Math.floor(remainingTime / (1000 * 60 * 60 * 24)); const hours = Math.floor( (remainingTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60) ); const minutes = Math.floor( (remainingTime % (1000 * 60 * 60)) / (1000 * 60) ); const seconds = Math.floor((remainingTime % (1000 * 60)) / 1000); const dStr = days < 10 ? "0" + days : days; const hStr = hours < 10 ? "0" + hours : hours; const mStr = minutes < 10 ? "0" + minutes : minutes; const sStr = seconds < 10 ? "0" + seconds : seconds; return `${dStr}d ${hStr}h:${mStr}m:${sStr}s`; } public static formatTimeRemaining(targetTimestamp) { // 获取当前时间戳 const now = Date.now(); // 计算剩余时间的毫秒数 let remainingTime = targetTimestamp - now; // 如果剩余时间小于等于 0,表示目标时间已经过去 if (remainingTime <= 0) { return "00:00:00"; } // 计算小时、分钟、秒 const hours = Math.floor(remainingTime / (1000 * 60 * 60)); remainingTime -= hours * 1000 * 60 * 60; const minutes = Math.floor(remainingTime / (1000 * 60)); remainingTime -= minutes * 1000 * 60; const seconds = Math.floor(remainingTime / 1000); // 格式化为两位数的字符串 const formattedHours = Utils.padStartCustom(hours, 2, "0"); const formattedMinutes = Utils.padStartCustom(minutes, 2, "0"); const formattedSeconds = Utils.padStartCustom(seconds, 2, "0"); return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`; } // 秒转换为时分秒 public static getTimeStrByS(second: number) { second = Math.floor(second); if (second < 0) second = 0; var d = Math.floor(second / 3600 / 24); second -= d * 3600 * 24; var h = Math.floor(second / 3600); second -= h * 3600; var m = Math.floor(second / 60); second -= m * 60; var front = "00"; if (h > 9) { front = "" + h; } else { front = "0" + h; } var mid = "00"; if (m > 9) { mid = "" + m; } else { mid = "0" + m; } var back = "00"; if (second > 9) { back = "" + second; } else { back = "0" + second; } if (d > 0) { return d + ":" + h + ":" + m; } else { var longTime = h > 0; if (longTime) { return front + ":" + mid; } else { return mid + ":" + back; //+ '秒'; } } } public static formattedDate(timestamp) { const date = new Date(Number(timestamp)); // 获取年月日 const year = date.getFullYear(); let month = date.getMonth() + 1; let monthS = month < 9 ? "0" + month : month; let day = date.getDate(); let dayS = day < 9 ? "0" + day : day; const hours = date.getHours(); let hoursS = hours < 9 ? "0" + hours : hours; const minutes = date.getMinutes(); let minutesS = minutes < 9 ? "0" + minutes : minutes; const seconds = date.getSeconds(); let secondsS = seconds < 9 ? "0" + seconds : seconds; const formattedDate = `${year}-${monthS}-${dayS} ${hoursS}:${minutesS}:${secondsS}`; return formattedDate; } public static formattedOnlyDate(timestamp) { const date = new Date(Number(timestamp)); // 获取年月日 const year = date.getFullYear(); let month = date.getMonth() + 1; let monthS = month < 9 ? "0" + month : month; let day = date.getDate(); let dayS = day < 9 ? "0" + day : day; const formattedDate = `${year}-${monthS}-${dayS}`; return formattedDate; } // 格式化金币 public static formatCoin(num: number) { num = Math.floor(num); return BigNumber.getLargeString(num); } // 加载资源 public static loadRes(path: string, type: typeof Asset): Promise { return new Promise((resolve, reject) => { resources.load(path, type, (err, ret) => { if (err) { error(path, err); reject(null); } else { resolve(ret); } }); }); } public static loadRemote(path: string, type: typeof ImageAsset) { return new Promise((resolve, reject) => { assetManager.loadRemote(path, type, (err, ret) => { if (err) { error(path, err); reject(null); } else { resolve(ret); } }); }); } // 权重 public static weight(v: number[]): number { var mTotalWeight = 0; for (var i = 0; i < v.length; ++i) { mTotalWeight += v[i]; } if (mTotalWeight <= 0) return -1; var randnum = Math.round(Math.random() * Number.MAX_VALUE) % mTotalWeight; for (var i = 0; i < v.length; ++i) { if (randnum < v[i]) { return i; } else { randnum -= v[i]; } } return -1; } //定点数 public static fixFloat(val: number, count: number = 2) { var a = count * 100; return Math.floor(val * a) / a; } // 在子节点中查找 private static _findInChildren(node: Node, name: string): Node { var x = node.getChildByName(name); if (x) return x; if (node.children.length == 0) return null; for (var i = 0; i < node.children.length; ++i) { var tmp = this._findInChildren(node.children[i], name); if (tmp) return tmp; } return null; } // 飞动动画 public static flyAnim( type: number, startNode: Node, targetNodeName: string, count: number, radius: number, callback: Function ) { let srcNode = this._findInChildren(director.getScene(), targetNodeName); if (!srcNode) { notPlay = false; callback(); return; } let getPoint = (r, ox, oy, count) => { var point = []; //结果 var radians = (Math.PI / 180) * Math.round(360 / count), //弧度 i = 0; for (; i < count; i++) { var x = ox + r * Math.sin(radians * i), y = oy + r * Math.cos(radians * i); point.unshift(v2(x, y)); //为保持数据顺时针 } return point; }; let createNode = (type) => { if (type == 0) return PoolMgr.Instance().get("Coin"); if (type == 1) return PoolMgr.Instance().get("Gem"); if (type == 2) return PoolMgr.Instance().get("Zp"); if (type == 3) return PoolMgr.Instance().get("ZpGold"); }; let start = startNode.parent .getComponent(UITransform) .convertToWorldSpaceAR(startNode.position); start = find("Canvas/flyNode") .getComponent(UITransform) .convertToNodeSpaceAR(start); var array = getPoint(radius, start.x, start.y, count); var nodeArray = new Array(); for (var i = 0; i < array.length; i++) { var gold = createNode(type); gold.parent = find("Canvas/flyNode"); var randPos = v2( array[i].x + Utils.getRandomInt(0, 50), array[i].y + Utils.getRandomInt(0, 50) ); gold.setPosition(start); nodeArray.push({ gold, randPos }); } var notPlay = false; let dstPos = srcNode.parent .getComponent(UITransform) .convertToWorldSpaceAR(srcNode.position); dstPos = find("Canvas/flyNode") .getComponent(UITransform) .convertToNodeSpaceAR(dstPos); var targetGoldNode = srcNode; for (var i = 0; i < nodeArray.length; i++) { var pos = nodeArray[i].randPos; var node = nodeArray[i].gold; nodeArray[i].gold.id = i; tween(node) .to(0.2, { position: pos }) .delay(i * 0.03) .to(0.5, { position: v2(dstPos.x, dstPos.y) }) .call(() => { if (!notPlay) { targetGoldNode.scale = v3(1, 1, 1); notPlay = true; tween(targetGoldNode) .to(0.1, { scale: v3(2, 2, 2) }) .to(0.1, { scale: v3(1, 1, 1) }) .call(() => { notPlay = false; }); } callback(node._id == nodeArray.length - 1); PoolMgr.Instance().put(node.name, node); if (node._id == nodeArray.length - 1) { find("Canvas/flyNode").removeAllChildren(); } }) .start(); } } /** * 深度复制 * @param value */ public static deepClone(value: any) { // 处理基本数据类型和特殊情况 if (value === null || typeof value !== "object") { return value; // 基本数据类型和 null } // 处理数组 if (Array.isArray(value)) { return value.map((item) => Utils.deepClone(item)); // 递归拷贝每个元素 } // 处理对象 const copy = {}; for (const key in value) { if (value.hasOwnProperty(key)) { copy[key] = Utils.deepClone(value[key]); // 递归拷贝每个属性 } } return copy; } /** * 获取url参数 * @param name * @returns */ public static getQueryString(name: any) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); var r = window.location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; } /** * 贝塞尔曲线计算 * @param points * @param t */ public static calculateBezier(points: Vec2[], t: number) { const n = points.length; if (n === 0) return v2(0, 0); // 如果没有控制点,返回 (0,0) if (n === 1) return points[0]; // 如果只有一个控制点,返回该点 let tempPoints = points.slice(); // 创建控制点的副本 // De Casteljau's algorithm for (let r = 1; r < n; r++) { for (let i = 0; i < n - r; i++) { tempPoints[i] = v2( (1 - t) * tempPoints[i].x + t * tempPoints[i + 1].x, (1 - t) * tempPoints[i].y + t * tempPoints[i + 1].y ); } } return tempPoints[0]; // 返回计算得到的点 } /** * 是否为某个链接 * @param url * @param hostname * @returns */ static isNameLink(url: string | URL, hostname: string) { try { const parsedUrl = new URL(url); return parsedUrl.hostname === hostname; } catch (e) { console.error("Invalid URL"); return false; } } // 将用户 ID 转换为 26 进制字符串 public static convertTo26(userId) { let sb = ""; while (userId > 0) { userId--; let ch = String.fromCharCode((userId % 26) + "A".charCodeAt(0)); sb = ch + sb; userId = Math.floor(userId / 26); } return sb; } // 生成邀请代码 public static generateInviteCode(channelId, userId) { return channelId + this.convertTo26(userId); } // 取后缀 public static getSuffix(idx) { idx = Math.floor(idx); if (idx <= 0) { return ""; } idx = idx - 1; let result = ""; while (idx >= 0) { result = String.fromCharCode((idx % 26) + 97) + result; idx = Math.floor(idx / 26) - 1; } result = ""; return result; } // max: 10 ==> 1->1,2->2,..10->10,11->1 public static getMappedValue(idx, max) { let result; result = idx % max == 0 ? max : idx % max; return result; } // public static getStarValue(idx, max) { let result = Math.floor((idx - 1) / max); return result; } // 格式化地址 static formAddress(address: string) { if ( address == null || address == "" || address == undefined || address.length < 10 ) { return " "; } return ( address.substring(0, 8) + "..." + address.substring(address.length - 8, address.length) ); } // public static updateLabelText(fullText, maxWidth, fontSize) { let str = ""; // 计算文本的实际宽度 let textWidth = this.getTextWidth(fullText, fontSize); if (textWidth > maxWidth) { // 超过最大宽度,显示截断后的文本并加上省略号 let truncatedText = this.truncateText(fullText, maxWidth, fontSize); str = truncatedText + "..."; } else { // 显示完整文本 str = fullText; } return str; } // 获取文本的宽度 private static getTextWidth(text, fontSize) { let canvas = document.createElement("canvas"); let ctx = canvas.getContext("2d"); ctx.font = fontSize + "px Arial"; // 设置字体大小 return ctx.measureText(text).width; } // 截断文本并返回适合的文本 private static truncateText(text, maxWidth, fontSize) { let i = 0; let truncatedText = text; // 循环截取字符直到文本超出最大宽度 while ( this.getTextWidth(truncatedText, fontSize) > maxWidth && i < text.length ) { i++; truncatedText = text.slice(0, text.length - i); } return truncatedText; } // 多语言 static setI18nLabel(str: string, value?: string, value2?: string) { let i18nStr = str; i18nStr = LanguageManager.getText(str); if (value) { i18nStr = i18nStr.replace("{value}", value); } if (value2) { i18nStr = i18nStr.replace("{value2}", value2); } return i18nStr; } }