GameItem.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { AppKernel } from '@scripts/app/AppKernel';
  2. import { GAME_TABS } from '@scripts/define/Define';
  3. import { IGameInfo } from '@scripts/store/gameStore/data';
  4. import { setSpriteFrame } from '@scripts/utils/ResHelper';
  5. import { addBtnClick, removeBtnClick, setAlpha } from '@scripts/utils/UIHelper';
  6. import { _decorator, Button, CCBoolean, Color, Component, isValid, Label, Node, Size, Sprite, UITransform, Vec4 } from 'cc';
  7. const { ccclass, property } = _decorator;
  8. @ccclass('GameItem')
  9. export class GameItem extends Component {
  10. @property(Sprite)
  11. gameIcon: Sprite;
  12. @property(Node)
  13. btnFavo: Node;
  14. @property(Sprite)
  15. favoFlag: Sprite;
  16. @property(Node)
  17. hotFlag: Node;
  18. @property(Node)
  19. newFlag: Node;
  20. @property(Label)
  21. lblGameName: Label;
  22. @property(CCBoolean)
  23. isPortrait: boolean;
  24. @property({ tooltip: '配合 sprite-cover 材质,图片居中等比铺满节点区域' })
  25. coverIcon = false;
  26. private _data: IGameInfo;
  27. private _dataIdx: number;
  28. protected onLoad(): void {
  29. if (this.coverIcon) {
  30. this.node.on(Node.EventType.SIZE_CHANGED, this.updateIconCover, this);
  31. this.updateIconCover();
  32. }
  33. addBtnClick(this.gameIcon.node, this.onClick, this);
  34. addBtnClick(this.btnFavo, this.onBtnFavoClick, this);
  35. }
  36. public async updateItem(data: IGameInfo, index: number) {
  37. this._data = data;
  38. this._dataIdx = index;
  39. if (!this.node.active) this.node.active = true;
  40. if (this.lblGameName) this.lblGameName.string = data.gameName;
  41. setAlpha(this.favoFlag, data.isFavorite);
  42. this.setGameFlag(data.tags.map(t => t.tagCode));
  43. const url = this.isPortrait ? data.coverUrl?.Portrait : data.coverUrl?.Square;
  44. if (!url) {
  45. this.gameIcon.spriteFrame = null;
  46. return;
  47. }
  48. const tmpIdx = this._dataIdx;
  49. const res = await AppKernel.resources.loadRemoteSp(url);
  50. if (tmpIdx === this._dataIdx && this._data === data && isValid(this.node)) {
  51. this.gameIcon.spriteFrame = res;
  52. this.updateIconCover();
  53. }
  54. }
  55. private setGameFlag(tags: string[]) {
  56. this.newFlag.active = false;
  57. this.hotFlag.active = false;
  58. if (tags.includes(GAME_TABS.Hot)) {
  59. this.hotFlag.active = true;
  60. return;
  61. }
  62. if (tags.includes(GAME_TABS.New)) {
  63. this.newFlag.active = true;
  64. }
  65. }
  66. private updateIconCover(): void {
  67. if (!this.coverIcon) return;
  68. const target = this.node.getComponent(UITransform);
  69. const icon = this.gameIcon;
  70. icon.sizeMode = Sprite.SizeMode.CUSTOM;
  71. icon.getComponent(UITransform).setContentSize(target.contentSize);
  72. const frame = icon.spriteFrame;
  73. if (!frame || target.width <= 0 || target.height <= 0) return;
  74. const { width, height } = frame.rect;
  75. if (width <= 0 || height <= 0) return;
  76. const scale = Math.max(target.width / width, target.height / height);
  77. const sx = target.width / (width * scale);
  78. const sy = target.height / (height * scale);
  79. const uv = frame.uv;
  80. const material = icon.getMaterialInstance(0);
  81. if (!material) return;
  82. const nodeInfo = material.getProperty('nodeInfo') as Vec4;
  83. material.setProperty('nodeInfo', new Vec4(target.width, target.height, nodeInfo?.z ?? 1, 0));
  84. // Invert the frame UV basis: bottom-left -> bottom-right / top-left.
  85. // Round corners must follow the node, independently of the cover crop.
  86. const ux = uv[2] - uv[0];
  87. const uy = uv[3] - uv[1];
  88. const vx = uv[4] - uv[0];
  89. const vy = uv[5] - uv[1];
  90. const determinant = ux * vy - uy * vx;
  91. if (Math.abs(determinant) < 1e-12) return;
  92. material.setProperty('nodeUVRowX', new Vec4(
  93. vy / determinant, -vx / determinant,
  94. (vx * uv[1] - vy * uv[0]) / determinant, 0,
  95. ));
  96. material.setProperty('nodeUVRowY', new Vec4(
  97. -uy / determinant, ux / determinant,
  98. (uy * uv[0] - ux * uv[1]) / determinant, 0,
  99. ));
  100. material.setProperty('uvTransform', new Vec4(
  101. frame.rotated ? sy : sx,
  102. frame.rotated ? sx : sy,
  103. (uv[0] + uv[6]) / 2,
  104. (uv[1] + uv[7]) / 2,
  105. ));
  106. }
  107. protected onDestroy(): void {
  108. this.node.off(Node.EventType.SIZE_CHANGED, this.updateIconCover, this);
  109. }
  110. private onClick() {
  111. AppKernel.appLobby.openGame(this._data);
  112. // AppKernel.appLobby.openGame(this._gameConfig.id);
  113. }
  114. private async onBtnFavoClick() {
  115. const gameStore = AppKernel.stores.gameStore;
  116. if (gameStore.favoriting) return;
  117. const index = this._dataIdx;
  118. const data = this._data;
  119. const isFavorite = data.isFavorite;
  120. const game = isFavorite ? await gameStore.reqFavoRemove(data.gameID)
  121. : await gameStore.reqFavoAdd(data.gameID);
  122. if (!!game && this._dataIdx === index) {
  123. this.updateItem(game, index);
  124. }
  125. }
  126. }