107 lines
3.6 KiB
JavaScript
107 lines
3.6 KiB
JavaScript
import { chromium } from '@playwright/test'
|
|
|
|
/**
|
|
* 定位 h=80 时底部那 4 个连续深色像素来自哪个元素。
|
|
* 做法:用一个只在上方有元素的地方做"逐元素排除"——逐个隐藏候选元素,看哪一条消失。
|
|
*/
|
|
const BASE = process.env.PROBE_URL || 'http://localhost:5200'
|
|
const browser = await chromium.launch()
|
|
const page = await browser.newPage({ viewport: { width: 1400, height: 1200 } })
|
|
await page.goto(BASE, { waitUntil: 'networkidle' })
|
|
|
|
const clipSample = async (x, y, w, h) => {
|
|
const shot = await page.screenshot({ clip: { x, y, width: w, height: h } })
|
|
return page.evaluate(async (dataUrl) => {
|
|
const img = new Image()
|
|
await new Promise((res) => {
|
|
img.onload = res
|
|
img.src = dataUrl
|
|
})
|
|
const cv = document.createElement('canvas')
|
|
cv.width = img.width
|
|
cv.height = img.height
|
|
const ctx = cv.getContext('2d')
|
|
ctx.drawImage(img, 0, 0)
|
|
const d = ctx.getImageData(0, 0, img.width, img.height).data
|
|
const out = []
|
|
for (let i = 0; i < d.length; i += 4) out.push([d[i], d[i + 1], d[i + 2]])
|
|
return out
|
|
}, `data:image/png;base64,${shot.toString('base64')}`)
|
|
}
|
|
|
|
const vec = async (x, y) => {
|
|
const px = await clipSample(x, y, 1, 10)
|
|
return px.map(([r, g, b]) => (r < 246 || g < 246 || b < 246 ? 1 : 0)).join('')
|
|
}
|
|
|
|
// 压到 80px
|
|
await page.evaluate(() => {
|
|
document.querySelector('[data-testid="table-wrap"]').style.height = '80px'
|
|
})
|
|
await page.waitForTimeout(600)
|
|
await page.locator('[data-testid="table-wrap"]').scrollIntoViewIfNeeded()
|
|
await page.waitForTimeout(200)
|
|
|
|
const geo = await page.evaluate(() => {
|
|
const wrap = document.querySelector('[data-testid="table-wrap"]')
|
|
const root = wrap.querySelector('.g3-table')
|
|
const r = root.getBoundingClientRect()
|
|
const inner = wrap.querySelector('.stk-table')
|
|
const tbody = wrap.querySelector('.stk-tbody-main')
|
|
return {
|
|
left: Math.round(r.left),
|
|
rootRect: { top: Math.round(r.top), bottom: Math.round(r.bottom) },
|
|
innerRect: (() => {
|
|
const x = inner.getBoundingClientRect()
|
|
return { top: Math.round(x.top), bottom: Math.round(x.bottom), scrollH: inner.scrollHeight, clientH: inner.clientHeight }
|
|
})(),
|
|
tbodyRect: (() => {
|
|
const x = tbody.getBoundingClientRect()
|
|
return { top: Math.round(x.top), bottom: Math.round(x.bottom) }
|
|
})(),
|
|
}
|
|
})
|
|
|
|
console.log('=== h=80 几何 ===')
|
|
console.log(JSON.stringify(geo, null, 2))
|
|
|
|
const x = geo.left + 300
|
|
const base = await vec(x, geo.rootRect.bottom - 4)
|
|
console.log(`\n基线采样(root.bottom-4 起 10 行): ${base}`)
|
|
|
|
// 逐元素隐藏,看哪一次让多余的线消失
|
|
const candidates = [
|
|
['tbody 最后一行', '.stk-tbody-main tr:last-child'],
|
|
['tbody', '.stk-tbody-main'],
|
|
['thead', 'thead'],
|
|
['stk-table 根', '.stk-table'],
|
|
]
|
|
for (const [name, sel] of candidates) {
|
|
const v = await page.evaluate(
|
|
({ sel, y, x }) => {
|
|
const wrap = document.querySelector('[data-testid="table-wrap"]')
|
|
const el = wrap.querySelector(sel)
|
|
if (!el) return 'not-found'
|
|
const prev = el.style.visibility
|
|
el.style.visibility = 'hidden'
|
|
return 'hidden'
|
|
},
|
|
{ sel, y: geo.rootRect.bottom - 4, x },
|
|
)
|
|
if (v === 'not-found') {
|
|
console.log(`隐藏 ${name}: 元素不存在`)
|
|
continue
|
|
}
|
|
await page.waitForTimeout(150)
|
|
const after = await vec(x, geo.rootRect.bottom - 4)
|
|
console.log(`隐藏 ${name.padEnd(16)} → ${after} ${after !== base ? '← 变化' : ''}`)
|
|
await page.evaluate((sel) => {
|
|
const wrap = document.querySelector('[data-testid="table-wrap"]')
|
|
const el = wrap.querySelector(sel)
|
|
if (el) el.style.visibility = ''
|
|
}, sel)
|
|
await page.waitForTimeout(120)
|
|
}
|
|
|
|
await browser.close()
|