ViewUtil.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // 导入 Cocos Creator 核心模块
  2. const { _decorator, Component, EventTouch, find, instantiate, Node, Animation, Prefab, Size, UITransform, v3, Vec3, AnimationClip } = cc;
  3. /**
  4. * 视图工具类 - 提供节点查找、组件操作、坐标转换、预制创建等常用功能
  5. */
  6. const ViewUtil = {
  7. /**
  8. * 查找节点
  9. * @param {Node} curNode 当前节点
  10. * @param {string} path 从当前节点开始的路径
  11. * @returns {Node|null} 找到的节点(未找到返回null)
  12. **/
  13. findNode(curNode, path) {
  14. if (!curNode) {
  15. console.error(`当前节点为空:${path}`);
  16. return null;
  17. }
  18. let node = find(path, curNode);
  19. if (node) {
  20. return node;
  21. }
  22. console.error(`未找到节点:${path},当前节点:${curNode.name}`);
  23. return null;
  24. },
  25. /**
  26. * 查找组件
  27. * @param {Node} curNode 当前节点
  28. * @param {string} path 从当前节点开始的路径
  29. * @param {Function} type 组件构造函数(如 cc.Button)
  30. * @returns {Component|null} 找到的组件(未找到返回null)
  31. */
  32. findComponent(curNode, path, type) {
  33. let node = this.findNode(curNode, path);
  34. if (node) {
  35. return node.getComponent(type);
  36. }
  37. console.error(`未找到组件:${path}`);
  38. return null;
  39. },
  40. /**
  41. * 添加组件(若节点已存在该组件则直接返回,否则新增组件)
  42. * @param {Node} curNode 当前节点
  43. * @param {string} path 从当前节点开始的路径
  44. * @param {Function} type 组件构造函数(如 cc.Button)
  45. * @returns {Component|null} 添加/获取的组件(失败返回null)
  46. */
  47. addComponent(curNode, path, type) {
  48. let node = this.findNode(curNode, path);
  49. if (node) {
  50. let compt = node.getComponent(type) || node.addComponent(type);
  51. return compt;
  52. }
  53. console.error(`添加组件失败:${path}`);
  54. return null;
  55. },
  56. /**
  57. * 把Node当前的节点树结构根据Node命名转成一个Map对象,重名的组件会覆盖
  58. * Node的name不应该包含空格键,否则将跳过
  59. * @param {Node} parent 被遍历的Node组件
  60. * @param {Map} obj 绑定的Map对象 (可选)
  61. * @returns {Map|null} 存储节点名与节点对应关系的Map
  62. */
  63. nodeTreeInfoLite(parent, obj) {
  64. let map = obj || new Map();
  65. let items = parent.children;
  66. for (let i = 0; i < items.length; i++) {
  67. let _node = items[i];
  68. if (_node.name.indexOf(" ") < 0) {
  69. map.set(_node.name, _node);
  70. }
  71. ViewUtil.nodeTreeInfoLite(_node, map);
  72. }
  73. return map;
  74. },
  75. /**
  76. * 正则搜索节点名字,符合条件的节点将会返回
  77. * @param {RegExp} reg 正则表达式
  78. * @param {Node} parent 要搜索的父节点
  79. * @param {Array<Node>} nodes 返回的数组(可选)
  80. * @returns {Array<Node>} 符合正则条件的节点数组
  81. */
  82. findNodes(reg, parent, nodes) {
  83. let ns = nodes || [];
  84. let items = parent.children;
  85. for (let i = 0; i < items.length; i++) {
  86. let _name = items[i].name;
  87. if (reg.test(_name)) {
  88. ns.push(items[i]);
  89. }
  90. ViewUtil.findNodes(reg, items[i], ns);
  91. }
  92. return ns;
  93. },
  94. /**
  95. * 节点之间坐标互转
  96. * @param {Node} a A节点
  97. * @param {Node} b B节点
  98. * @param {Vec3} aPos A节点空间中的相对位置
  99. * @returns {Vec3} 转换到B节点空间的位置
  100. */
  101. calculateASpaceToBSpacePos(a, b, aPos) {
  102. const world = a.getComponent(UITransform).convertToWorldSpaceAR(aPos);
  103. return b.getComponent(UITransform).convertToNodeSpaceAR(world);
  104. },
  105. /**
  106. * 屏幕转空间坐标
  107. * @param {EventTouch} event 触摸事件
  108. * @param {Node} space 转到此节点的坐标空间
  109. * @returns {Vec3} 转换后的节点空间坐标
  110. */
  111. calculateScreenPosToSpacePos(event, space) {
  112. const uil = event.getUILocation();
  113. const worldPos = v3(uil.x, uil.y);
  114. return space.getComponent(UITransform).convertToNodeSpaceAR(worldPos);
  115. },
  116. /**
  117. * 显示对象等比缩放
  118. * @param {number} targetWidth 目标宽
  119. * @param {number} targetHeight 目标高
  120. * @param {number} defaultWidth 默认宽
  121. * @param {number} defaultHeight 默认高
  122. * @returns {Size} 等比缩放后的尺寸
  123. */
  124. uniformScale(targetWidth, targetHeight, defaultWidth, defaultHeight) {
  125. const widthRatio = defaultWidth / targetWidth;
  126. const heightRatio = defaultHeight / targetHeight;
  127. let ratio;
  128. widthRatio < heightRatio ? ratio = widthRatio : ratio = heightRatio;
  129. return new Size(Math.floor(targetWidth * ratio), Math.floor(targetHeight * ratio));
  130. },
  131. // /**
  132. // * 从资源缓存中找到预制资源名并创建一个显示对象
  133. // * (建议使用GameComponent里的同名方法,能自动管理内存释放)
  134. // * @param {string} path 资源路径
  135. // * @param {string} bundleName 资源包名(默认使用resLoader的默认包名)
  136. // * @returns {Node|null} 创建的预制节点(未找到资源返回null)
  137. // */
  138. // createPrefabNode(path, bundleName = resLoader.defaultBundleName) {
  139. // // 通过路径与类型获取已缓存资源
  140. // const p = resLoader.get(path, Prefab, bundleName);
  141. // if (p) {
  142. // return instantiate(p);
  143. // }
  144. // console.warn(`未找到预制资源:${path},是否在预加载common文件夹之前调用?`);
  145. // return null;
  146. // },
  147. // /**
  148. // * 异步加载预制并创建预制节点
  149. // * (建议使用GameComponent里的同名方法,能自动管理内存释放)
  150. // * @param {string} path 资源路径
  151. // * @param {string} bundleName 资源包名(默认使用resLoader的默认包名)
  152. // * @returns {Promise<Node|null>} 创建的预制节点Promise
  153. // */
  154. // createPrefabNodeAsync(path, bundleName = resLoader.defaultBundleName) {
  155. // return new Promise(async (resolve, reject) => {
  156. // try {
  157. // const p = await resLoader.loadAsync(bundleName, path, Prefab);
  158. // if (p) {
  159. // resolve(instantiate(p));
  160. // } else {
  161. // console.error(`名为【${path}】的资源加载失败`);
  162. // resolve(null);
  163. // }
  164. // } catch (error) {
  165. // console.error(`预制资源异步加载异常:${path}`, error);
  166. // resolve(null);
  167. // }
  168. // });
  169. // },
  170. // /**
  171. // * 添加节点动画
  172. // * @param {string} path 资源路径
  173. // * @param {Node} node 目标节点
  174. // * @param {boolean} onlyOne 是否唯一(默认true,避免重复播放)
  175. // * @param {boolean} isDefaultClip 是否播放默认动画剪辑(默认false)
  176. // */
  177. // addNodeAnimation(path, node, onlyOne = true, isDefaultClip = false) {
  178. // if (!node || !node.isValid) {
  179. // return;
  180. // }
  181. // let anim = node.getComponent(Animation);
  182. // if (anim == null) {
  183. // anim = node.addComponent(Animation);
  184. // }
  185. // const clip = resLoader.get(path, AnimationClip);
  186. // if (!clip) {
  187. // return;
  188. // }
  189. // if (onlyOne && anim.getState(clip.name) && anim.getState(clip.name).isPlaying) {
  190. // return;
  191. // }
  192. // if (isDefaultClip) {
  193. // anim.defaultClip = clip;
  194. // anim.play();
  195. // return;
  196. // }
  197. // // 播放完成后恢复播放默认动画
  198. // anim.once(Animation.EventType.FINISHED, () => {
  199. // if (anim.defaultClip) {
  200. // anim.play();
  201. // }
  202. // }, this);
  203. // if (anim.getState(clip.name)) {
  204. // anim.play(clip.name);
  205. // return;
  206. // }
  207. // anim.createState(clip, clip.name);
  208. // anim.play(clip.name);
  209. // }
  210. };
  211. // 导出工具类,供其他文件导入
  212. module.exports = { ViewUtil };//使用 CommonJS 模块导出方式