|
|
@@ -96,6 +96,9 @@ const main = async () => {
|
|
|
return !!v && v.shadowRoot && v.shadowRoot.childElementCount > 0;
|
|
|
}, null, { timeout: 20000 });
|
|
|
await page.waitForTimeout(600);
|
|
|
+ /* 光场 / 入场动效会让第一轮几何还没稳定:滚动回顶部,动效按系统偏好关掉 */
|
|
|
+ try { await page.waitForFunction(() => window.scrollY === 0, null, { timeout: 3000 }); } catch { await page.evaluate(() => window.scrollTo(0, 0)); }
|
|
|
+ await page.waitForTimeout(200);
|
|
|
};
|
|
|
|
|
|
const DESKTOP = `(() => { ${DEEP}
|
|
|
@@ -115,35 +118,44 @@ const main = async () => {
|
|
|
const cards = [...vr.querySelectorAll('kf-glass-card')].filter((c) => c.getBoundingClientRect().height > 0);
|
|
|
|
|
|
/* ---------- 栅格:找行容器 ---------- */
|
|
|
- const gridRows = [...vr.children].filter((el) => {
|
|
|
+ const gridRows = [...vr.querySelectorAll('*')].filter((el) => {
|
|
|
const s = getComputedStyle(el); return /grid/.test(s.display) && el.querySelector(':scope > kf-glass-card');
|
|
|
}).map((el) => {
|
|
|
const kids = [...el.querySelectorAll(':scope > kf-glass-card')].map((k) => ({ rect: R(k), role: k.dataset.role || '' }));
|
|
|
- return { cls: el.className, cols: getComputedStyle(el).gridTemplateColumns, kids };
|
|
|
+ return { el, cls: el.className, cols: getComputedStyle(el).gridTemplateColumns, kids };
|
|
|
});
|
|
|
const byRole = (r) => gridRows.flatMap((g) => g.kids).find((k) => k.role === r)?.rect || null;
|
|
|
- const A = byRole('a'), B = byRole('b'), C = byRole('c'), D = byRole('d'), E = byRole('e'), F = byRole('f');
|
|
|
+ const A = byRole('a'), B = byRole('b'), C = byRole('c'), D = byRole('d'), E = byRole('e'), F = byRole('f')
|
|
|
+ || (vr.querySelector('kf-glass-card[data-role="f"]') ? R(vr.querySelector('kf-glass-card[data-role="f"]')) : null);
|
|
|
/* 列数:找 A 所在的栅格容器,读它的 grid-template-columns 与 column-gap,算出单列宽,再数 A 占几列 */
|
|
|
const aEl = vr.querySelector('kf-glass-card[data-role="a"]');
|
|
|
- const rowABC = aEl && [...aEl.parentElement.children].find((el) => /grid/.test(getComputedStyle(el).display));
|
|
|
- let gapRow = null, colW = null;
|
|
|
+ /* 直接在已找到的行容器里挑「装着 A 的那一行」——不要 [...vr.children](vr 是 ShadowRoot,children 是 HTMLCollection,
|
|
|
+ 展开后第一项是属性本身,不是元素) */
|
|
|
+ const rowABC = gridRows.find((g) => g.el.contains(aEl))?.el || null;
|
|
|
+ let gapRow = null, colW = null, colDbg = null;
|
|
|
if (rowABC) {
|
|
|
- gapRow = parseFloat(getComputedStyle(rowABC).columnGap) || 0;
|
|
|
- const tracks = getComputedStyle(rowABC).gridTemplateColumns.split(/\\s+/).map(parseFloat);
|
|
|
+ gapRow = parseFloat(getComputedStyle(rowABC).columnGap) || parseFloat(getComputedStyle(rowABC).gap) || 0;
|
|
|
+ const tracks = getComputedStyle(rowABC).gridTemplateColumns.split(/ +/).filter(Boolean).map(parseFloat);
|
|
|
colW = tracks.length ? (rowABC.getBoundingClientRect().width - gapRow * (tracks.length - 1)) / tracks.length : null;
|
|
|
+ colDbg = { n: tracks.length, w: rowABC.getBoundingClientRect().width, gapRow, colW };
|
|
|
}
|
|
|
- const aColsExact = rowABC && rowABC.querySelectorAll(':scope > kf-glass-card').length ? Math.round((rowABC.querySelector(':scope > kf-glass-card').getBoundingClientRect().width + gapRow) / (colW + gapRow)) : null;
|
|
|
+ /* 列数:单列宽 = (行宽 − 间距 × (列数 − 1)) / 列数;A 占几列 = (A 宽 + 间距) / (单列宽 + 间距) */
|
|
|
+ const aColsExact = A && colW ? Math.round((A.w + gapRow) / (colW + gapRow)) : null;
|
|
|
+ const colWDbg = colW;
|
|
|
|
|
|
/* ---------- B 卡深色 ---------- */
|
|
|
- let Bbg = null, BbgText = '', Bcontrast = null;
|
|
|
+ let Bbg = null, BbgText = '', Bcontrast = null, Bother = [];
|
|
|
const bc = cards.find((c) => c.dataset.role === 'b');
|
|
|
if (bc) {
|
|
|
const frame = frameOf(bc);
|
|
|
- Bbg = rgb(getComputedStyle(frame).backgroundColor);
|
|
|
- const texts = [...bc.shadowRoot.querySelectorAll('*')].filter((e) => [...e.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim()));
|
|
|
+ Bbg = rgb(getComputedStyle(frame)?.backgroundColor || '');
|
|
|
+ if (!Bbg) Bbg = paintOf(bc);
|
|
|
+ /* 文本都在 slotted 内容里(bc 的 light DOM),不在它自己的 shadow 内 */
|
|
|
+ const texts = [...bc.querySelectorAll('*')].filter((e) => [...e.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim()));
|
|
|
const worst = texts.map((e) => { const fg = rgb(getComputedStyle(e).color); return { t: e.textContent.trim().slice(0, 16), fg, c: +contrast(fg, Bbg).toFixed(2) }; }).sort((p, q) => p.c - q.c);
|
|
|
Bcontrast = worst[0] || null;
|
|
|
- /* 原始字符串另存,供打印比对 */
|
|
|
+ BbgText = 'rgb(' + Bbg.r + ', ' + Bbg.g + ', ' + Bbg.b + ')';
|
|
|
+ Bother = texts.map((e) => ({ t: e.textContent.trim().slice(0, 16), c: +contrast(rgb(getComputedStyle(e).color), Bbg).toFixed(2) }));
|
|
|
}
|
|
|
|
|
|
/* ---------- 主列直接子块间距 ---------- */
|
|
|
@@ -184,12 +196,12 @@ const main = async () => {
|
|
|
rows: gridRows.map((g) => ({ cls: g.cls, cols: g.cols, kids: g.kids.map((k) => ({ role: k.role, ...k.rect })) })),
|
|
|
A, B, C, D, E, F,
|
|
|
bento: {
|
|
|
- A: A?.w ?? null, B: B?.w ?? null, aCols: aColsExact,
|
|
|
+ A: A?.w ?? null, B: B?.w ?? null, aCols: aColsExact, colDbg,
|
|
|
ABheightDiff: A && B ? +Math.abs(A.h - B.h).toFixed(1) : null,
|
|
|
CDE: C && D && E ? { w: [C.w, D.w, E.w], h: [C.h, D.h, E.h], hDiff: +(Math.max(C.h, D.h, E.h) - Math.min(C.h, D.h, E.h)).toFixed(1) } : null,
|
|
|
F: F || null,
|
|
|
},
|
|
|
- Bbg, BbgText, Bcontrast,
|
|
|
+ Bbg, BbgText, Bcontrast, Bother,
|
|
|
gaps,
|
|
|
facePad,
|
|
|
fonts: { tiers: Object.keys(fsCount).sort((p, q) => parseFloat(p) - parseFloat(q)), count: fsCount, where: Object.fromEntries(Object.entries(fsWhere).map(([k, v]) => [k, [...v]])) },
|
|
|
@@ -389,13 +401,17 @@ const main = async () => {
|
|
|
L(` B 背景 ${desktopDet.BbgText || '(无 data-role="b" 的卡)'}`);
|
|
|
L(` C/D/E ${desktopDet.bento.CDE ? `宽 ${desktopDet.bento.CDE.w.join(' / ')} 高 ${desktopDet.bento.CDE.h.join(' / ')} 高差 ${desktopDet.bento.CDE.hDiff}px` : '(无 data-role=c/d/e 的卡)'}`);
|
|
|
L(` F 通栏 ${desktopDet.bento.F ? `宽 ${desktopDet.bento.F.w}px 高 ${desktopDet.bento.F.h}px` : '(无 data-role="f" 的卡)'}`);
|
|
|
- L(` — 实时数据(页面真身):A ${desktop.bento.A}×? B ${desktop.bento.B}×? 高差 ${desktop.bento.ABheightDiff}px;C/D/E 高差 ${desktop.bento.CDE?.hDiff}px`);
|
|
|
+ L(` A 占 ${desktopDet.bento.aCols} / 12 列`);
|
|
|
+ L(` — 实时数据(页面真身):A ${desktop.bento.A} / B ${desktop.bento.B} 高差 ${desktop.bento.ABheightDiff}px;C/D/E 高差 ${desktop.bento.CDE?.hDiff}px`);
|
|
|
L(` B 背景 ${desktop.BbgText}`);
|
|
|
if (desktop.Bbg) {
|
|
|
const lum = ({ r, g, b }) => { const f = (v) => { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); }; return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b); };
|
|
|
L(` 亮度 ${lum(desktop.Bbg).toFixed(3)} ${lum(desktop.Bbg) < 0.35 ? '✓(<0.35)' : '✗'}`);
|
|
|
}
|
|
|
- if (desktop.Bcontrast) L(` 最差对比度 ${desktop.Bcontrast.c}:1(“${desktop.Bcontrast.t}”) ${desktop.Bcontrast.c >= 7 ? '✓(≥7)' : '✗(要求 ≥7)'}`);
|
|
|
+ if (desktop.Bcontrast) {
|
|
|
+ L(` 最差对比度 ${desktop.Bcontrast.c}:1(“${desktop.Bcontrast.t}”) ${desktop.Bcontrast.c >= 7 ? '✓(≥7)' : '✗(要求 ≥7)'}`);
|
|
|
+ L(` 逐段:${desktop.Bother.map((o) => `${o.c}:1「${o.t}」`).join(' ')}`);
|
|
|
+ }
|
|
|
L(`\n[3] 主列相邻块间距(上→下)`);
|
|
|
for (const g of desktopDet.gaps) L(` ${String(g.gap).padStart(6)}px ${g.between}`);
|
|
|
L(`\n[4] 卡片 .face 内边距`);
|