e0f9f811b7
补回 transform translateX(-50%):锚点给的是占位卡中心,少了它浮层整体右偏半个面板宽 高度只按画布安全带分配:装得下时挂在卡下面,装不下时与卡顶边对齐盖住占位,不再压成一百多像素 reveal 的面板高度按当前缩放从屏幕 px 折回世界坐标,占位与浮层并集参与可见性 浮层宽度并入可见性并集,靠边卡片不再把浮层切掉一半 新增真实矩形模拟用例(画布高 568)核对浮层高度与落点不越出安全带
153 lines
5.1 KiB
TypeScript
153 lines
5.1 KiB
TypeScript
import { describe, expect, test } from 'vitest';
|
|
|
|
import { revealResourceCanvasGenerationContent } from '../src/features/resource-canvas/resourceCanvasGenerationVisibilityModel';
|
|
import { resolveResourceCanvasGenerationPanelPlacement } from '../src/features/resource-canvas/resourceCanvasGenerationVisibilityModel';
|
|
|
|
const canvasSize = { width: 800, height: 600 };
|
|
const insets = { top: 60, bottom: 90 };
|
|
|
|
describe('生成浮层与占位的可见性', () => {
|
|
test('内容已经在安全区内时坐标不变(宿主据此跳过写回)', () => {
|
|
const viewport = { x: 0, y: 0, scale: 1 };
|
|
const next = revealResourceCanvasGenerationContent({
|
|
viewport,
|
|
content: { x: 40, y: 80, width: 180, height: 200 },
|
|
canvasSize,
|
|
insets,
|
|
});
|
|
expect(next).toEqual(viewport);
|
|
});
|
|
|
|
test('内容落到视口下方时做最小上移,底边贴住底栏安全区', () => {
|
|
const viewport = { x: 0, y: 0, scale: 1 };
|
|
// 占位在 y=550、加上浮层后整块落到视口外——真实浏览器上复现过的那一档。
|
|
const next = revealResourceCanvasGenerationContent({
|
|
viewport,
|
|
content: { x: 40, y: 550, width: 180, height: 320 },
|
|
canvasSize,
|
|
insets,
|
|
});
|
|
const safeBottom = canvasSize.height - insets.bottom;
|
|
expect(viewport.y + (550 + 320)).toBeGreaterThan(safeBottom);
|
|
expect(next.y + 550 + 320).toBe(safeBottom);
|
|
expect(next.x).toBe(0);
|
|
expect(next.scale).toBe(1);
|
|
});
|
|
|
|
test('内容顶出顶栏时下移,内容越出左右边时做最小横移', () => {
|
|
const next = revealResourceCanvasGenerationContent({
|
|
viewport: { x: 0, y: 0, scale: 1 },
|
|
content: { x: -300, y: -40, width: 180, height: 120 },
|
|
canvasSize,
|
|
insets,
|
|
});
|
|
expect(next.y).toBe(insets.top + 40);
|
|
expect(next.x).toBe(12 + 300);
|
|
});
|
|
|
|
test('缩放参与换算:缩小后的越界按同一口径平移,比例保持不变', () => {
|
|
const next = revealResourceCanvasGenerationContent({
|
|
viewport: { x: 0, y: 0, scale: 0.5 },
|
|
content: { x: 40, y: 900, width: 180, height: 320 },
|
|
canvasSize,
|
|
insets,
|
|
});
|
|
expect(next.scale).toBe(0.5);
|
|
const safeBottom = canvasSize.height - insets.bottom;
|
|
expect(next.y + (900 + 320) * 0.5).toBe(safeBottom);
|
|
});
|
|
|
|
test('内容比安全区还大时以顶边 / 左边对齐,不来回抖', () => {
|
|
const next = revealResourceCanvasGenerationContent({
|
|
viewport: { x: 0, y: 0, scale: 1 },
|
|
content: { x: 0, y: 0, width: 2000, height: 1200 },
|
|
canvasSize,
|
|
insets,
|
|
});
|
|
expect(next).toEqual({ scale: 1, x: 12, y: insets.top });
|
|
});
|
|
|
|
test('尺寸非法或画布未就绪时原样返回', () => {
|
|
const viewport = { x: 5, y: 6, scale: 1 };
|
|
expect(
|
|
revealResourceCanvasGenerationContent({
|
|
viewport,
|
|
content: { x: 0, y: 0, width: 0, height: 0 },
|
|
canvasSize,
|
|
insets,
|
|
}),
|
|
).toEqual(viewport);
|
|
expect(
|
|
revealResourceCanvasGenerationContent({
|
|
viewport,
|
|
content: { x: 0, y: 0, width: 10, height: 10 },
|
|
canvasSize: { width: 0, height: 0 },
|
|
insets,
|
|
}),
|
|
).toEqual(viewport);
|
|
});
|
|
});
|
|
|
|
describe('浮层高度与「盖住占位」判定按画布安全带算', () => {
|
|
const placement = (input: {
|
|
canvasHeight: number;
|
|
topInset: number;
|
|
bottomInset: number;
|
|
anchorHeight: number;
|
|
}) => resolveResourceCanvasGenerationPanelPlacement(input);
|
|
|
|
test('空间够时挂在卡下面,高度 = 安全带 - 卡 - 间隙', () => {
|
|
const result = placement({
|
|
canvasHeight: 568,
|
|
topInset: 54,
|
|
bottomInset: 84,
|
|
anchorHeight: 128,
|
|
});
|
|
expect(result.overlaysAnchor).toBe(false);
|
|
// 安全带 430 - 卡 128 - 间隙 12 = 290:落在「至少 260 可编辑」的区间里。
|
|
expect(result.availableHeight).toBe(290);
|
|
});
|
|
|
|
test('安全带给不出可编辑高度时改为盖住占位,拿整条安全带', () => {
|
|
// 缩放 1.5:占位卡视觉 192px,卡下面只剩 224 (< 280) → 盖住占位。
|
|
const result = placement({
|
|
canvasHeight: 568,
|
|
topInset: 58,
|
|
bottomInset: 72,
|
|
anchorHeight: 192,
|
|
});
|
|
expect(result.overlaysAnchor).toBe(true);
|
|
// 安全带 438 - 间隙 12 = 426:远大于「一百多像素」的旧表现。
|
|
expect(result.availableHeight).toBe(426);
|
|
expect(result.availableHeight).toBeGreaterThanOrEqual(260);
|
|
});
|
|
|
|
test('空间充足时收到上限,几何非法时回退到最小编辑高度', () => {
|
|
expect(
|
|
placement({
|
|
canvasHeight: 1400,
|
|
topInset: 54,
|
|
bottomInset: 84,
|
|
anchorHeight: 128,
|
|
}).availableHeight,
|
|
).toBe(520);
|
|
expect(
|
|
placement({
|
|
canvasHeight: 0,
|
|
topInset: 54,
|
|
bottomInset: 84,
|
|
anchorHeight: 128,
|
|
}),
|
|
).toEqual({ overlaysAnchor: false, availableHeight: 280 });
|
|
// 画布小到连安全带都装不下:退到安全带能给的量,但仍有可编辑高度。
|
|
const tiny = placement({
|
|
canvasHeight: 240,
|
|
topInset: 40,
|
|
bottomInset: 40,
|
|
anchorHeight: 128,
|
|
});
|
|
expect(tiny.overlaysAnchor).toBe(true);
|
|
expect(tiny.availableHeight).toBe(160);
|
|
});
|
|
});
|