feat(ui-migration): 坐标换算与水平锚点三区推断
左上原点/左上锚点 → Cocos 中心原点/中心锚点(0.5,0.5)。 fitHeight 下垂直永不溢出, 故只推断水平锚点。 背景四边拉伸; 越界对象只标记不裁剪。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import {
|
||||||
|
DESIGN_WIDTH, DESIGN_HEIGHT,
|
||||||
|
toCocosPosition, inferWidget, stretchWidget, isFullScreen, isOffscreen,
|
||||||
|
} from '../ui-migration/coords.mjs';
|
||||||
|
|
||||||
|
test('设计分辨率保持 1280x720', () => {
|
||||||
|
assert.equal(DESIGN_WIDTH, 1280);
|
||||||
|
assert.equal(DESIGN_HEIGHT, 720);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toCocosPosition 用 spec 的验算样例', () => {
|
||||||
|
// spec §1.2: Left=320 Top=200 150x70 → x=-245 y=125
|
||||||
|
assert.deepEqual(toCocosPosition(320, 200, 150, 70), { x: -245, y: 125 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toCocosPosition 左上角对象落在画布左上', () => {
|
||||||
|
// 0,0 的 100x50 → 中心在 (50,25) → x=50-640=-590, y=360-25=335
|
||||||
|
assert.deepEqual(toCocosPosition(0, 0, 100, 50), { x: -590, y: 335 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toCocosPosition 满屏对象落在原点', () => {
|
||||||
|
assert.deepEqual(toCocosPosition(0, 0, 1280, 720), { x: 0, y: 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inferWidget 中心点在左区 → 锚左', () => {
|
||||||
|
// cx = 10+100/2 = 60 < 320
|
||||||
|
assert.deepEqual(inferWidget(10, 100), { horizontal: 'left', left: 10 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inferWidget 中心点在右区 → 锚右', () => {
|
||||||
|
// cx = 1100+100/2 = 1150 > 960;right = 1280 - 1200 = 80
|
||||||
|
assert.deepEqual(inferWidget(1100, 100), { horizontal: 'right', right: 80 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inferWidget 中心点在中区 → 水平居中', () => {
|
||||||
|
// cx = 320+150/2 = 395 ∈ [320,960];horizontalCenter = 395-640 = -245
|
||||||
|
assert.deepEqual(inferWidget(320, 150), { horizontal: 'center', horizontalCenter: -245 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inferWidget 分界值 320 与 960 归入中区(非严格小于/大于才算边区)', () => {
|
||||||
|
// cx 恰为 320:不满足 < 320 → 中区
|
||||||
|
assert.equal(inferWidget(220, 200).horizontal, 'center');
|
||||||
|
// cx 恰为 960:不满足 > 960 → 中区
|
||||||
|
assert.equal(inferWidget(860, 200).horizontal, 'center');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inferWidget 屏外负坐标自然落入左区', () => {
|
||||||
|
// cx = -286+100/2 = -236 < 320
|
||||||
|
assert.equal(inferWidget(-286, 100).horizontal, 'left');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stretchWidget 四边全 0', () => {
|
||||||
|
assert.deepEqual(stretchWidget(), {
|
||||||
|
horizontal: 'stretch', vertical: 'stretch', left: 0, right: 0, top: 0, bottom: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isFullScreen 只认 >= 设计分辨率', () => {
|
||||||
|
assert.equal(isFullScreen(1280, 720), true);
|
||||||
|
assert.equal(isFullScreen(1300, 800), true);
|
||||||
|
assert.equal(isFullScreen(1279, 720), false);
|
||||||
|
assert.equal(isFullScreen(1280, 719), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isOffscreen 识别越界对象', () => {
|
||||||
|
assert.equal(isOffscreen(0, 0, 1280, 720), false, '恰好满屏不算越界');
|
||||||
|
assert.equal(isOffscreen(-286, 100, 50, 50), true, '左越界');
|
||||||
|
assert.equal(isOffscreen(100, -318, 50, 50), true, '上越界');
|
||||||
|
assert.equal(isOffscreen(1260, 100, 50, 50), true, '右越界');
|
||||||
|
assert.equal(isOffscreen(100, 700, 50, 50), true, '下越界');
|
||||||
|
});
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* 坐标换算与锚点推断。见 spec §2。
|
||||||
|
* 旧引擎:原点左上、Y 向下、精灵锚点左上。
|
||||||
|
* Cocos:原点在父节点中心、Y 向上、节点锚点 (0.5, 0.5)。
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const DESIGN_WIDTH = 1280;
|
||||||
|
export const DESIGN_HEIGHT = 720;
|
||||||
|
|
||||||
|
const HALF_W = DESIGN_WIDTH / 2;
|
||||||
|
const HALF_H = DESIGN_HEIGHT / 2;
|
||||||
|
const LEFT_ZONE = DESIGN_WIDTH / 4; // 320
|
||||||
|
const RIGHT_ZONE = (DESIGN_WIDTH * 3) / 4; // 960
|
||||||
|
|
||||||
|
/** 左上坐标 → Cocos 中心锚点坐标。spec §2.2。 */
|
||||||
|
export function toCocosPosition(left, top, width, height) {
|
||||||
|
return {
|
||||||
|
x: left + width / 2 - HALF_W,
|
||||||
|
y: HALF_H - top - height / 2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 水平锚点三区推断。spec §2.3。
|
||||||
|
* fitHeight 下垂直方向永远精确撑满,故不推断垂直锚点。
|
||||||
|
*/
|
||||||
|
export function inferWidget(left, width) {
|
||||||
|
const cx = left + width / 2;
|
||||||
|
if (cx < LEFT_ZONE) return { horizontal: 'left', left };
|
||||||
|
if (cx > RIGHT_ZONE) return { horizontal: 'right', right: DESIGN_WIDTH - (left + width) };
|
||||||
|
return { horizontal: 'center', horizontalCenter: cx - HALF_W };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 背景与遮罩:四边拉伸。spec §2.4。 */
|
||||||
|
export function stretchWidget() {
|
||||||
|
return { horizontal: 'stretch', vertical: 'stretch', left: 0, right: 0, top: 0, bottom: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFullScreen(width, height) {
|
||||||
|
return width >= DESIGN_WIDTH && height >= DESIGN_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 坐标超出画布。spec §2.5:不裁剪,只标记。 */
|
||||||
|
export function isOffscreen(left, top, width, height) {
|
||||||
|
return left < 0 || top < 0 || left + width > DESIGN_WIDTH || top + height > DESIGN_HEIGHT;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user