71 lines
4.4 KiB
TypeScript
71 lines
4.4 KiB
TypeScript
/** One owner for home activity positions; visibility is still decided by each activity. */
|
|
export default class HomeActivityEntryLayout {
|
|
private static signatures = new WeakMap<cc.Node, string>();
|
|
private static leftNames = ["shop", "yicon", "redeemBtn", "passBtn", "jungle", "xinshou"];
|
|
private static rightNames = ["rank", "gacha", "day", "hammer"];
|
|
|
|
static refresh(top: cc.Node): void {
|
|
if (!top || !top.parent) return;
|
|
const first = top.getChildByName("shop"), right = top.getChildByName("rank");
|
|
const footer = top.parent.getChildByName("shezhiBtn");
|
|
if (!first || !right || !footer) return;
|
|
const left = this.leftNames.map(name => top.getChildByName(name)).filter(node => !!node);
|
|
const rightActivities = ["cloudRise", "chengxu"].map(name => top.getChildByName(name)).filter(node => !!node);
|
|
const fixedRight = this.rightNames.map(name => top.getChildByName(name)).filter(node => !!node);
|
|
// Adaptation sets the same steady scale on Load and the side buttons. Ignore temporary button press zoom.
|
|
const scale = Math.abs(top.parent.scaleY) || 1;
|
|
const signature = JSON.stringify([first.x, first.y, right.x, top.y, scale, footer.y, footer.height,
|
|
...left.map(n => [n.name, n.active, n.width, n.height]),
|
|
...rightActivities.map(n => [n.name, n.active, n.width, n.height]),
|
|
...fixedRight.map(n => [n.name, n.active, n.x, n.y, n.width, n.height])]);
|
|
if (this.signatures.get(top) === signature) return;
|
|
this.signatures.set(top, signature);
|
|
|
|
const footerTop = footer.y + footer.height * (1 - footer.anchorY) * scale - top.y + 16;
|
|
const visibleLeft = left.filter(n => n.active);
|
|
const leftBounds = visibleLeft.map(n => this.extent(n));
|
|
const minimumSteps = leftBounds.slice(1).map((bounds, i) =>
|
|
(bounds.top - leftBounds[i].bottom) * scale + 16);
|
|
const minimumSpan = minimumSteps.reduce((sum, step) => sum + step, 0);
|
|
const extraSpan = minimumSteps.reduce((sum, step) => sum + Math.max(0, 220 - step), 0);
|
|
const availableSpan = leftBounds.length ? first.y + leftBounds[leftBounds.length - 1].bottom * scale - footerTop : 0;
|
|
// Tighten only the spare gaps when all left entries are visible, keeping the bottom controls clear.
|
|
const gapRatio = extraSpan ? Math.max(0, Math.min(1, (availableSpan - minimumSpan) / extraSpan)) : 1;
|
|
let leftY = first.y;
|
|
const occupiedRight = fixedRight.filter(n => n.active);
|
|
let rightY = occupiedRight.length ? Math.min(...occupiedRight.map(n => n.y)) : first.y + 220;
|
|
let rightBottom = occupiedRight.length
|
|
? Math.min(...occupiedRight.map(n => n.y + this.extent(n).bottom * scale)) : Infinity;
|
|
for (const node of rightActivities.filter(n => n.active)) {
|
|
const bounds = this.extent(node);
|
|
rightY = Math.min(rightY - 220, rightBottom - 16 - bounds.top * scale);
|
|
node.setPosition(right.x, rightY);
|
|
rightBottom = rightY + bounds.bottom * scale;
|
|
}
|
|
for (let i = 0; i < visibleLeft.length; i++) {
|
|
const node = visibleLeft[i], extent = leftBounds[i], above = extent.top * scale, below = extent.bottom * scale;
|
|
const step = i ? minimumSteps[i - 1] : 0;
|
|
const y = i ? leftY - step - Math.max(0, 220 - step) * gapRatio : first.y;
|
|
if (y + below >= footerTop) {
|
|
node.setPosition(first.x, y); leftY = y;
|
|
} else {
|
|
// Use the free lower right slots instead of covering the bottom-left Settings button.
|
|
const nextY = Math.min(rightY - 220, rightBottom - 16 - above);
|
|
node.setPosition(right.x, nextY); rightY = nextY; rightBottom = nextY + below;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Include clocks and labels below icons so the next entry cannot cover them. */
|
|
private static extent(node: cc.Node): { top: number; bottom: number } {
|
|
let top = node.height * (1 - node.anchorY), bottom = -node.height * node.anchorY;
|
|
for (const child of node.children) {
|
|
if (!child.active) continue;
|
|
const bounds = this.extent(child), scale = Math.abs(child.scaleY);
|
|
top = Math.max(top, child.y + bounds.top * scale);
|
|
bottom = Math.min(bottom, child.y + bounds.bottom * scale);
|
|
}
|
|
return { top, bottom };
|
|
}
|
|
}
|