utils.ts 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210
  1. import { UITransform, Vec2, Vec3,Node,screen, _decorator, find, director, Color, UIOpacity, tween, Label } from "cc";
  2. const { ccclass, property } = _decorator;
  3. @ccclass("utils")
  4. export class utils {
  5. /**
  6. * 验证字符串是否是空的
  7. * @s 字符串
  8. */
  9. public static isNull(s: string) {
  10. if(s == ""
  11. || s == null
  12. || s == undefined){
  13. return true;
  14. }
  15. if (typeof s === 'string'){
  16. let re = new RegExp("^[ ]+$");
  17. return re.test(s);
  18. }else{
  19. return false;
  20. }
  21. }
  22. /**
  23. * 合并多个字典
  24. * @param args
  25. */
  26. public static merge(...args) {
  27. let mergeFn = <T, U> (arg1: T, arg2: U) : (T & U) =>{
  28. let res = {} as (T & U);
  29. res = Object.assign(arg1, arg2);
  30. return res;
  31. };
  32. let nDict = {};
  33. args.forEach(obj => {
  34. nDict = mergeFn(nDict,obj);
  35. });
  36. return nDict;
  37. }
  38. /**
  39. * 深度拷贝
  40. * @param {any} sObj 拷贝的对象
  41. * @returns
  42. */
  43. public static clone(sObj: any) {
  44. if (sObj === null || typeof sObj !== "object") {
  45. return sObj;
  46. }
  47. let s: { [key: string]: any } = {};
  48. if (sObj.constructor === Array) {
  49. s = [];
  50. }
  51. for (let i in sObj) {
  52. if (sObj.hasOwnProperty(i)) {
  53. s[i] = this.clone(sObj[i]);
  54. }
  55. }
  56. return s;
  57. }
  58. /**
  59. * 将object转化为数组
  60. * @param { any} srcObj
  61. * @returns
  62. */
  63. public static objectToArray(srcObj: { [key: string]: any }) {
  64. let resultArr: any[] = [];
  65. // to array
  66. for (let key in srcObj) {
  67. if (!srcObj.hasOwnProperty(key)) {
  68. continue;
  69. }
  70. resultArr.push(srcObj[key]);
  71. }
  72. return resultArr;
  73. }
  74. /**
  75. * !#zh 将数组转化为object。
  76. */
  77. /**
  78. * 将数组转化为object。
  79. * @param { any} srcObj
  80. * @param { string} objectKey
  81. * @returns
  82. */
  83. public static arrayToObject(srcObj: any, objectKey: string) {
  84. let resultObj: { [key: string]: any } = {};
  85. // to object
  86. for (var key in srcObj) {
  87. if (!srcObj.hasOwnProperty(key) || !srcObj[key][objectKey]) {
  88. continue;
  89. }
  90. resultObj[srcObj[key][objectKey]] = srcObj[key];
  91. }
  92. return resultObj;
  93. }
  94. /**
  95. * 根据权重,计算随机内容
  96. * @param {arrany} weightArr
  97. * @param {number} totalWeight 权重
  98. * @returns
  99. */
  100. public static getWeightRandIndex(weightArr: [], totalWeight: number) {
  101. let randWeight: number = Math.floor(Math.random() * totalWeight);
  102. let sum: number = 0;
  103. for (var weightIndex: number = 0; weightIndex < weightArr.length; weightIndex++) {
  104. sum += weightArr[weightIndex];
  105. if (randWeight < sum) {
  106. break;
  107. }
  108. }
  109. return weightIndex;
  110. }
  111. /**
  112. * 从n个数中获取m个随机数
  113. * @param {Number} n 总数
  114. * @param {Number} m 获取数
  115. * @returns {Array} array 获取数列
  116. */
  117. public static getRandomNFromM(n: number, m: number) {
  118. let array: any[] = [];
  119. let intRd: number = 0;
  120. let count: number = 0;
  121. while (count < m) {
  122. if (count >= n + 1) {
  123. break;
  124. }
  125. intRd = this.getRandomInt(0, n);
  126. var flag = 0;
  127. for (var i = 0; i < count; i++) {
  128. if (array[i] === intRd) {
  129. flag = 1;
  130. break;
  131. }
  132. }
  133. if (flag === 0) {
  134. array[count] = intRd;
  135. count++;
  136. }
  137. }
  138. return array;
  139. }
  140. /**
  141. * 获取随机整数
  142. * @param {Number} min 最小值
  143. * @param {Number} max 最大值
  144. * @returns
  145. */
  146. public static getRandomInt(min: number, max: number) {
  147. let r: number = Math.random();
  148. let rr: number = r * (max - min + 1) + min;
  149. return Math.floor(rr);
  150. }
  151. /**
  152. * 获取字符串长度
  153. * @param {string} render
  154. * @returns
  155. */
  156. public static getStringLength(render: string) {
  157. let strArr: string = render;
  158. let len: number = 0;
  159. for (let i: number = 0, n = strArr.length; i < n; i++) {
  160. let val: number = strArr.charCodeAt(i);
  161. if (val <= 255) {
  162. len = len + 1;
  163. } else {
  164. len = len + 2;
  165. }
  166. }
  167. return Math.ceil(len / 2);
  168. }
  169. /**
  170. * 要从一个数组模型中随机取出 n 个元素
  171. * @param arr
  172. * @param n
  173. * @returns 返回一个新的数组
  174. */
  175. public static getRandomElements<T>(arr: T[], n: number): T[] {
  176. if (n <= 0) return []; // 如果 n 小于等于 0,返回空数组
  177. //复制数组以避免修改原数组
  178. const copy = [...arr];
  179. //Fisher-Yates 洗牌算法
  180. for (let i = copy.length - 1; i > 0; i--) {
  181. const j = Math.floor(Math.random() * (i + 1));
  182. [copy[i], copy[j]] = [copy[j], copy[i]];
  183. }
  184. //如果 n 超过数组长度,返回乱序后的整个数组
  185. if (n >= copy.length) {
  186. return copy;
  187. }
  188. //返回前 n 个元素
  189. return copy.slice(0, n);
  190. }
  191. /**
  192. * 判断传入的参数是否为空的Object。数组或undefined会返回false
  193. * @param obj
  194. */
  195. public static isEmptyObject(obj: any) {
  196. let result: boolean = true;
  197. if (obj && obj.constructor === Object) {
  198. for (var key in obj) {
  199. if (obj.hasOwnProperty(key)) {
  200. result = false;
  201. break;
  202. }
  203. }
  204. } else {
  205. result = false;
  206. }
  207. return result;
  208. }
  209. /**
  210. * 判断是否是新的一天
  211. * @param {Object|Number} dateValue 时间对象 todo MessageCenter 与 pve 相关的时间存储建议改为 Date 类型
  212. * @returns {boolean}
  213. */
  214. public static isNewDay(dateValue: any) {
  215. // todo:是否需要判断时区?
  216. var oldDate: any = new Date(dateValue);
  217. var curDate: any = new Date();
  218. var oldYear = oldDate.getYear();
  219. var oldMonth = oldDate.getMonth();
  220. var oldDay = oldDate.getDate();
  221. var curYear = curDate.getYear();
  222. var curMonth = curDate.getMonth();
  223. var curDay = curDate.getDate();
  224. if (curYear > oldYear) {
  225. return true;
  226. } else {
  227. if (curMonth > oldMonth) {
  228. return true;
  229. } else {
  230. if (curDay > oldDay) {
  231. return true;
  232. }
  233. }
  234. }
  235. return false;
  236. }
  237. /**
  238. * 获取对象属性数量
  239. * @param {object}o 对象
  240. * @returns
  241. */
  242. public static getPropertyCount(o: Object) {
  243. var n, count = 0;
  244. for (n in o) {
  245. if (o.hasOwnProperty(n)) {
  246. count++;
  247. }
  248. }
  249. return count;
  250. }
  251. /**
  252. * 返回一个差异化数组(将array中diff里的值去掉)
  253. * @param array
  254. * @param diff
  255. */
  256. public static difference(array: [], diff: any) {
  257. let result: any[] = [];
  258. if (array.constructor !== Array || diff.constructor !== Array) {
  259. return result;
  260. }
  261. let length = array.length;
  262. for (let i: number = 0; i < length; i++) {
  263. if (diff.indexOf(array[i]) === -1) {
  264. result.push(array[i]);
  265. }
  266. }
  267. return result;
  268. }
  269. public static _stringToArray(string: string) {
  270. // 用于判断emoji的正则们
  271. var rsAstralRange = '\\ud800-\\udfff';
  272. var rsZWJ = '\\u200d';
  273. var rsVarRange = '\\ufe0e\\ufe0f';
  274. var rsComboMarksRange = '\\u0300-\\u036f';
  275. var reComboHalfMarksRange = '\\ufe20-\\ufe2f';
  276. var rsComboSymbolsRange = '\\u20d0-\\u20ff';
  277. var rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange;
  278. var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');
  279. var rsFitz = '\\ud83c[\\udffb-\\udfff]';
  280. var rsOptVar = '[' + rsVarRange + ']?';
  281. var rsCombo = '[' + rsComboRange + ']';
  282. var rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')';
  283. var reOptMod = rsModifier + '?';
  284. var rsAstral = '[' + rsAstralRange + ']';
  285. var rsNonAstral = '[^' + rsAstralRange + ']';
  286. var rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}';
  287. var rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]';
  288. var rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*';
  289. var rsSeq = rsOptVar + reOptMod + rsOptJoin;
  290. var rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
  291. var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
  292. var hasUnicode = function (val: any) {
  293. return reHasUnicode.test(val);
  294. };
  295. var unicodeToArray = function (val: any) {
  296. return val.match(reUnicode) || [];
  297. };
  298. var asciiToArray = function (val: any) {
  299. return val.split('');
  300. };
  301. return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string);
  302. }
  303. // 模拟传msg的uuid
  304. public static simulationUUID() {
  305. function s4() {
  306. return Math.floor((1 + Math.random()) * 0x10000)
  307. .toString(16)
  308. .substring(1);
  309. }
  310. return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
  311. s4() + '-' + s4() + s4() + s4();
  312. }
  313. public static trim(str: string) {
  314. return str.replace(/(^\s*)|(\s*$)/g, "");
  315. }
  316. /**
  317. * 判断当前时间是否在有效时间内
  318. * @param {String|Number} start 起始时间。带有时区信息
  319. * @param {String|Number} end 结束时间。带有时区信息
  320. */
  321. public static isNowValid(start: any, end: any) {
  322. var startTime = new Date(start);
  323. var endTime = new Date(end);
  324. var result = false;
  325. if (startTime.getDate() + '' !== 'NaN' && endTime.getDate() + '' !== 'NaN') {
  326. var curDate = new Date();
  327. result = curDate < endTime && curDate > startTime;
  328. }
  329. return result;
  330. }
  331. /**
  332. * 返回相隔天数
  333. * @param start
  334. * @param end
  335. * @returns
  336. */
  337. public static getDeltaDays(start: any, end: any) {
  338. start = new Date(start);
  339. end = new Date(end);
  340. let startYear: number = start.getFullYear();
  341. let startMonth: number = start.getMonth() + 1;
  342. let startDate: number = start.getDate();
  343. let endYear: number = end.getFullYear();
  344. let endMonth: number = end.getMonth() + 1;
  345. let endDate: number = end.getDate();
  346. start = new Date(startYear + '/' + startMonth + '/' + startDate + ' GMT+0800').getTime();
  347. end = new Date(endYear + '/' + endMonth + '/' + endDate + ' GMT+0800').getTime();
  348. let deltaTime = end - start;
  349. return Math.floor(deltaTime / (24 * 60 * 60 * 1000));
  350. }
  351. /**
  352. * 获取数组最小值
  353. * @param array 数组
  354. * @returns
  355. */
  356. public static getMin(array: number[]) {
  357. let result: number = null!;
  358. if (array.constructor === Array) {
  359. let length = array.length;
  360. for (let i = 0; i < length; i++) {
  361. if (i === 0) {
  362. result = Number(array[0]);
  363. } else {
  364. result = result > Number(array[i]) ? Number(array[i]) : result;
  365. }
  366. }
  367. }
  368. return result;
  369. }
  370. /**
  371. * 格式化两位小数点
  372. * @param time
  373. * @returns
  374. */
  375. public static formatTwoDigits(time: number) {
  376. //@ts-ignore
  377. return (Array(2).join(0) + time).slice(-2);
  378. }
  379. /**
  380. * 根据格式返回时间
  381. * @param date 时间
  382. * @param fmt 格式
  383. * @returns
  384. */
  385. public static formatDate(date: Date, fmt: string) {
  386. let o: any = {
  387. "M+": date.getMonth() + 1, //月份
  388. "d+": date.getDate(), //日
  389. "h+": date.getHours(), //小时
  390. "m+": date.getMinutes(), //分
  391. "s+": date.getSeconds(), //秒
  392. "q+": Math.floor((date.getMonth() + 3) / 3), //季度
  393. "S": date.getMilliseconds() //毫秒
  394. };
  395. if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
  396. for (let k in o)
  397. if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
  398. return fmt;
  399. }
  400. /**
  401. * 获取格式化后的日期(不含小时分秒)
  402. */
  403. public static getDay() {
  404. let date: Date = new Date();
  405. return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
  406. }
  407. /**
  408. * 格式化名字,XXX...
  409. * @param {string} name 需要格式化的字符串
  410. * @param {number}limit
  411. * @returns {string} 返回格式化后的字符串XXX...
  412. */
  413. public static formatName(name: string, limit: number) {
  414. limit = limit || 6;
  415. var nameArray = this._stringToArray(name);
  416. var str = '';
  417. var length = nameArray.length;
  418. if (length > limit) {
  419. for (var i = 0; i < limit; i++) {
  420. str += nameArray[i];
  421. }
  422. str += '...';
  423. } else {
  424. str = name;
  425. }
  426. return str;
  427. }
  428. /**
  429. * 格式化钱数,超过10000 转换位 10K 10000K 转换为 10M
  430. * @param {number}money 需要被格式化的数值
  431. * @returns {string}返回 被格式化的数值
  432. */
  433. public static formatMoney(money: number) {
  434. let arrUnit: string[] = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'B', 'N', 'D'];
  435. let strValue: string = '';
  436. for (let idx: number = 0; idx < arrUnit.length; idx++) {
  437. if (money >= 10000) {
  438. money /= 1000;
  439. } else {
  440. strValue = Math.floor(money) + arrUnit[idx];
  441. break;
  442. }
  443. }
  444. if (strValue === '') {
  445. strValue = Math.floor(money) + 'U'; //超过最大值就加个U
  446. }
  447. return strValue;
  448. }
  449. /**
  450. * 格式化数值
  451. * @param {number}value 需要被格式化的数值
  452. * @returns {string}返回 被格式化的数值
  453. */
  454. public static formatValue(value: number) {
  455. let arrUnit: string[] = [];
  456. let strValue: string = '';
  457. for (let i = 0; i < 26; i++) {
  458. arrUnit.push(String.fromCharCode(97 + i));
  459. }
  460. for (let idx: number = 0; idx < arrUnit.length; idx++) {
  461. if (value >= 10000) {
  462. value /= 1000;
  463. } else {
  464. strValue = Math.floor(value) + arrUnit[idx];
  465. break;
  466. }
  467. }
  468. return strValue;
  469. }
  470. /**
  471. * 根据剩余秒数格式化剩余时间 返回 HH:MM:SS
  472. * @param {Number} leftSec
  473. */
  474. public static formatTimeForSecond(leftSec: number, withoutSeconds: boolean = false) {
  475. let timeStr: string = '';
  476. let sec: number = leftSec % 60;
  477. let leftMin: number = Math.floor(leftSec / 60);
  478. leftMin = leftMin < 0 ? 0 : leftMin;
  479. let hour: number = Math.floor(leftMin / 60);
  480. let min: number = leftMin % 60;
  481. if (hour > 0) {
  482. timeStr += hour > 9 ? hour.toString() : '0' + hour;
  483. timeStr += ':';
  484. } else {
  485. timeStr += '00:';
  486. }
  487. timeStr += min > 9 ? min.toString() : '0' + min;
  488. if (!withoutSeconds) {
  489. timeStr += ':';
  490. timeStr += sec > 9 ? sec.toString() : '0' + sec;
  491. }
  492. return timeStr;
  493. }
  494. /**
  495. * 根据剩余毫秒数格式化剩余时间 返回 HH:MM:SS
  496. *
  497. * @param {Number} ms
  498. */
  499. public static formatTimeForMillisecond(ms: number): Object {
  500. let second: number = Math.floor(ms / 1000 % 60);
  501. let minute: number = Math.floor(ms / 1000 / 60 % 60);
  502. let hour: number = Math.floor(ms / 1000 / 60 / 60);
  503. return { 'hour': hour, 'minute': minute, 'second': second };
  504. }
  505. /**
  506. * 格式化时间戳字符串
  507. * @param timestamp 1740006560000
  508. * @returns 输出2025-02-20 05:09:20
  509. */
  510. public static formatTimestamp(timestamp: number): string {
  511. const date = new Date(timestamp);
  512. //获取年月日时分秒
  513. const year = date.getFullYear();
  514. //月份从 0 开始,需要 +1
  515. const month = String(date.getMonth() + 1).padStart(2, '0');
  516. const day = String(date.getDate()).padStart(2, '0');
  517. const hours = String(date.getHours()).padStart(2, '0');
  518. const minutes = String(date.getMinutes()).padStart(2, '0');
  519. const seconds = String(date.getSeconds()).padStart(2, '0');
  520. //拼接成 YYYY-MM-DD HH:MM:SS 格式
  521. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  522. }
  523. /**
  524. * 随机乱序数组
  525. * @param array
  526. * @returns
  527. */
  528. public static randomArray(array) {
  529. // 使用 Fisher-Yates Shuffle 算法
  530. for (let i = array.length - 1; i > 0; i--) {
  531. const j = Math.floor(Math.random() * (i + 1));
  532. // 交换元素
  533. [array[i], array[j]] = [array[j], array[i]];
  534. }
  535. return array;
  536. }
  537. /**
  538. * 获得开始和结束两者之间相隔分钟数
  539. *
  540. * @static
  541. * @param {number} start
  542. * @param {number} end
  543. * @memberof utils
  544. */
  545. public static getOffsetMimutes(start: number, end: number) {
  546. let offSetTime: number = end - start;
  547. let minute: number = Math.floor((offSetTime % (1000 * 60 * 60)) / (1000 * 60));
  548. return minute;
  549. }
  550. /**
  551. * 获取随机小数
  552. * @param {Number} min 最小值
  553. * @param {Number} max 最大值
  554. * @returns
  555. */
  556. public static getRandomFloat(min: number, max: number) {
  557. return Math.random() * (max - min) + min;
  558. }
  559. /**
  560. * 返回指定小数位的数值
  561. * @param {number} num
  562. * @param {number} idx
  563. */
  564. public static formatNumToFixed(num: number, idx: number = 0) {
  565. return Number(num.toFixed(idx));
  566. }
  567. /**
  568. * 用于数值到达另外一个目标数值之间进行平滑过渡运动效果
  569. * @param {number} targetValue 目标数值
  570. * @param {number} curValue 当前数值
  571. * @param {number} ratio 过渡比率
  572. * @returns
  573. */
  574. public static lerp(targetValue: number, curValue: number, ratio: number = 0.25) {
  575. let v: number = curValue;
  576. if (targetValue > curValue) {
  577. v = curValue + (targetValue - curValue) * ratio;
  578. } else if (targetValue < curValue) {
  579. v = curValue - (curValue - targetValue) * ratio;
  580. }
  581. return v;
  582. }
  583. /**
  584. * 数据解密
  585. * @param {String} str
  586. */
  587. public static decrypt(b64Data: string) {
  588. if(b64Data == null || b64Data == undefined){
  589. return "";
  590. }
  591. let n: number = 6;
  592. if (b64Data.length % 2 === 0) {
  593. n = 7;
  594. }
  595. let decodeData = '';
  596. for (var idx = 0; idx < b64Data.length - n; idx += 2) {
  597. decodeData += b64Data[idx + 1];
  598. decodeData += b64Data[idx];
  599. }
  600. decodeData += b64Data.slice(b64Data.length - n + 1);
  601. decodeData = this._base64Decode(decodeData);
  602. return decodeData;
  603. }
  604. /**
  605. * 数据加密
  606. * @param {String} str
  607. */
  608. public static encrypt(str: string) {
  609. if(str == null || str == undefined){
  610. return "";
  611. }
  612. let b64Data = this._base64encode(str);
  613. let n: number = 6;
  614. if (b64Data.length % 2 === 0) {
  615. n = 7;
  616. }
  617. let encodeData: string = '';
  618. for (let idx = 0; idx < (b64Data.length - n + 1) / 2; idx++) {
  619. encodeData += b64Data[2 * idx + 1];
  620. encodeData += b64Data[2 * idx];
  621. }
  622. encodeData += b64Data.slice(b64Data.length - n + 1);
  623. return encodeData;
  624. }
  625. //public method for encoding
  626. /**
  627. * base64加密
  628. * @param {string}input
  629. * @returns
  630. */
  631. private static _base64encode(input: string) {
  632. let keyStr: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  633. let output: string = "", chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number, enc4: number, i: number = 0;
  634. input = this._utf8Encode(input);
  635. while (i < input.length) {
  636. chr1 = input.charCodeAt(i++);
  637. chr2 = input.charCodeAt(i++);
  638. chr3 = input.charCodeAt(i++);
  639. enc1 = chr1 >> 2;
  640. enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
  641. enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
  642. enc4 = chr3 & 63;
  643. if (isNaN(chr2)) {
  644. enc3 = enc4 = 64;
  645. } else if (isNaN(chr3)) {
  646. enc4 = 64;
  647. }
  648. output = output +
  649. keyStr.charAt(enc1) + keyStr.charAt(enc2) +
  650. keyStr.charAt(enc3) + keyStr.charAt(enc4);
  651. }
  652. return output;
  653. }
  654. /**
  655. * utf-8 加密
  656. * @param string
  657. * @returns
  658. */
  659. private static _utf8Encode(string: string) {
  660. string = string.replace(/\r\n/g, "\n");
  661. let utftext: string = "";
  662. for (let n: number = 0; n < string.length; n++) {
  663. let c: number = string.charCodeAt(n);
  664. if (c < 128) {
  665. utftext += String.fromCharCode(c);
  666. } else if ((c > 127) && (c < 2048)) {
  667. utftext += String.fromCharCode((c >> 6) | 192);
  668. utftext += String.fromCharCode((c & 63) | 128);
  669. } else {
  670. utftext += String.fromCharCode((c >> 12) | 224);
  671. utftext += String.fromCharCode(((c >> 6) & 63) | 128);
  672. utftext += String.fromCharCode((c & 63) | 128);
  673. }
  674. }
  675. return utftext;
  676. }
  677. /**
  678. * utf-8解密
  679. * @param utftext
  680. * @returns
  681. */
  682. private static _utf8Decode(utftext: string) {
  683. let string = "";
  684. let i: number = 0;
  685. let c: number = 0;
  686. let c1: number = 0;
  687. let c2: number = 0;
  688. let c3: number = 0;
  689. while (i < utftext.length) {
  690. c = utftext.charCodeAt(i);
  691. if (c < 128) {
  692. string += String.fromCharCode(c);
  693. i++;
  694. } else if ((c > 191) && (c < 224)) {
  695. c2 = utftext.charCodeAt(i + 1);
  696. string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  697. i += 2;
  698. } else {
  699. c2 = utftext.charCodeAt(i + 1);
  700. c3 = utftext.charCodeAt(i + 2);
  701. string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  702. i += 3;
  703. }
  704. }
  705. return string;
  706. }
  707. /**
  708. * base64解密
  709. * @param {string}input 解密字符串
  710. * @returns
  711. */
  712. private static _base64Decode(input: string) {
  713. let keyStr: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  714. let output: string = "";
  715. let chr1: number;
  716. let chr2: number;
  717. let chr3: number;
  718. let enc1: number;
  719. let enc2: number;
  720. let enc3: number;
  721. let enc4: number;
  722. let i: number = 0;
  723. input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  724. while (i < input.length) {
  725. enc1 = keyStr.indexOf(input.charAt(i++));
  726. enc2 = keyStr.indexOf(input.charAt(i++));
  727. enc3 = keyStr.indexOf(input.charAt(i++));
  728. enc4 = keyStr.indexOf(input.charAt(i++));
  729. chr1 = (enc1 << 2) | (enc2 >> 4);
  730. chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
  731. chr3 = ((enc3 & 3) << 6) | enc4;
  732. output = output + String.fromCharCode(chr1);
  733. if (enc3 != 64) {
  734. output = output + String.fromCharCode(chr2);
  735. }
  736. if (enc4 != 64) {
  737. output = output + String.fromCharCode(chr3);
  738. }
  739. }
  740. output = this._utf8Decode(output);
  741. return output;
  742. }
  743. /**
  744. * 开始展示文字
  745. * @param lable 文本
  746. * @param words 播放的文字
  747. * @param cb 播放完成回调
  748. * @param cbbS 回调延迟
  749. * @param delay 延迟逐字播放
  750. * @param s 一个字的播放速度/秒
  751. */
  752. public static verbatim(lable: Label,words: string, cb?: Function,cbbS:number = 0,delay: number = 0,s: number = 0.1,){
  753. if (!words.hasOwnProperty('length')) {
  754. return;
  755. }
  756. lable.node.active = true;
  757. lable.unscheduleAllCallbacks();
  758. let f = function(){
  759. let arr = words.replace(/ /g,"").split('');
  760. var step = 0;
  761. var allWords: string = "";
  762. let fun: Function = function () {
  763. allWords += arr[step];
  764. lable.string = allWords;
  765. if (++step >= arr.length) {
  766. lable.unschedule(fun);
  767. let cbFun = ()=>{cb?.()};
  768. cbbS > 0 ? (lable.scheduleOnce(cbFun, cbbS)) : cbFun();
  769. }
  770. }.bind(this);
  771. lable.schedule(fun,s,Number.MAX_SAFE_INTEGER);
  772. };
  773. delay > 0 ? (lable.scheduleOnce(f.bind(this),delay)) : f();
  774. }
  775. /**
  776. * 页面渐隐渐显动画
  777. * @param n 节点
  778. * @param isApper 是否是出现
  779. * @param restore 是否恢复255显示的状态
  780. * @param cb 执行完回调
  781. */
  782. public static pageAnim(n: Node,isApper: boolean = true,cb?:Function){
  783. let uiop:UIOpacity = n.getComponent(UIOpacity);
  784. if(!n || !uiop){
  785. cb?.();
  786. }else{
  787. uiop.opacity = isApper ? 25 : 255;
  788. let toOpacity: number = isApper ? 255 : 25;
  789. tween(uiop)
  790. .to(0.4,{opacity:toOpacity})
  791. .call(function(){
  792. cb?.();
  793. }.bind(this))
  794. .start();
  795. }
  796. }
  797. /**
  798. * 将数组(array)拆分成多个 size 长度的区块,并将这些区块组成一个新数组
  799. * @param {Array}array
  800. * @param {number}size
  801. * @returns
  802. */
  803. public static chunk(array: any[], size: number) {
  804. var length = array === null ? 0 : array.length;
  805. if (!length || size < 1) {
  806. return [];
  807. }
  808. let result = [];
  809. while (array.length > size) {
  810. result.push(array.slice(0, size));
  811. array = array.slice(size);
  812. }
  813. result.push(array);
  814. return result;
  815. }
  816. /**
  817. * 查询一个节点下的子节点
  818. */
  819. public static findName(root: Node, name: string) : Node{
  820. let child: Node;
  821. if (name.indexOf("/") != -1) {
  822. child = find(name, root);
  823. } else {
  824. if (!root) {
  825. root = director.getScene();
  826. }
  827. if (root.name === name) {
  828. child = root;
  829. } else {
  830. child = this.findChild(name, root);
  831. }
  832. }
  833. if (child) {
  834. return child;
  835. } else {
  836. console.log("没有找到指定的Node, node name ==", name);
  837. return null;
  838. }
  839. }
  840. private static findChild(name: string, parent: Node): Node {
  841. let child: Node = parent.getChildByName(name);
  842. if (child) {
  843. return child;
  844. } else {
  845. let children: Node[] = parent.children;
  846. for (let i = 0; i < children.length; i++) {
  847. child = this.findChild(name, children[i]);
  848. if (child) {
  849. return child;
  850. }
  851. }
  852. }
  853. return null;
  854. }
  855. /**
  856. * 处理万为单位
  857. * @param num 数值
  858. * @param point 保留小数点
  859. * @param s 是否去掉无用的0
  860. * @returns
  861. */
  862. public static numUnit(num: number,point: number = 0,f: boolean = true): string {
  863. let n: number = num;
  864. let unit: number = 10000;
  865. if (n > unit) {
  866. if(point == 0){
  867. n = Math.ceil(n / unit);
  868. return n.toString() + "万";
  869. }else{
  870. let s: string = (n / unit).toFixed(point);
  871. if(f){
  872. return this.removeZeros(s) + "万";
  873. }else{
  874. return s + "万";
  875. }
  876. }
  877. }else{
  878. return Math.ceil(num).toString();
  879. }
  880. }
  881. /**
  882. * 格式化数字:
  883. * * 如果小数部分全是 0(如 38.0 或 38.00),去掉小数部分,返回整数。
  884. * 如果是小数(如 38.1 或 38.01),保留两位小数。
  885. * @param num 输入的数字
  886. * @returns 格式化后的数字
  887. */
  888. public static formatNumber(num: number): number | string {
  889. // 如果 num 是 undefined 或 null,返回 0 或空字符串
  890. if (num == undefined || num == null) return 0;
  891. //将数字转换为字符串
  892. const numStr = num.toString();
  893. //判断小数部分是否全是 0 如果小数部分全是 0,去掉小数部分并返回整数
  894. if (numStr.includes('.') && /\.0+$/.test(numStr)) {
  895. return parseInt(numStr, 10);
  896. } else {
  897. //否则保留两位小数
  898. const fixedNum = num.toFixed(2);
  899. //如果小数部分全是 0,去掉小数部分
  900. if(fixedNum.endsWith(".00")) {
  901. return parseInt(fixedNum, 10);
  902. }
  903. return parseFloat(fixedNum);
  904. }
  905. }
  906. /**
  907. * 去掉小数点后无用的0
  908. * @param numberString 字符串呢
  909. * @returns
  910. */
  911. public static removeZeros(numberString: string): string {
  912. const trimmedString = numberString.trim(); // 去除首尾空格
  913. const decimalIndex = trimmedString.indexOf('.');
  914. if (decimalIndex !== -1) {
  915. let endIndex = trimmedString.length - 1;
  916. while (trimmedString[endIndex] === '0') {
  917. endIndex--;
  918. }
  919. if (trimmedString[endIndex] === '.') {
  920. endIndex--; // 如果小数点后面全是零,也去掉小数点
  921. }
  922. return trimmedString.slice(0, endIndex + 1);
  923. }
  924. return trimmedString;
  925. }
  926. /**
  927. * 数组移除某一个元素
  928. */
  929. public static remove(arr,p){
  930. let index = arr.indexOf(p)
  931. if (index > -1) {
  932. arr.splice(index, 1)
  933. }
  934. }
  935. /**
  936. * 16进制的颜色
  937. * @param hexColor
  938. * @returns
  939. */
  940. public static hexColor(hexColor) {
  941. const hex = hexColor.replace(/^#?/, "0x");
  942. const c = parseInt(hex);
  943. const r = c >> 16;
  944. const g = (65280 & c) >> 8;
  945. const b = 255 & c;
  946. return new Color(r, g, b, 255);
  947. };
  948. /**
  949. * 计算两点间的距离
  950. */
  951. public static pDistance(localPos: Vec3,tarPos: Vec3): number {
  952. let dx = localPos.x - tarPos.x;
  953. let dy = localPos.y - tarPos.y;
  954. let dis = Math.sqrt(dx * dx + dy * dy);
  955. return dis;
  956. }
  957. /**
  958. * 计算两点之间的绝对距离
  959. */
  960. public static pAbsDistance(a: Vec3,b: Vec3): number {
  961. let p: number = Math.abs(a.x - b.x);
  962. let k: number = Math.abs(a.y - b.y);
  963. return p + k;
  964. }
  965. /**
  966. * 角度转向量
  967. * @param angle
  968. * @returns
  969. */
  970. public static angle_to_vector (angle: number): Vec2 {
  971. // tan = sin / cos 将传入的角度转为弧度
  972. let radian = this.angle_to_radian(angle);
  973. // 算出cos,sin和tan
  974. let cos = Math.cos(radian);// 邻边 / 斜边
  975. let sin = Math.sin(radian);// 对边 / 斜边
  976. let tan = sin / cos;// 对边 / 邻边
  977. //结合在一起并归一化
  978. let vec = new Vec2(cos, sin).normalize();
  979. //返回向量
  980. return(vec);
  981. }
  982. /**
  983. * 向量转角度
  984. * @param vector
  985. * @returns
  986. */
  987. public static vector_to_angle (vector: Vec2): number {
  988. //将传入的向量归一化
  989. let dir = vector.normalize();
  990. //计算出目标角度的弧度
  991. let radian = dir.signAngle(new Vec2(1, 0));
  992. //把弧度计算成角度
  993. let angle = -this.radian_to_angle(radian);
  994. //返回角度
  995. return(angle);
  996. }
  997. /**
  998. * 角度转弧度
  999. * @param angle
  1000. * @returns
  1001. */
  1002. public static angle_to_radian (angle: number): number {
  1003. //角度转弧度公式 π / 180 * 角度 计算出弧度
  1004. let radian = Math.PI / 180 * angle;
  1005. //返回弧度
  1006. return(radian);
  1007. }
  1008. /**
  1009. * 弧度转角度
  1010. * @param radian
  1011. * @returns
  1012. */
  1013. public static radian_to_angle (radian: number): number {
  1014. //弧度转角度公式 180 / π * 弧度 计算出角度
  1015. let angle = 180 / Math.PI * radian;
  1016. //返回角度
  1017. return(angle);
  1018. }
  1019. /**
  1020. * 计算弧度
  1021. * @param start
  1022. * @param end
  1023. */
  1024. public static getAngle(start: Vec3, end: Vec3) {
  1025. //两点的x、y值
  1026. var x = end.x - start.x;
  1027. var y = end.y - start.y;
  1028. var hypotenuse = Math.sqrt(x * x + y * y);
  1029. //斜边长度
  1030. var cos = x / hypotenuse;
  1031. var radian = Math.acos(cos);
  1032. //求出弧度
  1033. var angle = 180 / (Math.PI / radian);
  1034. //用弧度算出角度
  1035. if (y < 0) {
  1036. angle = 0 - angle;
  1037. }else if (y == 0 && x < 0) {
  1038. angle = 180;
  1039. }
  1040. return angle;
  1041. }
  1042. /**
  1043. * 扣血转化成字符串
  1044. * @param number
  1045. * @returns
  1046. */
  1047. public static numberToString(number:number) {
  1048. let str = '';
  1049. let data = [
  1050. { minnum: 1000000000,maxnum:9999999999, toStr: 'o' },
  1051. { minnum: 100000000,maxnum:999999999, toStr: 'p' },
  1052. { minnum: 10000000,maxnum:99999999, toStr: 't' },
  1053. { minnum: 1000000,maxnum:9999999, toStr: 'g' },
  1054. { minnum: 100000,maxnum:999999, toStr: 'm' },
  1055. { minnum: 10000,maxnum:99999, toStr: 'b' },
  1056. { minnum: 1000,maxnum:9999, toStr: 'K' }
  1057. ];
  1058. for (let i: number = 0; i < data.length; i++) {
  1059. if (number >= data[i].minnum && number <= data[i].maxnum ) {
  1060. number /= data[i].minnum;
  1061. let m: string = number.toFixed(1);
  1062. str = m + data[i].toStr;
  1063. return str;
  1064. }
  1065. }
  1066. return number.toFixed(0);
  1067. }
  1068. /**
  1069. * 将某个节点下的坐标转移到另外一个节点
  1070. * @param fromNode 坐标所在的节点
  1071. * @param toNode 目标节点
  1072. * @returns 转换后的坐标值
  1073. */
  1074. public static convertPosition(fromNode: Node, toNode: Node): Vec3 {
  1075. let pos: Vec3 = fromNode.position.clone();
  1076. // 将 pos 转为世界坐标系下的坐标
  1077. const worldPos: Vec3 = fromNode.parent.getComponent(UITransform).convertToWorldSpaceAR(pos);
  1078. // 将世界坐标系下的坐标转为目标节点的局部坐标系下的坐标
  1079. const localPos: Vec3 = toNode.getComponent(UITransform).convertToNodeSpaceAR(worldPos);
  1080. return localPos;
  1081. }
  1082. /**
  1083. * 以敌人的中心点 攻击范围为半径 产生随机坐标
  1084. * @param center 中心点
  1085. * @param radius 半径
  1086. * @returns
  1087. */
  1088. public static randomPointGenerator(center: Vec3,radius: number){
  1089. //随机角度
  1090. let angle = Math.random() * Math.PI * 2;
  1091. //随机距离
  1092. let distance = Math.sqrt(Math.random()) * radius;
  1093. //根据极坐标转换成笛卡尔坐标
  1094. const x = center.x + distance * Math.cos(angle);
  1095. const y = center.y + distance * Math.sin(angle);
  1096. return new Vec3(x,y,1);
  1097. }
  1098. /**
  1099. * 将某个节点上的坐标转移到另外一个节点
  1100. * @param fromNode 坐标所在的节点
  1101. * @param toNode 目标节点
  1102. * @returns 转换后的坐标值
  1103. */
  1104. public static convertPositionPos(fromNode: Node,pos: Vec3, toNode: Node): Vec3 {
  1105. let nPos: Vec3 = pos.clone();
  1106. // 将 pos 转为世界坐标系下的坐标
  1107. const worldPos: Vec3 = fromNode.parent.getComponent(UITransform).convertToWorldSpaceAR(nPos);
  1108. // 将世界坐标系下的坐标转为目标节点的局部坐标系下的坐标
  1109. const localPos: Vec3 = toNode.getComponent(UITransform).convertToNodeSpaceAR(worldPos);
  1110. return localPos;
  1111. }
  1112. /**
  1113. * 根据A,B两个坐标点 和抛物线的弧度 来计算中心点坐标
  1114. */
  1115. public static calculateParabolaCenter(start: Vec3, end: Vec3){
  1116. // 计算两点之间的水平距离
  1117. const deltaX = end.x - start.x;
  1118. // 将控制点的 x 坐标设置为两点的中点
  1119. const controlX = (start.x + end.x) / 2;
  1120. // 计算抛物线的最高点,使其位于两点之间的中间位置 可以根据需要调整最高点的位置
  1121. const highestY = Math.max(start.y, end.y) + Math.abs(deltaX) / 4;
  1122. // 计算控制点的 y 坐标
  1123. const controlY = highestY;
  1124. //返回抛物线的中心坐标点
  1125. return new Vec3(controlX, controlY);
  1126. }
  1127. /**
  1128. * 生成一个随机颜色值的函数
  1129. */
  1130. public static getRandomColor() {
  1131. let r = Math.floor(Math.random() * 256);
  1132. let g = Math.floor(Math.random() * 256);
  1133. let b = Math.floor(Math.random() * 256);
  1134. return new Color(r, g, b);
  1135. }
  1136. /**
  1137. * 获取是否为16:9的常规屏幕尺寸
  1138. */
  1139. public static isNormalScreen(): boolean {
  1140. return screen.windowSize.height /screen.windowSize.width < 1335 / 750;
  1141. }
  1142. }