const { ccclass, property, executeInEditMode } = cc._decorator; /** Authored number artwork for integer counts and fractions (optional slash frame). */ @ccclass @executeInEditMode export default class CloudRiseCounter extends cc.Component { @property([cc.SpriteFrame]) frames: cc.SpriteFrame[] = []; @property value = "0/5"; @property glyphHeight = 56; @property usePrefabLayout = false; get string(): string { return this.value; } set string(value: string) { this.value = value; this.render(); } onEnable(): void { this.render(); } private render(): void { if (![10, 11].includes(this.frames.length) || this.frames.some(frame => !frame)) return; const chars = this.value.split(""); if (chars.some(char => !/^[0-9/]$/.test(char) || char === "/" && !this.frames[10])) return; if (this.usePrefabLayout) { // Keep the designer's per-slot positions and Sprite size modes when updating the value. this.node.children.forEach((node, i) => { node.active = i < chars.length; if (node.active) node.getComponent(cc.Sprite).spriteFrame = this.frames[chars[i] === "/" ? 10 : Number(chars[i])]; }); return; } const widths = chars.map(char => { const rect = this.frames[char === "/" ? 10 : Number(char)].getRect(); return this.glyphHeight * rect.width / rect.height; }); const total = widths.reduce((a, b) => a + b, 0) + Math.max(0, chars.length - 1) * 2; const scale = Math.min(1, this.node.width / total); let x = -total / 2; this.node.children.forEach((node, i) => { node.active = i < chars.length; if (!node.active) return; const sprite = node.getComponent(cc.Sprite); sprite.spriteFrame = this.frames[chars[i] === "/" ? 10 : Number(chars[i])]; sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM; node.setContentSize(widths[i] * scale, this.glyphHeight * scale); node.setPosition((x + widths[i] / 2) * scale, 0); x += widths[i] + 2; }); } }