ch_util.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import { size, view, Node, UITransform, screen, ImageAsset, SpriteFrame, Texture2D, sys, assetManager } from "cc";
  2. /**工具*/
  3. class ch_util {
  4. private static _instance: ch_util;
  5. public static getInstance(): ch_util {
  6. if (!this._instance) this._instance = new ch_util();
  7. return this._instance;
  8. }
  9. /**
  10. * 随机数 (包含min,不包含max)
  11. * @param min
  12. * @param max
  13. * @param isInt
  14. * @return {*}
  15. */
  16. public getRandom(min: number = 0, max: number = 1): number {
  17. if (min == null) min = 0;
  18. if (max == null) max = 1;
  19. if (min === max) return min;
  20. return min + (Math.random() * (max - min));
  21. }
  22. /**
  23. * 随机整数-不包含最大值
  24. * @param min
  25. * @param max
  26. * @return {*}
  27. */
  28. public getRandomInt(min: number, max: number): number {
  29. min = Math.ceil(min); max = Math.ceil(max);
  30. return Math.floor(Math.random() * (max - min)) + min;
  31. }
  32. /** 生成随机整数 -1 或 1*/
  33. public getRandomDir(): -1 | 1 {
  34. return Math.floor(Math.random() * 2) === 0 ? -1 : 1;
  35. }
  36. /**
  37. * 在指定数组中随机取出N个不重复的数据
  38. * @param resArr
  39. * @param ranNum
  40. * @returns {Array}
  41. */
  42. public getRandomDiffValueFromArr<T>(resArr: Array<T>, ranNum: number): Array<T> {
  43. let arr = new Array<T>();
  44. let result = new Array<T>();
  45. if (!resArr || resArr.length <= 0 || ranNum <= 0) {
  46. return result;
  47. }
  48. for (let i = 0; i < resArr.length; i++) {
  49. arr.push(resArr[i]);
  50. }
  51. if (ranNum >= arr.length) return arr;
  52. ranNum = Math.min(ranNum, arr.length - 1);
  53. for (let i = 0; i < ranNum; i++) {
  54. let ran = this.getRandomInt(0, arr.length - 1);
  55. result.push(arr.splice(ran, 1)[0]);
  56. }
  57. return result;
  58. }
  59. /**
  60. * 取小数位
  61. * @param decimal 小数
  62. * @param places 位数
  63. * @return {number}
  64. */
  65. public numberToDecimal(decimal: number, places: number): number {
  66. let round: number = Math.pow(10, places);
  67. return Math.round(decimal * round) / round;
  68. }
  69. public parse(text: string, reciver?: (key: any, value: any) => any): any {
  70. try {
  71. return JSON.parse(text, reciver);
  72. } catch (error) {
  73. //ch_log.error(error);
  74. return null;
  75. }
  76. }
  77. public stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number) {
  78. try {
  79. return JSON.stringify(value, replacer, space);
  80. } catch (error) {
  81. return null;
  82. }
  83. }
  84. /**
  85. * 判断字符是否为双字节字符(如中文字符)
  86. * @param string 原字符串
  87. */
  88. public str_isDoubleWord(string: string): boolean {
  89. return /[^\x00-\xff]/.test(string);
  90. }
  91. /**
  92. * 是否为空
  93. * @param str
  94. */
  95. public str_isEmpty(str: string): boolean {
  96. if (str == null || str == undefined || str.length == 0) {
  97. return true;
  98. }
  99. return false;
  100. }
  101. /**
  102. * 转美式计数字符串
  103. * @param value 数字
  104. * @example
  105. * 123456789 = 123,456,789
  106. */
  107. public numberTotPermil(value: number): string {
  108. return value.toLocaleString();
  109. }
  110. private readonly _k: number = 1000;
  111. private readonly _k_sizes_en: string[] = ['', 'K', 'M', 'G', 'T', 'P', 'E'];
  112. private readonly _k_sizes_cn: string[] = ['', '千', '百万', '十亿', '万亿', '拍(千万亿)', '艾(十亿亿)'];
  113. private readonly _w: number = 10000;
  114. private readonly _w_sizes_en: string[] = ['', 'W', 'M', 'B', 'T'];
  115. private readonly _w_sizes_cn: string[] = ['', '万', '亿', '万亿'];
  116. /**
  117. * 通用单位转换方法
  118. */
  119. private convertNumber(value: number, base: number, sizes: string[], fixed: number): string {
  120. if (value < base) return value.toString();
  121. const i = Math.floor(Math.log(value) / Math.log(base));
  122. const r = value / Math.pow(base, i);
  123. if (i >= sizes.length) return value.toString();
  124. return `${r.toFixed(fixed)}${sizes[i]}`;
  125. }
  126. /**
  127. * 转单位计数(默认英文)
  128. * @param value 数字
  129. * @param fixed 保留小数位数
  130. * @param isEn 是否英文
  131. * @example
  132. * 12345 = 12.35K
  133. */
  134. public numberToThousand(value: number, fixed: number = 2, isEn: boolean = true): string {
  135. const sizes = isEn ? this._k_sizes_en : this._k_sizes_cn;
  136. return this.convertNumber(value, this._k, sizes, fixed);
  137. }
  138. /**
  139. * 转单位计数(默认中文)
  140. * @param value 数字
  141. * @param fixed 保留小数位数
  142. * @param isEn 是否英文
  143. * @example
  144. * 12345 = 1.23万
  145. */
  146. public numberToTenThousand(value: number, fixed: number = 2, isEn: boolean = false): string {
  147. const sizes = isEn ? this._w_sizes_en : this._w_sizes_cn;
  148. return this.convertNumber(value, this._w, sizes, fixed);
  149. }
  150. /**获取一个唯一标识的字符串 */
  151. public guid() {
  152. let guid: string = Math.random().toString(36).substring(2);
  153. return guid;
  154. }
  155. /**排序一个json Object*/
  156. public obj_sort(obj: any): any {
  157. let sorted_keys: string[] = Object.keys(obj).sort();
  158. let new_obj = {};
  159. for (let i = 0; i < sorted_keys.length; i++) {
  160. const key = sorted_keys[i];
  161. const value = obj[key];
  162. const t = this.obj_get_value_type(value);
  163. new_obj[key] = t == 'object' ? this.obj_sort(value) : value;
  164. }
  165. return new_obj;
  166. }
  167. /**获取一个josn值的类型*/
  168. public obj_get_value_type(value: any): string {
  169. let type: string;
  170. if (value === null) {
  171. type = 'null';
  172. } else {
  173. type = typeof value;
  174. }
  175. // 如果是对象或数组,还可以进一步判断
  176. if (type === 'object') {
  177. if (Array.isArray(value)) { type = 'array'; }
  178. //else if (value instanceof Date) { type = 'date'; }
  179. }
  180. return type;
  181. }
  182. /**
  183. * 判断指定的值是否为对象
  184. * @param value 值
  185. */
  186. public valueIsObject(value: any): boolean {
  187. return Object.prototype.toString.call(value) === '[object Object]';
  188. }
  189. /**
  190. * 深拷贝
  191. * @param target 目标
  192. */
  193. /** 克隆对象 */
  194. public obj_clone<T = any>(target_: T, record_set = new Set()): T {
  195. let result: any;
  196. switch (typeof target_) {
  197. case "object": {
  198. // 数组:遍历拷贝
  199. if (Array.isArray(target_)) {
  200. if (record_set.has(target_)) {
  201. return target_;
  202. }
  203. record_set.add(target_);
  204. result = [];
  205. for (let k_n = 0; k_n < target_.length; ++k_n) {
  206. // 递归克隆数组中的每一项
  207. result.push(this.obj_clone(target_[k_n], record_set));
  208. }
  209. }
  210. // null:直接赋值
  211. else if (target_ === null) {
  212. result = null;
  213. }
  214. // RegExp:直接赋值
  215. else if ((target_ as any).constructor === RegExp) {
  216. result = target_;
  217. }
  218. // 普通对象:循环递归赋值对象的所有值
  219. else {
  220. if (record_set.has(target_)) {
  221. return target_;
  222. }
  223. record_set.add(target_);
  224. result = {};
  225. for (const k_s in target_) {
  226. result[k_s] = this.obj_clone(target_[k_s], record_set);
  227. }
  228. }
  229. break;
  230. }
  231. case "function": {
  232. result = target_.bind({});
  233. break;
  234. }
  235. default: {
  236. result = target_;
  237. }
  238. }
  239. return result;
  240. }
  241. /**
  242. * 拷贝对象
  243. * @param target 目标
  244. */
  245. public copy(target: object): object {
  246. return this.parse(this.stringify(target));
  247. }
  248. //请求相关--------------------------------------------
  249. public getReqId(): string {
  250. return Date.now() + "_" + Math.ceil(1e3 * Math.random());
  251. }
  252. private _url_data: Record<string, string> | null = null;
  253. /**获取链接中传入的某个值*/
  254. public getUrlData<T extends (string | number)>(key: string): T | null {
  255. if (!this._url_data) {
  256. this._url_data = this.parseUrl();
  257. }
  258. const value = this._url_data[key];
  259. if (value === undefined) return null;
  260. if (typeof value === 'string') {
  261. const numberValue = parseFloat(value);
  262. if (!isNaN(numberValue)) return (numberValue as unknown) as T;
  263. return value as T;
  264. }
  265. return null;
  266. }
  267. private parseUrl(): Record<string, string> {
  268. if (!sys.isBrowser || typeof window !== "object" || !window.document) return {};
  269. const url = window.document.location.href;
  270. const queryString = url.split("?")[1];
  271. if (!queryString) return {};
  272. return queryString.split("&").reduce<Record<string, string>>((acc, param) => {
  273. const [key, value] = param.split("=");
  274. if (key) acc[decodeURIComponent(key)] = value ? decodeURIComponent(value) : '';
  275. return acc;
  276. }, {});
  277. }
  278. /**获到node的坐标和范围用于平台在对应位置创建按纽*/
  279. public getBtnOp(btnNode: Node): { left: number, top: number, width: number, height: number } {
  280. let btnNodeUiTransform = btnNode.getComponent(UITransform);
  281. const btnSize = size(btnNodeUiTransform.width + 0, btnNodeUiTransform.height + 0);
  282. //let frameWidth = screen.windowSize.width / screen.devicePixelRatio
  283. let frameHeight = screen.windowSize.height / screen.devicePixelRatio
  284. //const winSize = screen.windowSize;
  285. //const designSize = view.getDesignResolutionSize();
  286. //console.log('designSize', designSize);
  287. //console.log('winSize:', winSize);
  288. //console.log('frameSize:', frameWidth,frameHeight);
  289. //适配不同机型来创建微信授权按钮
  290. let rect = btnNodeUiTransform.getBoundingBoxToWorld();
  291. let ratio = screen.devicePixelRatio;
  292. let scale = view.getScaleX();
  293. let factor = scale / ratio;
  294. let offsetX = 0;
  295. let offsetY = 0;
  296. let top = frameHeight - (rect.y + rect.height) * factor - offsetY;
  297. let left = rect.x * factor + offsetX;
  298. const width = btnSize.width * factor;
  299. const height = btnSize.height * factor;
  300. return { left: left, top: top, width: width, height: height }
  301. }
  302. /**远程加载图片*/
  303. public async loadImage(url: string): Promise<SpriteFrame> {
  304. if (chsdk.get_pf() == chsdk.pf.web) {
  305. return await this.loadRemoteSprite(url);
  306. } else {
  307. const idata = await chsdk.loadImage(url);
  308. return this.getSpriteFrame(idata);
  309. }
  310. }
  311. /**cocos加载远程图片*/
  312. public loadRemoteSprite(remoteUrl: string, ext: '.png' | '.jpg' = '.png'): Promise<SpriteFrame> {
  313. return new Promise((resolve) => {
  314. assetManager.loadRemote<ImageAsset>(remoteUrl, { ext: ext }, function (err, imageAsset) {
  315. const spriteFrame = new SpriteFrame();
  316. const texture = new Texture2D();
  317. texture.image = imageAsset;
  318. spriteFrame.texture = texture;
  319. resolve(spriteFrame);
  320. });
  321. });
  322. }
  323. /**远程加载的图片数据转成spriteFrame*/
  324. private getSpriteFrame(img: any): SpriteFrame {
  325. if (!img) return null;
  326. try {
  327. const spriteFrame = new SpriteFrame();
  328. const texture = new Texture2D();
  329. texture.image = img instanceof ImageAsset ? img : new ImageAsset(img);
  330. spriteFrame.texture = texture;
  331. return spriteFrame;
  332. } catch (error) {
  333. //ch_log.warn(error);
  334. return null;
  335. }
  336. }
  337. }
  338. export default ch_util.getInstance();