vv.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. namespace vv {
  2. /**全局消息注册分发 */
  3. export let Notifi = new cc.EventTarget();
  4. /**存储本地数据 */
  5. export function saveData(name: string, data: any) {
  6. data = JSON.stringify(data)
  7. cc.log("本地存储:", name, data)
  8. cc.sys.localStorage.setItem(name, data);
  9. }
  10. /**读取本地数据 */
  11. export function readData(name: string) {
  12. let data = cc.sys.localStorage.getItem(name)
  13. data = JSON.parse(data);
  14. cc.log("本地读取:", name, data)
  15. return data
  16. }
  17. export let lang = lang_();
  18. export function lang_(): string {
  19. let str = cc.sys.localStorage.getItem("language");
  20. if (str == "" || str == null) str = "en";
  21. return str;
  22. };
  23. /**找到子节点
  24. @param nodeName 子节点名称
  25. @param parentNode 开始寻找的父节点
  26. */
  27. export function findChildNode(nodeName: string, parentNode: cc.Node) {
  28. let node: cc.Node = null
  29. let nodes: cc.Node[] = []
  30. let fun = (parentNode: cc.Node) => {
  31. for (let i of parentNode.children) {
  32. if (i.name == nodeName) {
  33. nodes.push(i)
  34. if (nodes.length > 1) {
  35. //cc.warn("至少存在2个同名子节点:", nodes, nodeName, parentNode.name)
  36. return node
  37. }
  38. }
  39. if (i.childrenCount > 0) {
  40. fun(i)
  41. }
  42. }
  43. }
  44. fun(parentNode)
  45. switch (nodes.length) {
  46. case 0:
  47. //cc.warn("找不到子节点:", nodeName, parentNode.name)
  48. return null;
  49. default:
  50. return nodes[0];
  51. }
  52. }
  53. /**
  54. * 加载图片
  55. * @param url 图片地址(网图不加png后缀,本地需要)
  56. * @param spriteNode 需要改变纹理的节点
  57. * @param isNet 默认false:本地资源, true:是网图
  58. */
  59. export function loadTexture(url: string, spriteNode: cc.Node | cc.Sprite, isNet?: boolean) {
  60. if (spriteNode == null) return
  61. spriteNode.getComponent(cc.Sprite).spriteFrame = null
  62. if (url == "" || url == null) return cc.log("url is null");
  63. if (isNet == null || !isNet) {
  64. cc.loader.loadRes(url, cc.SpriteFrame, (error, texture) => {
  65. if (error) {
  66. cc.warn("图片加载失败!path:", url)
  67. return
  68. }
  69. let sp = texture
  70. if (!spriteNode.isValid) return;
  71. if (spriteNode instanceof cc.Node) {
  72. spriteNode.getComponent(cc.Sprite).spriteFrame = sp
  73. }
  74. else {
  75. spriteNode.spriteFrame = sp
  76. }
  77. })
  78. }
  79. else {
  80. cc.loader.load(url, (error, texture) => {
  81. if (error) return error
  82. let sp = new cc.SpriteFrame(texture)
  83. if (!spriteNode.isValid) return;
  84. if (spriteNode instanceof cc.Node) {
  85. spriteNode.getComponent(cc.Sprite).spriteFrame = sp
  86. }
  87. else {
  88. spriteNode.spriteFrame = sp
  89. }
  90. })
  91. }
  92. }
  93. /**
  94. * 添加点击事件
  95. * 回调必须要lambda表达式或者bind(this)
  96. * @param target 添加点击的节点
  97. * @param cb 回调方法
  98. * @param bindNode 绑定脚本
  99. */
  100. export function addBtnEvent(target: cc.Node, cb: Function, bindNode: any, clickAudio = true) {
  101. let btn: cc.Button = target.getComponent(cc.Button);
  102. if (!btn) btn = target.addComponent(cc.Button);
  103. btn.clickEvents = [];
  104. target.on("click", (...arg) => {
  105. // if (clickAudio)
  106. //vv.playAudio("ui_click");
  107. cb?.(...arg);
  108. }, bindNode);
  109. }
  110. export function deleteBtnEvent(target: cc.Node, bindNode: any, clickAudio = true) {
  111. let btn: cc.Button = target.getComponent(cc.Button);
  112. if (!btn) btn = target.addComponent(cc.Button);
  113. btn.clickEvents = [];
  114. target.targetOff(bindNode)
  115. }
  116. /**
  117. * 数字跳动
  118. * @param startNum 起始值
  119. * @param originNum 目标值
  120. * @param go 数字节点
  121. * @param time 跳动时间
  122. */
  123. export function numberAnim(startNum: number, endNum: number, go: cc.Node, str: string = "", time: number = 0.6) {
  124. let originNum = (endNum - startNum) * 100;
  125. let obj: any = {};
  126. obj.num = originNum;
  127. let tempNum = 0;
  128. let ac = cc.tween(obj)
  129. .to(time, { num: 0 }, {
  130. progress: (start, end, current, t) => {
  131. tempNum = Math.ceil((start - end) * t);
  132. go.getComponent(cc.Label).string = str + vv.goldFormat((Number(Number(startNum) + Number(((tempNum) / 100).toFixed(2))).toFixed(2)));
  133. return start + (start - end) * t;
  134. }
  135. })
  136. .call(() => {
  137. go.getComponent(cc.Label).string = str + vv.goldFormat(Number(endNum).toFixed(2));
  138. })
  139. .start();
  140. return ac;
  141. }
  142. /**
  143. * 将数字格式转换成 100.000,00 的格式
  144. * @param num 数字或字符串类型的数
  145. * @param isInt 整数是否保留.00
  146. * @returns
  147. */
  148. export function goldFormat(num, isInt = false) {
  149. if (typeof (num) == "string") num = Number(num);
  150. let fushu = false;
  151. if (num < 0) {
  152. fushu = true;
  153. num = Math.abs(num);
  154. }
  155. if (isInt) {
  156. num = num.toString();
  157. if (num.indexOf(".") != -1) {
  158. num = num.split(".")[0];
  159. cc.warn("当前传入的不是正数");
  160. }
  161. let str = "";
  162. let zheng = "";
  163. let idx = 0;
  164. for (let i = num.length - 1; i > -1; i--) {
  165. str = num[i] + str;
  166. idx++;
  167. if (idx % 3 == 0 || i == 0) {
  168. if (zheng != "")
  169. zheng = str + "." + zheng;
  170. else zheng = str + zheng;
  171. str = "";
  172. }
  173. }
  174. return fushu ? "-" + zheng : zheng;
  175. }
  176. num = num.toFixed(2);
  177. let arr = num.split(".");
  178. let dian = arr.length == 1 ? "00" : arr[1];
  179. if (Number(dian) < 10 && dian.length == 1) dian = "0" + dian;
  180. let str = "";
  181. let zheng = "";
  182. let idx = 0;
  183. for (let i = arr[0].length - 1; i > -1; i--) {
  184. str = arr[0][i] + str;
  185. idx++;
  186. if (idx % 3 == 0 || i == 0) {
  187. if (zheng != "")
  188. zheng = str + "." + zheng;
  189. else zheng = str + zheng;
  190. str = "";
  191. }
  192. }
  193. return fushu ? ("-" + zheng + "," + dian) : (zheng + "," + dian);
  194. }
  195. /**
  196. * 分割网址中的参数
  197. * @param url 网址
  198. * @returns
  199. */
  200. export function getUrlData(url: string): any {
  201. let arr = url.split("?");
  202. if (arr.length == 1) {
  203. return null
  204. }
  205. let arr1 = arr[1].split("&");
  206. let data = {};
  207. arr1.forEach(v => {
  208. let temp = v.split('=');
  209. if (!temp[1] || data[temp[0]]) {
  210. } else
  211. data[temp[0]] = temp[1];
  212. });
  213. return data;
  214. }
  215. /**加载预制体并适配 适配仅限个别预制体 */
  216. export function loadPrefab(str, parent = null, cb = null, isactive = true) {
  217. return new Promise((resolve, reject) => {
  218. cc.resources.load<cc.Prefab>("Prefab/" + str, (err, res) => {
  219. if (err) {
  220. reject(err);
  221. return cc.warn(err);
  222. }
  223. let can = cc.find("Canvas");
  224. let ch = cc.instantiate(res);
  225. let arr = str.split("/");
  226. ch.name = arr[arr.length - 1];
  227. ch.active = isactive;
  228. if (parent == null) ch.parent = can;
  229. else ch.parent = parent;
  230. if (cb != null && typeof (cb) == 'function') cb(ch);
  231. resolve(ch);
  232. })
  233. })
  234. }
  235. /**预加载场景 */
  236. export function preLoadScene(sceneName, onProgressCB = null) {
  237. return new Promise((resolve, reject) => {
  238. cc.director.preloadScene(sceneName, (completedCount: number, totalCount: number, item: any) => {
  239. onProgressCB && onProgressCB(completedCount, totalCount);
  240. }, (err) => {
  241. if (err) {
  242. reject(err);
  243. return;
  244. }
  245. cc.log(`场景 ${sceneName} 预加载完成`)
  246. resolve(sceneName);
  247. })
  248. });
  249. }
  250. /**预加载场景 */
  251. export function loadScene(sceneName, onProgressCB = null) {
  252. return new Promise((resolve, reject) => {
  253. cc.director.loadScene(sceneName, (err) => {
  254. if (err) {
  255. reject(err);
  256. return;
  257. }
  258. cc.log(`场景 ${sceneName} 加载完成`)
  259. resolve(sceneName);
  260. onProgressCB?.()
  261. })
  262. });
  263. }
  264. /**时间戳转换成时间格式
  265. @param format 格式,比如:"2019.6.3" "2018-11-27" "11月7日" "13:05:45:585"
  266. @param time 时间戳[13位](兼容10位 默认当前时间)
  267. @param bitZero 是否补零(只针对月、日) 默认不补零
  268. */
  269. export function timeToTime(format: string, time?: number, bitZero: boolean = false) {
  270. if (time == null) time = Date.now()
  271. if (time < 100000000000) time = time * 1000
  272. let newTime = new Date(time)
  273. let str = ""
  274. if (format.includes(" ")) {
  275. cc.warn("该时间字符串模版未实现解析:", format)
  276. return str
  277. }
  278. if (format.indexOf(":") == -1) { //年月日
  279. let year = String(newTime.getFullYear())
  280. let month = String(newTime.getMonth() + 1)
  281. let day = String(newTime.getDate())
  282. if (bitZero) {
  283. if (month.length == 1) month = "0" + month
  284. if (day.length == 1) day = "0" + day
  285. }
  286. let m = ""
  287. if (format.indexOf(".") > 0) m = "."
  288. else if (format.indexOf("-") > 0) m = "-"
  289. else if (format.indexOf("/") > 0) m = "/"
  290. if (m != "") {
  291. let num = 0
  292. for (let i = 0; i < format.length; i++) {
  293. if (format[i] == m) num++
  294. }
  295. if (num == 2) { //年月日
  296. str = year + m + month + m + day
  297. } else if (num == 1) { //年月或月日
  298. if (format.length < 6) { //月日
  299. str = month + m + day
  300. } else { //年月
  301. str = year + m + month
  302. }
  303. }
  304. } else {
  305. if (format.includes("年") && format.includes("日")) { //年月日
  306. str = year + "年" + month + "月" + day + "日"
  307. } else if (format.includes("年")) { //年月
  308. str = year + "年" + month + "月"
  309. } else if (format.includes("日")) { //月日
  310. str = month + "月" + day + "日"
  311. }
  312. }
  313. } else {
  314. let f = format.split(':')
  315. let hour = String(newTime.getHours())
  316. let minute = String(newTime.getMinutes())
  317. let second = String(newTime.getSeconds())
  318. let millisecond = String(newTime.getMilliseconds())
  319. if (hour.length == 1) hour = "0" + hour
  320. if (minute.length == 1) minute = "0" + minute
  321. if (second.length == 1) second = "0" + second
  322. while (millisecond.length < 3) millisecond = "0" + millisecond
  323. if (f.length == 4) { //小时:分:秒:毫秒
  324. str = hour + ":" + minute + ":" + second + ":" + millisecond
  325. } else if (f.length == 3) {
  326. if (f[2].length > 2 || +f[0] > 24) { //分:秒:毫秒
  327. str = minute + ":" + second + ":" + millisecond
  328. } else { //小时:分:秒
  329. str = hour + ":" + minute + ":" + second
  330. }
  331. } else if (f.length == 2) {
  332. if (+f[0] > 24) { //分:秒
  333. str = minute + ":" + second
  334. } else { //小时:分
  335. str = hour + ":" + minute
  336. }
  337. }
  338. }
  339. if (str == "") cc.warn("该时间字符串模版未实现解析:", format)
  340. return str
  341. }
  342. /** 00:00:00 */
  343. export function timeToTime2(time?: number) {
  344. if (time == null) time = Date.now()
  345. if (time < 100000000000) time = time * 1000
  346. let newTime = new Date(time)
  347. let str = ""
  348. let hour = String(newTime.getHours())
  349. let minute = String(newTime.getMinutes())
  350. let second = String(newTime.getSeconds())
  351. if (hour.length == 1) hour = "0" + hour
  352. if (minute.length == 1) minute = "0" + minute
  353. if (second.length == 1) second = "0" + second
  354. str = hour + ":" + minute + ":" + second;
  355. cc.log(hour, minute, second)
  356. cc.log(str)
  357. return str
  358. }
  359. /** 00:00:00 */
  360. export function timeToTime3(time?: number) {
  361. let str = "";
  362. let hour: any = Math.floor(time / 3600);
  363. let less: any = time - 3600 * hour;
  364. let minute: any = Math.floor(less / 60);
  365. less = less - 60 * minute;
  366. let second: any = less;
  367. if (hour < 10) hour = "0" + hour;
  368. if (minute < 10) minute = "0" + minute;
  369. if (second < 10) second = "0" + second;
  370. str = hour + ":" + minute + ":" + second;
  371. return str
  372. }
  373. //加载resources下文件
  374. export function loadRes(url, type, completeCallback?, progressCallback?) {
  375. var extname = cc.path.extname(url);
  376. if (extname) {
  377. url = url.slice(0, - extname.length);
  378. }
  379. cc.resources.load(url, type, progressCallback, (err, asset: cc.Asset) => {
  380. if (err) {
  381. console.error("加载失败", url, "\n", err);
  382. return;
  383. }
  384. completeCallback(asset);
  385. });
  386. }
  387. /**子节点转换子节点坐标 */
  388. export function nodePosToNodePos(node_1: cc.Node, node_2: cc.Node) {
  389. let wPos = this.nodePosToWord(node_1);
  390. let nPos = this.nodePosToNode(node_2);
  391. return nPos;
  392. }
  393. /**转世界坐标 */
  394. export function nodePosToWord(node: cc.Node) {
  395. return node.parent.convertToWorldSpaceAR(node.position);
  396. }
  397. /**将世界坐标转节点坐标 */
  398. export function nodePosToNode(pos: cc.Vec3, node: cc.Node) {
  399. return node.convertToNodeSpaceAR(pos);
  400. }
  401. /**限制小数点后位数,大于5位限制5,小于5显示正常,最少保留2位 */
  402. export function limtStr(number, limit = 5) {
  403. let str = number.toString();
  404. let arr = str.split(".");
  405. if (arr.length > 1 && arr[1].length > 2) {
  406. if (arr[1].length > 5) return number.toFixed(limit);
  407. else return number.toFixed(arr[1].length);
  408. }
  409. return number.toFixed(2);
  410. }
  411. export function maskString(str: string): string {
  412. const length = str.length;
  413. if (length === 0) {
  414. // 如果是空字符串,返回 "***"
  415. return '***';
  416. } else if (length === 1) {
  417. // 如果长度为1,保留该字符前后加上“***”
  418. return str + '***';
  419. } else if (length === 2) {
  420. // 如果长度为2,保留前1和后1,中间加上“***”
  421. return str.charAt(0) + '***' + str.charAt(1);
  422. } else if (length < 6) {
  423. // 如果长度小于6,保留前2位和后2位
  424. return str.slice(0, 2) + '***' + str.slice(-2);
  425. } else {
  426. // 如果长度大于或等于6,保留前3位和后3位
  427. return str.slice(0, 3) + '***' + str.slice(-3);
  428. }
  429. }
  430. export function webCopyString(copyStr) {
  431. const el = document.createElement('textarea');
  432. el.value = copyStr;
  433. // Prevent keyboard from showing on mobile
  434. el.setAttribute('readonly', '');
  435. //el.style.contain = 'strict';
  436. el.style.position = 'absolute';
  437. el.style.left = '-9999px';
  438. el.style.fontSize = '12pt'; // Prevent zooming on iOS
  439. const selection = getSelection()!;
  440. let originalRange;
  441. if (selection.rangeCount > 0) {
  442. originalRange = selection.getRangeAt(0);
  443. }
  444. document.body.appendChild(el);
  445. el.select();
  446. // Explicit selection workaround for iOS
  447. el.selectionStart = 0;
  448. el.selectionEnd = copyStr.length;
  449. let success = false;
  450. try {
  451. success = document.execCommand('copy');
  452. } catch (err) { }
  453. document.body.removeChild(el);
  454. if (originalRange) {
  455. selection.removeAllRanges();
  456. selection.addRange(originalRange);
  457. }
  458. }
  459. /**限制名字 */
  460. export function limitName(str) {
  461. str = str.slice(0, 3) + "..." + str.slice(str.length - 2, str.length);
  462. return str;
  463. }
  464. }
  465. if (!window["vv"]) { window["vv"] = vv }
  466. else { for (let i in vv) { window["vv"][i] = vv[i] } }
  467. setTimeout(() => { for (let i in window["vv"]) vv[i] = window["vv"][i] }, 0)