(rebase) avoid dist and binary
This commit is contained in:
248
src/utils/CoordinateMapper.ts
Normal file
248
src/utils/CoordinateMapper.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
/**
|
||||
* 坐标映射工具类 - 用于测试和验证坐标转换的准确性
|
||||
*/
|
||||
|
||||
interface DeviceInfo {
|
||||
density: number
|
||||
densityDpi: number
|
||||
hasNavigationBar: boolean
|
||||
aspectRatio: number
|
||||
realScreenSize: { width: number; height: number }
|
||||
appScreenSize: { width: number; height: number }
|
||||
navigationBarSize: { width: number; height: number }
|
||||
}
|
||||
|
||||
interface CoordinateTestResult {
|
||||
success: boolean
|
||||
accuracy: number // 准确度百分比
|
||||
avgError: number // 平均误差(像素)
|
||||
maxError: number // 最大误差(像素)
|
||||
testPoints: Array<{
|
||||
input: { x: number; y: number }
|
||||
expected: { x: number; y: number }
|
||||
actual: { x: number; y: number }
|
||||
error: number
|
||||
}>
|
||||
}
|
||||
|
||||
export class CoordinateMapper {
|
||||
/**
|
||||
* 测试坐标映射准确性
|
||||
*/
|
||||
static testCoordinateMapping(
|
||||
deviceWidth: number,
|
||||
deviceHeight: number,
|
||||
displayWidth: number,
|
||||
displayHeight: number,
|
||||
deviceInfo?: DeviceInfo
|
||||
): CoordinateTestResult {
|
||||
console.log('🧪 开始坐标映射准确性测试')
|
||||
|
||||
// 生成测试点
|
||||
const testPoints = [
|
||||
// 屏幕角落
|
||||
{ x: 0, y: 0 },
|
||||
{ x: deviceWidth - 1, y: 0 },
|
||||
{ x: 0, y: deviceHeight - 1 },
|
||||
{ x: deviceWidth - 1, y: deviceHeight - 1 },
|
||||
|
||||
// 屏幕中心
|
||||
{ x: deviceWidth / 2, y: deviceHeight / 2 },
|
||||
|
||||
// 常见按钮位置
|
||||
{ x: deviceWidth * 0.8, y: deviceHeight * 0.9 }, // 右下角按钮
|
||||
{ x: deviceWidth * 0.5, y: deviceHeight * 0.8 }, // 底部中央按钮
|
||||
{ x: deviceWidth * 0.2, y: deviceHeight * 0.1 }, // 左上角按钮
|
||||
|
||||
// 键盘区域
|
||||
{ x: deviceWidth * 0.25, y: deviceHeight * 0.6 },
|
||||
{ x: deviceWidth * 0.5, y: deviceHeight * 0.6 },
|
||||
{ x: deviceWidth * 0.75, y: deviceHeight * 0.6 },
|
||||
|
||||
// 导航栏区域(如果有)
|
||||
...(deviceInfo?.hasNavigationBar ? [
|
||||
{ x: deviceWidth * 0.2, y: deviceHeight + 50 },
|
||||
{ x: deviceWidth * 0.5, y: deviceHeight + 50 },
|
||||
{ x: deviceWidth * 0.8, y: deviceHeight + 50 }
|
||||
] : [])
|
||||
]
|
||||
|
||||
const results: CoordinateTestResult['testPoints'] = []
|
||||
let totalError = 0
|
||||
let maxError = 0
|
||||
|
||||
for (const point of testPoints) {
|
||||
// 模拟正向转换:设备坐标 → 显示坐标
|
||||
const displayCoords = this.deviceToDisplay(point.x, point.y, deviceWidth, deviceHeight, displayWidth, displayHeight)
|
||||
|
||||
// 模拟反向转换:显示坐标 → 设备坐标
|
||||
const backToDevice = this.displayToDevice(displayCoords.x, displayCoords.y, deviceWidth, deviceHeight, displayWidth, displayHeight, deviceInfo)
|
||||
|
||||
// 计算误差
|
||||
const error = Math.sqrt(Math.pow(point.x - backToDevice.x, 2) + Math.pow(point.y - backToDevice.y, 2))
|
||||
totalError += error
|
||||
maxError = Math.max(maxError, error)
|
||||
|
||||
results.push({
|
||||
input: point,
|
||||
expected: point,
|
||||
actual: backToDevice,
|
||||
error
|
||||
})
|
||||
}
|
||||
|
||||
const avgError = totalError / testPoints.length
|
||||
const accuracy = Math.max(0, 100 - (avgError / Math.min(deviceWidth, deviceHeight) * 100))
|
||||
|
||||
const result: CoordinateTestResult = {
|
||||
success: avgError < 5, // 平均误差小于5像素认为成功
|
||||
accuracy,
|
||||
avgError,
|
||||
maxError,
|
||||
testPoints: results
|
||||
}
|
||||
|
||||
console.log('🧪 坐标映射测试结果:', {
|
||||
success: result.success,
|
||||
accuracy: `${accuracy.toFixed(2)}%`,
|
||||
avgError: `${avgError.toFixed(2)}px`,
|
||||
maxError: `${maxError.toFixed(2)}px`,
|
||||
testPointsCount: testPoints.length
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备坐标转换为显示坐标
|
||||
*/
|
||||
private static deviceToDisplay(
|
||||
deviceX: number,
|
||||
deviceY: number,
|
||||
deviceWidth: number,
|
||||
deviceHeight: number,
|
||||
displayWidth: number,
|
||||
displayHeight: number
|
||||
): { x: number; y: number } {
|
||||
const scaleX = displayWidth / deviceWidth
|
||||
const scaleY = displayHeight / deviceHeight
|
||||
const scale = Math.min(scaleX, scaleY)
|
||||
|
||||
const actualDisplayWidth = deviceWidth * scale
|
||||
const actualDisplayHeight = deviceHeight * scale
|
||||
const offsetX = (displayWidth - actualDisplayWidth) / 2
|
||||
const offsetY = (displayHeight - actualDisplayHeight) / 2
|
||||
|
||||
return {
|
||||
x: deviceX * scale + offsetX,
|
||||
y: deviceY * scale + offsetY
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示坐标转换为设备坐标(增强版本)
|
||||
*/
|
||||
private static displayToDevice(
|
||||
displayX: number,
|
||||
displayY: number,
|
||||
deviceWidth: number,
|
||||
deviceHeight: number,
|
||||
displayWidth: number,
|
||||
displayHeight: number,
|
||||
deviceInfo?: DeviceInfo
|
||||
): { x: number; y: number } {
|
||||
const scaleX = displayWidth / deviceWidth
|
||||
const scaleY = displayHeight / deviceHeight
|
||||
const scale = Math.min(scaleX, scaleY)
|
||||
|
||||
const actualDisplayWidth = deviceWidth * scale
|
||||
const actualDisplayHeight = deviceHeight * scale
|
||||
const offsetX = (displayWidth - actualDisplayWidth) / 2
|
||||
const offsetY = (displayHeight - actualDisplayHeight) / 2
|
||||
|
||||
const adjustedX = displayX - offsetX
|
||||
const adjustedY = displayY - offsetY
|
||||
|
||||
let deviceX = adjustedX / scale
|
||||
let deviceY = adjustedY / scale
|
||||
|
||||
// 应用设备特性修正
|
||||
if (deviceInfo) {
|
||||
const density = deviceInfo.density
|
||||
if (density > 2) {
|
||||
deviceX = Math.round(deviceX * 10) / 10
|
||||
deviceY = Math.round(deviceY * 10) / 10
|
||||
} else {
|
||||
deviceX = Math.round(deviceX)
|
||||
deviceY = Math.round(deviceY)
|
||||
}
|
||||
|
||||
// 导航栏区域修正
|
||||
if (deviceInfo.hasNavigationBar && deviceInfo.navigationBarSize.height > 0) {
|
||||
const navBarHeight = deviceInfo.navigationBarSize.height
|
||||
const threshold = deviceHeight - navBarHeight
|
||||
if (deviceY > threshold) {
|
||||
deviceY = Math.min(deviceY, deviceHeight + Math.min(navBarHeight, 150))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
x: Math.max(0, Math.min(deviceWidth, deviceX)),
|
||||
y: Math.max(0, Math.min(deviceHeight, deviceY))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成坐标映射诊断报告
|
||||
*/
|
||||
static generateDiagnosticReport(
|
||||
deviceWidth: number,
|
||||
deviceHeight: number,
|
||||
displayWidth: number,
|
||||
displayHeight: number,
|
||||
deviceInfo?: DeviceInfo
|
||||
): string {
|
||||
const testResult = this.testCoordinateMapping(deviceWidth, deviceHeight, displayWidth, displayHeight, deviceInfo)
|
||||
|
||||
let report = `📊 坐标映射诊断报告\n`
|
||||
report += `==========================================\n`
|
||||
report += `设备分辨率: ${deviceWidth}x${deviceHeight}\n`
|
||||
report += `显示分辨率: ${displayWidth}x${displayHeight}\n`
|
||||
report += `设备信息: ${deviceInfo ? '已提供' : '未提供'}\n`
|
||||
report += `\n`
|
||||
report += `测试结果:\n`
|
||||
report += `- 测试状态: ${testResult.success ? '✅ 通过' : '❌ 失败'}\n`
|
||||
report += `- 准确度: ${testResult.accuracy.toFixed(2)}%\n`
|
||||
report += `- 平均误差: ${testResult.avgError.toFixed(2)}px\n`
|
||||
report += `- 最大误差: ${testResult.maxError.toFixed(2)}px\n`
|
||||
report += `- 测试点数: ${testResult.testPoints.length}\n`
|
||||
report += `\n`
|
||||
|
||||
if (!testResult.success) {
|
||||
report += `❌ 问题点分析:\n`
|
||||
testResult.testPoints
|
||||
.filter(p => p.error > 5)
|
||||
.forEach((p, i) => {
|
||||
report += ` ${i + 1}. 输入(${p.input.x.toFixed(1)}, ${p.input.y.toFixed(1)}) → 输出(${p.actual.x.toFixed(1)}, ${p.actual.y.toFixed(1)}) 误差: ${p.error.toFixed(2)}px\n`
|
||||
})
|
||||
}
|
||||
|
||||
if (deviceInfo) {
|
||||
report += `\n📱 设备特征:\n`
|
||||
report += `- 密度: ${deviceInfo.density}\n`
|
||||
report += `- DPI: ${deviceInfo.densityDpi}\n`
|
||||
report += `- 长宽比: ${deviceInfo.aspectRatio.toFixed(3)}\n`
|
||||
report += `- 虚拟按键: ${deviceInfo.hasNavigationBar ? '是' : '否'}\n`
|
||||
if (deviceInfo.hasNavigationBar) {
|
||||
report += `- 导航栏尺寸: ${deviceInfo.navigationBarSize.width}x${deviceInfo.navigationBarSize.height}\n`
|
||||
}
|
||||
}
|
||||
|
||||
report += `==========================================\n`
|
||||
|
||||
return report
|
||||
}
|
||||
}
|
||||
|
||||
export default CoordinateMapper
|
||||
Reference in New Issue
Block a user