Files
photo-live/apps/viewer/src/__tests__/zoom.spec.ts
T
2026-09-18 11:04:05 +08:00

65 lines
2.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
INITIAL_ZOOM,
MAX_SCALE,
MIN_SCALE,
clampScale,
panBy,
resetZoom,
wheelFactor,
zoomAt,
type ZoomState,
} from '../viewer/zoom';
// 大图查看器缩放状态机(Task 8)纯函数层:钳制、不动点缩放、平移门控与复位。
describe('zoom state machine (Task 8)', () => {
it('clamps scale into [1, 4]', () => {
expect(clampScale(0.2)).toBe(MIN_SCALE);
expect(clampScale(3.9)).toBe(3.9);
expect(clampScale(11)).toBe(MAX_SCALE);
});
it('zooms around the given anchor point: the anchor stays fixed on screen', () => {
const state: ZoomState = { scale: 1, tx: 0, ty: 0 };
// 锚点 (100, 40)(相对图片中心):缩放前屏幕位置 = 100*1+0 = 100。
const next = zoomAt(state, 2, 100, 40);
expect(next.scale).toBe(2);
expect(next.tx).toBe(100 * (1 - 2));
expect(next.ty).toBe(40 * (1 - 2));
// 锚点屏幕位置不变:100*2 + (-100) = 100。
expect(100 * next.scale + next.tx).toBe(100);
expect(40 * next.scale + next.ty).toBe(40);
});
it('clamps at max scale and keeps the anchor fixed with the actual factor', () => {
const state: ZoomState = { scale: 3, tx: -50, ty: 20 };
const next = zoomAt(state, 10, 200, 0);
expect(next.scale).toBe(MAX_SCALE);
// 实际因子 4/3;锚点 200*3-50 = 550 → 200*4 + tx' = 550 → tx' = -250。
expect(200 * next.scale + next.tx).toBe(200 * state.scale + state.tx);
});
it('resets translation when scale returns to min', () => {
const state: ZoomState = { scale: 2, tx: -120, ty: 80 };
const next = zoomAt(state, 0.1, 300, 300);
expect(next).toEqual(resetZoom());
});
it('pans only when zoomed in; pan accumulates', () => {
const flat: ZoomState = { ...INITIAL_ZOOM };
expect(panBy(flat, 40, 20)).toBe(flat); // scale=1:无平移空间
const zoomed = zoomAt(flat, 2, 0, 0);
const panned = panBy(panBy(zoomed, 10, -5), 6, 6);
expect(panned.tx).toBe(16);
expect(panned.ty).toBe(1);
});
it('wheel factor maps deltaY sign consistently and stays continuous', () => {
expect(wheelFactor(-120)).toBeGreaterThan(1); // 上滚/外张 → 放大
expect(wheelFactor(120)).toBeLessThan(1); // 下滚/内收 → 缩小
expect(wheelFactor(0)).toBe(1);
});
});