/** * 跨 ±180 经线真机复验:维护者反馈「到达经度 0 的区域时会抽搐后旋转一周到达另一边」。 * 在真机上把中心移到 179.x,再连续东西向拖拽跨过 180,观察经度是否绕远路。 * * 运行(需先起 `yarn dev`):node .scratch/cockpit-shell-panel-layout/verify/t03-antimeridian-walkthrough.mjs */ import { join } from 'node:path' import { tmpdir } from 'node:os' const BASE = process.env.COCKPIT_BASE || 'http://localhost:8000' const CHROME = process.env.CHROME_PATH || 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' const PUPPETEER = 'file:///C:/Users/ASUS/.dsh/profiles/desktop/node_modules/puppeteer-core/lib/puppeteer/puppeteer-core.js' const puppeteer = (await import(PUPPETEER)).default const browser = await puppeteer.launch({ executablePath: CHROME, headless: true, userDataDir: join(tmpdir(), 'cockpit-t03-am-profile'), defaultViewport: { width: 1680, height: 950 }, args: ['--no-sandbox', '--disable-dev-shm-usage', '--no-first-run'], }) const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) const shortest = (a, b) => ((b - a + 540) % 360) - 180 try { const page = await browser.newPage() const errors = [] page.on('pageerror', (e) => errors.push(String((e && e.message) || e))) await page.goto(`${BASE}/#/cockpit`, { waitUntil: 'networkidle2', timeout: 60000 }) await page.waitForSelector('.cockpit-shell', { timeout: 20000 }) await page.waitForFunction(() => !!window.__cockpitMap, { timeout: 30000 }) await page .waitForFunction(() => document.querySelector('.cockpit-shell__map .map-canvas')?.dataset.mapEngine === 'ready', { timeout: 60000, }) .catch(() => {}) await sleep(1000) const read = () => page.evaluate(() => { const s = window.__cockpitMap.getCameraState() return { pivot: s.pivot, shown: s.shownPivot, hpr: s.hpr, settled: s.settled } }) // 让相机离地面近一点,跨经线的步长才不至于一次跳过大半圈。 await page.evaluate(() => window.__cockpitMap.setCameraDamping(110)) await page.evaluate(() => window.__cockpitMap.setCameraFocus({ lon: 179.0, lat: 10 })) await page.waitForFunction(() => window.__cockpitMap.getCameraState().settled === true, { timeout: 5000 }).catch(() => {}) await sleep(600) const before = await read() console.log('起点:', JSON.stringify({ pivot: before.pivot, r: Math.round(before.hpr.range) })) const canvasCenter = await page.evaluate(() => { const b = document.querySelector('.cockpit-shell__map .map-canvas').getBoundingClientRect() return { x: b.x + b.width / 2, y: b.y + b.height / 2 } }) // 连续向右拖(抓着地图拖 → 中心西移 → 经度增大,会跨过 ±180)。 const lons = [] await page.mouse.move(canvasCenter.x, canvasCenter.y) await page.mouse.down({ button: 'left' }) for (let i = 1; i <= 10; i += 1) { await page.mouse.move(canvasCenter.x - i * 60, canvasCenter.y, { steps: 3 }) await sleep(60) const s = await read() lons.push(s.pivot.lon) } await page.mouse.up({ button: 'left' }) await sleep(1200) // 解缠:从**起点**开始按最短弧累加,得出真实走过的经度总量。 // 关键判据:若跨 ±180 走了长边,单步增量会接近 ±360 的整数倍,且方向会翻转。 const track = [before.pivot.lon, ...lons] let acc = 0 const arc = [0] for (let i = 1; i < track.length; i += 1) { acc += shortest(track[i - 1], track[i]) arc.push(acc) } console.log('落点经度:', lons.map((v) => v.toFixed(3)).join(' → ')) console.log('弧长累加:', arc.map((v) => v.toFixed(3)).join(' → ')) console.log('总弧长 °:', arc[arc.length - 1].toFixed(3), '(应与拖拽量级相符,不是 ±360 的整数倍)') console.log('单调推进:', arc.every((v, i) => i === 0 || v >= arc[i - 1] - 1e-9)) const overshoot = Math.abs(acc - shortest(track[0], track[track.length - 1])) console.log('与首尾最短弧之差 °:', overshoot.toFixed(3), '(≈0 说明全程没绕远路)') console.log('终点:', JSON.stringify(await read())) console.log('页面报错:', errors.length ? errors.slice(0, 3).join(' | ') : '(无)') } finally { await browser.close() }