/////////////////////////////////////////////////////////////// ////////// EQW_Layout: 结算区配置(清单 §6.9 / §5.7a / §5.7b)/// /////////////////////////////////////////////////////////////// // 小局结算 + 冲关遮罩 + 大局总结算 + 出牌历史/亮牌 + 明牌,共五块。 // 【纯数据文件】无函数、无副作用;依赖 LayoutConstants.js 先加载 // (引用其 EQW_Layout.NUM_STYLE.* / CARD_SIZE.*,属 EQW_Layout 内部自引用)。 var EQW_Layout = EQW_Layout || {}; EQW_Layout.RESULT_BOTTOM_PANEL = { kind: 'point', x: 430, y: 140, w: 420, h: 148 }; //底牌说明文字:贴面板底部,纯文字,清单未给宽高 EQW_Layout.RESULT_BOTTOM_TEXT = { kind: 'attach', targetKind: 'layout', target: 'RESULT_BOTTOM_PANEL', hAlign: 'center', vAlign: 'bottom', offsetY: -14, runtime: ['w', 'h'] }; //三家分数光晕 EQW_Layout.RESULT_GLOW = { kind: 'point', w: 225, h: 100, bySeat: { LEFT: { x: 175, y: 150 }, RIGHT: { x: 865, y: 150 }, SELF: { x: 490, y: 340 } } }; //三家总分大字(多帧图数字精灵,非文字精灵,2026-08-27):贴对应光晕居中。 //target 直接指向光晕精灵键(固定,非动态);高度恒为单字高,取 NUM_STYLE.RESULT_WIN.charHeight //(与 NUM_STYLE.RESULT_LOSE 相同,SSOT);宽度随位数变化,留到运行时注入 //节点名去掉 _TEXT 后缀改为 _NUM:与 CALL_BTN_SCORE_NUM 同一理由,_TEXT 会让人误以为是文字精灵 EQW_Layout.RESULT_SCORE_NUM = { kind: 'attach', targetKind: 'sprite', hAlign: 'center', vAlign: 'middle', h: EQW_Layout.NUM_STYLE.RESULT_WIN.charHeight, runtime: ['w'], bySeat: { LEFT: { target: 'RESULT_GLOW_LEFT' }, RIGHT: { target: 'RESULT_GLOW_RIGHT' }, SELF: { target: 'RESULT_GLOW_SELF' } } }; //三家『牌局分』:贴对应光晕左下 EQW_Layout.RESULT_JF_TEXT = { kind: 'attach', targetKind: 'sprite', hAlign: 'left', vAlign: 'bottom', offsetX: -15, offsetY: 24, runtime: ['w', 'h'], bySeat: { LEFT: { target: 'RESULT_GLOW_LEFT' }, RIGHT: { target: 'RESULT_GLOW_RIGHT' }, SELF: { target: 'RESULT_GLOW_SELF' } } }; //三家『冲关分』:贴对应光晕右下 EQW_Layout.RESULT_AW_TEXT = { kind: 'attach', targetKind: 'sprite', hAlign: 'right', vAlign: 'bottom', offsetX: 15, offsetY: 24, runtime: ['w', 'h'], bySeat: { LEFT: { target: 'RESULT_GLOW_LEFT' }, RIGHT: { target: 'RESULT_GLOW_RIGHT' }, SELF: { target: 'RESULT_GLOW_SELF' } } }; EQW_Layout.RESULT_BTN_CG = { kind: 'point', x: 535, y: 515, w: 183, h: 63 }; EQW_Layout.RESULT_BTN_NEXT = { kind: 'point', x: 835, y: 518, w: 180, h: 65 }; //冲关遮罩:透明度也是配置项 EQW_Layout.CG_MASK = { kind: 'point', x: 0, y: 0, w: 1280, h: 720, opacity: 0.72 }; //===== 大局总结算 / 解散结算(AccountView,群组 241)===== // // 【坐标来源 · 2026-08-27 补齐】清单 §6.9 没有本面板的配置表,以下数值全部是 // docs_dev/uiref/大局结算.png 的【目视实测估值】(±5px),面板/标题栏/规则栏/按钮的 w/h // 取清单 §3.5 / §3.3 的资源尺寸(那是权威值)。设计稿到位后只改本节。 // 之所以现在就落成配置:不补的话 C/D/E 阶段写渲染代码时会就地硬编码坐标(零裸值红线)。 // // 【玩家栏是精灵复制】三条栏结构相同、条数随人数变,故不逐行写死,而是给 //「容器 ACC_ROW_CONTAINER + 行节点 ACC_ROW(行高即 itemHeight)+ 行内各模板贴附到行矩形」。 // 约定:ACC_ROW 及贴附到它的 ACC_TPL_* / ACC_ROW_TEXT_GRID 求解出的坐标都是 //【相对 ACC_ROW_CONTAINER 的】——SpriteCopyUtils 复制出的精灵是容器的子精灵, // x/y 本就相对容器,求解结果可直接喂给 SpriteCopyUtils.create(与 §5.6a 冲关牌同一用法)。 // 行数由数据给出(ctx.count),不进配置(清单 §6.1)。 //面板底(1100×540,资源 PANEL_ACCOUNT) EQW_Layout.ACC_PANEL_BG = { kind: 'point', x: 80, y: 88, w: 1100, h: 540 }; //标题栏:封住面板上沿(1100×56,资源 BAR_ACCOUNT_TITLE) EQW_Layout.ACC_TITLE_BAR = { kind: 'attach', targetKind: 'layout', target: 'ACC_PANEL_BG', corner: 'topLeft', offsetX: 0, offsetY: 0, w: 1100, h: 56 }; //标题文字「二七王-3人 房号… 共…局 …」:贴标题栏内部左侧居中。 //文案随房间信息变长变短,真实宽高要等文案渲染出来才知道 → 留到运行时注入 EQW_Layout.ACC_TITLE_TEXT = { kind: 'attach', targetKind: 'layout', target: 'ACC_TITLE_BAR', hAlign: 'left', vAlign: 'middle', offsetX: 30, runtime: ['w', 'h'] }; //关闭按钮:贴标题栏右上角(36×36,资源 BTN_ACC_CLOSE) EQW_Layout.ACC_BTN_CLOSE = { kind: 'attach', targetKind: 'layout', target: 'ACC_TITLE_BAR', corner: 'topRight', offsetX: -20, offsetY: 10, w: 36, h: 36 }; //规则栏:封住面板下沿(1100×62,资源 BAR_ACCOUNT_RULE) EQW_Layout.ACC_RULE_BAR = { kind: 'attach', targetKind: 'layout', target: 'ACC_PANEL_BG', corner: 'bottomLeft', offsetX: 0, offsetY: 0, w: 1100, h: 62 }; //规则文字「规则:可查牌、傍王、爬坡算子」:贴规则栏内部左侧居中,文案据 roomtype 拼出、长度不定 EQW_Layout.ACC_RULE_TEXT = { kind: 'attach', targetKind: 'layout', target: 'ACC_RULE_BAR', hAlign: 'left', vAlign: 'middle', offsetX: 30, runtime: ['w', 'h'] }; //玩家栏容器(ACC_ROW_CONTAINER 精灵的位置):标题栏下沿 144 到规则栏上沿 566 之间 EQW_Layout.ACC_ROW_CONTAINER = { kind: 'point', x: 80, y: 144, w: 1100, h: 422 }; //玩家栏(行):竖排等距,itemHeight 即【行高/行距】。坐标相对容器,行数由 ctx.count 给出 EQW_Layout.ACC_ROW = { kind: 'line', direction: 'vertical', anchorX: 0, anchorY: 14, itemWidth: 1100, itemHeight: 132, spacing: 0, anchor: 'top' }; //行内 · 名次徽章:贴该行内部左侧居中(44×44,资源 BADGE_RANK,帧=名次1–3) EQW_Layout.ACC_TPL_RANK = { kind: 'attach', targetKind: 'layout', target: 'ACC_ROW', hAlign: 'left', vAlign: 'middle', offsetX: 28, w: 44, h: 44 }; //行内 · 头像:贴该行内部左侧居中,名次徽章右侧 EQW_Layout.ACC_TPL_AVATAR = { kind: 'attach', targetKind: 'layout', target: 'ACC_ROW', hAlign: 'left', vAlign: 'middle', offsetX: 92, w: 80, h: 80 }; //行内 · 文字栅格(ACC_TPL_TEXT 复制出的 6 条文字的槽位):4 列 × 2 行,行优先—— // 列1: 昵称 / ID:{playerid} 列2: 基础分{n} / 总得分{n} // 列3: 冲关分{n}(参考图小字写「算奖分」,清单 §1.9 定案字段为 grade_cg_total) // 列4: 傍王分{n} // 第 2 行只有前两列有文字,第 3/4 列留空(求解仍返回 8 个槽位,调用方按数据取前 6 个)。 // anchorX 相对容器(= 参考图列1 绝对 x272 − 容器 x80);anchorY 随所在行浮动 → 运行时注入 // 【估值精度说明】本节点用等距列模型(itemWidth:176 spacingX:0 → 相对容器 192/368/544/720), // 而参考图「大局结算.png」目视实测的列 x 约为 272/455/634/800(列距 183/179/166,并不等距)—— // 与等距模型偏差约 10px,比本文件头部通用声明的「±5px」更大,待设计稿复核(本次未改数值,仅订正估值说明) EQW_Layout.ACC_ROW_TEXT_GRID = { kind: 'grid', anchorX: 192, cols: 4, rows: 2, itemWidth: 176, itemHeight: 34, spacingX: 0, spacingY: 10, anchor: 'left', fillOrder: 'row', runtime: ['anchorY'] }; //行内 · 右侧总分大字(多帧图数字精灵):贴该行内部右侧居中。 //高度恒为单字高,取 NUM_STYLE.RESULT_WIN.charHeight(与 RESULT_LOSE 相同,SSOT); //宽度随位数变化(setNumberImage 按【字符数 × charWidth】自动改精灵宽),留到运行时注入 EQW_Layout.ACC_TPL_SCORE_NUM = { kind: 'attach', targetKind: 'layout', target: 'ACC_ROW', hAlign: 'right', vAlign: 'middle', offsetX: -25, h: EQW_Layout.NUM_STYLE.RESULT_WIN.charHeight, runtime: ['w'] }; //行内 · 行间分隔线:贴该行【下沿】(vAlign bottom 的语义是"目标底边之下",见 AlignmentUtils)。 //1060×2,资源 LINE_DIVIDER;水平居中于 1100 宽的行,左右各留 20 EQW_Layout.ACC_TPL_DIVIDER = { kind: 'attach', targetKind: 'layout', target: 'ACC_ROW', hAlign: 'center', vAlign: 'bottom', offsetX: 0, offsetY: 0, w: 1060, h: 2 }; //面板【外】底部 4 个功能按钮(再来一局/分享好友/复制战绩/退出房间,各 190×60): //绝对坐标(不在玩家栏容器内)。参考图里这排不是居中于屏幕、而是整体偏右,故用 anchor:left 定起点 EQW_Layout.ACC_FUNC_BUTTONS = { kind: 'line', direction: 'horizontal', anchorX: 345, anchorY: 640, itemWidth: 190, itemHeight: 60, spacing: 25, anchor: 'left' }; //===== 出牌历史 / 上一轮 / 亮牌(HistoryView,群组 242)===== // // 【坐标来源 · 2026-08-27 补齐】清单 §5.7b 说本 View 与 MingPaiView、ChongGuanOverlayView // 三者【同一种版式】(半透黑遮罩 + 每家一排重叠大牌),参考图同为 // docs_dev/uiref/冲关牌型显示.png;故以下节点照 CHONGGUAN_FAN / CG_MASK 同构给出, // 三家落点沿用清单 §6.7 的冲关牌锚点(LEFT 260,120 / RIGHT 1035,120 / SELF 640,400)。 // 与冲关牌的唯一差别是【张数上限】:出牌历史摊平后可达 28 张以上,420 的 maxWidth 装不下。 // 2026-08-27 订正:原估值 maxWidth:500 是算错的——_solveFan 的间距公式是 // spacing = max(spacingMin, min(spacingMax, (maxWidth-itemWidth)/(count-1) - itemWidth)) // 代入 maxWidth 500 / itemWidth 110(CARD_SIZE.L.w)/ count 28: // (500-110)/27 - 110 = -95.56,没有触到 spacingMin:-96 的下限,故实际 spacing 是 -95.56, // 而 fan 的总宽恒等于 maxWidth(AlignmentUtils.distributeHorizontally 的 totalWidth 公式代入后 // 可化简为 maxWidth,只要 spacing 没被下限截断)——不是旧注释算的「488」。 // RIGHT 锚点 anchorX:1035、anchor:center 时,总宽 500 使整排右边缘落在 1035+250=1285, // 超出 1280 设计画布 5px(LEFT/SELF 因锚点更靠左,仍在画布内,故此前没暴露)。 // 2026-08-27 再订正(裁定 maxWidth:490 → 480):490 虽能让 RIGHT 恰好贴住 1280,但零余量太脆弱—— // 这些锚点本身是参考图目视实测(±5px 误差),且 490 时 spacingMin:-96 永远不触底、是个死参数, // 与「每张最窄只露 14px」这条约束名存实亡;旧注释原本写的「每张露 14px、总宽 488」正是 // spacingMin 触底后的结果,说明 488 才是当初想要的值,maxWidth:500 是与注释不符的笔误。 // 故改为 maxWidth:480:代入公式 (480-110)/27-110 = -96.30,**触及** spacingMin:-96 下限, // 实际 spacing 被截断为 -96(不再等于 raw),总宽 = 28×110 + 27×(-96) = 3080-2592 = 488(不是 480, // 因为 spacing 被下限截断后总宽不再恒等于 maxWidth)。RIGHT 边缘 = 1035+488/2 = 1279,留 1px 余量; // LEFT 边缘 = 260+244 = 504,SELF 边缘 = 640+244 = 884,均有余量。spacingMin:-96 就此真正生效 // (不再是死参数)。每张露 14px(itemWidth+spacing = 110-96 = 14,与旧注释「14px」精确一致)。 // maxWidth:480 与 spacingMin:-96 仍是【估值,待设计稿复核】——清单未给本 View 的排布参数。 // 【已知限制,留给后来者】每张仅露 14px、视觉偏窄,待设计稿复核是否改为分两排显示, // 或改用 CARD_SIZE.M(更小的牌面)——这是本次估值阶段接受的限制,不是本次要解决的问题。 // // 【牌用精灵复制】求解结果直接作为 SpriteCopyUtils.create 的 x/y(相对容器), // 与 §5.6a 冲关牌同一用法:三个容器精灵摆在原点、尺寸铺满屏,故容器内坐标 = 屏幕坐标。 //出牌历史 · 遮罩(兼点击关闭热区;【亮牌】用途下不注册点击,见 Sprites_Result.js 说明) EQW_Layout.HIST_MASK = { kind: 'point', x: 0, y: 0, w: 1280, h: 720, opacity: 0.72 }; //出牌历史 · 三个牌容器(HIST_BOX_LEFT/RIGHT/SELF):同摆在原点、铺满屏, //使 HIST_FAN 求出的屏幕坐标可直接当作容器内坐标使用 EQW_Layout.HIST_BOX = { kind: 'point', x: 0, y: 0, w: 1280, h: 720 }; //出牌历史 · 每家一排牌(张数不定,重叠压缩) EQW_Layout.HIST_FAN = { kind: 'fan', direction: 'horizontal', anchor: 'center', itemWidth: EQW_Layout.CARD_SIZE.L.w, itemHeight: EQW_Layout.CARD_SIZE.L.h, maxWidth: 480, spacingMax: -40, spacingMin: -96, bySeat: { LEFT: { anchorX: 260, anchorY: 120 }, RIGHT: { anchorX: 1035, anchorY: 120 }, SELF: { anchorX: 640, anchorY: 400 } } }; //出牌历史 · 三家座位标签(昵称/张数):各排牌下方;文案长度不定,w/h 运行时注入 EQW_Layout.HIST_LABEL = { kind: 'point', runtime: ['w', 'h'], bySeat: { LEFT: { x: 200, y: 318 }, RIGHT: { x: 975, y: 318 }, SELF: { x: 580, y: 598 } } }; //===== 明牌面板(MingPaiView,群组 243)===== // // 版式与 HistoryView 完全相同,差别只在【家数】:明牌只看另两家,没有自己那排, // 故 bySeat 只有 LEFT / RIGHT 两个变体(精灵键为 MING_BOX_A / _B,A 落 LEFT 位、B 落 RIGHT 位)。 // 坐标来源同上:冲关牌型显示.png 目视实测 + §6.7 冲关牌锚点,【估值,待设计稿复核】。 EQW_Layout.MING_MASK = { kind: 'point', x: 0, y: 0, w: 1280, h: 720, opacity: 0.72 }; EQW_Layout.MING_BOX = { kind: 'point', x: 0, y: 0, w: 1280, h: 720 }; EQW_Layout.MING_FAN = { kind: 'fan', direction: 'horizontal', anchor: 'center', itemWidth: EQW_Layout.CARD_SIZE.L.w, itemHeight: EQW_Layout.CARD_SIZE.L.h, maxWidth: 480, spacingMax: -40, spacingMin: -96, bySeat: { LEFT: { anchorX: 260, anchorY: 120 }, RIGHT: { anchorX: 1035, anchorY: 120 } } }; EQW_Layout.MING_LABEL = { kind: 'point', runtime: ['w', 'h'], bySeat: { LEFT: { x: 200, y: 318 }, RIGHT: { x: 975, y: 318 } } };