优化画布小地图拖拽手感
小地图拖拽改为基于按下时视图计算位移 降低小地图拖拽灵敏度并保留单击定位 补充反向拖拽不沿旧方向漂移的回归测试
This commit is contained in:
@@ -2192,6 +2192,53 @@ describe('ImageCanvasEditorView', () => {
|
||||
expect(screen.getByRole('button', { name: '画布小地图' })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('keeps minimap drag direction stable after pausing and reversing', async () => {
|
||||
await renderLoadedEditor();
|
||||
|
||||
const minimap = screen.getByRole('button', { name: '画布小地图' });
|
||||
vi.spyOn(minimap, 'getBoundingClientRect').mockReturnValue({
|
||||
x: 0,
|
||||
y: 0,
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 132,
|
||||
bottom: 84,
|
||||
width: 132,
|
||||
height: 84,
|
||||
toJSON: () => ({}),
|
||||
});
|
||||
const world = screen
|
||||
.getByLabelText('画布工作区')
|
||||
.querySelector('.image-canvas-editor__world') as HTMLElement;
|
||||
const readTranslateX = () => {
|
||||
const match = /translate\(([-\d.]+)px,/u.exec(world.style.transform);
|
||||
return match ? Number(match[1]) : 0;
|
||||
};
|
||||
|
||||
dispatchPointerEvent(minimap, 'pointerdown', {
|
||||
button: 0,
|
||||
pointerId: 72,
|
||||
clientX: 60,
|
||||
clientY: 42,
|
||||
});
|
||||
dispatchPointerEvent(screen.getByLabelText('画布工作区'), 'pointermove', {
|
||||
button: 0,
|
||||
pointerId: 72,
|
||||
clientX: 120,
|
||||
clientY: 42,
|
||||
});
|
||||
const translateAfterRightDrag = readTranslateX();
|
||||
|
||||
dispatchPointerEvent(screen.getByLabelText('画布工作区'), 'pointermove', {
|
||||
button: 0,
|
||||
pointerId: 72,
|
||||
clientX: 90,
|
||||
clientY: 42,
|
||||
});
|
||||
|
||||
expect(readTranslateX()).toBeGreaterThan(translateAfterRightDrag);
|
||||
});
|
||||
|
||||
it('persists layer groups in the canvas layer snapshot', async () => {
|
||||
await renderLoadedEditor();
|
||||
|
||||
|
||||
@@ -324,6 +324,11 @@ type DragState =
|
||||
| {
|
||||
kind: 'minimap';
|
||||
pointerId: number;
|
||||
startClientX: number;
|
||||
startClientY: number;
|
||||
startViewport: CanvasViewport;
|
||||
minimapScale: number;
|
||||
moved: boolean;
|
||||
};
|
||||
|
||||
const EDITOR_ASSET_FOLDERS: EditorAssetFolder[] = [
|
||||
@@ -347,6 +352,7 @@ const SNAP_THRESHOLD_SCREEN_PX = 18;
|
||||
const FIT_VIEW_PADDING = 10;
|
||||
const MINIMAP_SIZE = { width: 132, height: 84 };
|
||||
const MINIMAP_PADDING = 8;
|
||||
const MINIMAP_DRAG_SENSITIVITY = 0.3;
|
||||
const CONTEXT_MENU_SIZE = {
|
||||
blank: { width: 176, height: 148 },
|
||||
layer: { width: 176, height: 444 },
|
||||
@@ -3519,6 +3525,28 @@ export function ImageCanvasEditorView() {
|
||||
}));
|
||||
};
|
||||
|
||||
const moveViewportFromMinimapDrag = (
|
||||
dragState: Extract<DragState, { kind: 'minimap' }>,
|
||||
clientX: number,
|
||||
clientY: number,
|
||||
) => {
|
||||
const deltaWorldX =
|
||||
((clientX - dragState.startClientX) / dragState.minimapScale) *
|
||||
MINIMAP_DRAG_SENSITIVITY;
|
||||
const deltaWorldY =
|
||||
((clientY - dragState.startClientY) / dragState.minimapScale) *
|
||||
MINIMAP_DRAG_SENSITIVITY;
|
||||
setViewport({
|
||||
...dragState.startViewport,
|
||||
x:
|
||||
dragState.startViewport.x -
|
||||
deltaWorldX * dragState.startViewport.scale,
|
||||
y:
|
||||
dragState.startViewport.y -
|
||||
deltaWorldY * dragState.startViewport.scale,
|
||||
});
|
||||
};
|
||||
|
||||
const handleMinimapPointerDown = (
|
||||
event: ReactPointerEvent<HTMLButtonElement>,
|
||||
) => {
|
||||
@@ -3529,10 +3557,14 @@ export function ImageCanvasEditorView() {
|
||||
dragStateRef.current = {
|
||||
kind: 'minimap',
|
||||
pointerId: getPointerId(event),
|
||||
startClientX: pointer.x,
|
||||
startClientY: pointer.y,
|
||||
startViewport: { ...viewportRef.current },
|
||||
minimapScale: minimapModel?.scale ?? 1,
|
||||
moved: false,
|
||||
};
|
||||
captureCanvasHistory();
|
||||
dragHistoryCapturedRef.current = true;
|
||||
moveViewportFromMinimapPointer(pointer.x, pointer.y);
|
||||
};
|
||||
|
||||
const handlePointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {
|
||||
@@ -3625,7 +3657,14 @@ export function ImageCanvasEditorView() {
|
||||
dragHistoryCapturedRef.current = true;
|
||||
}
|
||||
const pointer = getPointerClient(event);
|
||||
moveViewportFromMinimapPointer(pointer.x, pointer.y);
|
||||
const deltaX = pointer.x - dragState.startClientX;
|
||||
const deltaY = pointer.y - dragState.startClientY;
|
||||
if (!dragState.moved && Math.hypot(deltaX, deltaY) >= 2) {
|
||||
dragState.moved = true;
|
||||
}
|
||||
if (dragState.moved) {
|
||||
moveViewportFromMinimapDrag(dragState, pointer.x, pointer.y);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3692,6 +3731,10 @@ export function ImageCanvasEditorView() {
|
||||
dragState &&
|
||||
(dragState.pointerId < 0 || pointerId < 0 || dragState.pointerId === pointerId)
|
||||
) {
|
||||
if (dragState.kind === 'minimap' && !dragState.moved) {
|
||||
const pointer = getPointerClient(event);
|
||||
moveViewportFromMinimapPointer(pointer.x, pointer.y);
|
||||
}
|
||||
dragStateRef.current = null;
|
||||
dragHistoryCapturedRef.current = false;
|
||||
setIsPanning(false);
|
||||
|
||||
Reference in New Issue
Block a user