Skip to content

Commit 7d877bf

Browse files
committed
wip
1 parent d8016b1 commit 7d877bf

File tree

5 files changed

+90
-5
lines changed

5 files changed

+90
-5
lines changed

src/components/grid/grid.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ export class Grid extends Component<Props, SpreadsheetChildEnv> {
207207

208208
usePinchToZoom(this.gridRef, (scale: number) => {
209209
this.env.model.dispatch("SET_ZOOM", {
210-
zoom: this.env.model.getters.getViewportZoomLevel() * scale,
210+
zoom: Math.round(this.env.model.getters.getViewportZoomLevel() * scale * 100) / 100,
211211
});
212212
});
213213
}

src/components/helpers/pinch_to_zoom_hook.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,30 @@ export function usePinchToZoom(ref: Ref<HTMLElement>, onZoom: (scale: number) =>
2727
// The pointerdown event signals the start of a touch interaction.
2828
// This event is cached to support 2-finger gestures
2929
evCache.push(ev);
30+
if (evCache.length < 2) {
31+
prevDiff = -1;
32+
} else if (evCache.length === 2) {
33+
prevDiff = computeDistance(evCache[0], evCache[1]);
34+
}
35+
}
36+
37+
function computeDistance(ev1: PointerEvent, ev2: PointerEvent) {
38+
const dx = ev1.clientX - ev2.clientX;
39+
const dy = ev1.clientY - ev2.clientY;
40+
return Math.abs(dx * dx + dy * dy);
3041
}
3142

3243
function pointermoveHandler(ev: PointerEvent) {
3344
// Find this event in the cache and update its record with this event
3445
const index = evCache.findIndex((cachedEv) => cachedEv.pointerId === ev.pointerId);
46+
if (index === -1) return;
47+
3548
evCache[index] = ev;
3649

3750
// If two pointers are down, check for pinch gestures
3851
if (evCache.length === 2) {
3952
// Calculate the distance between the two pointers
40-
const dx = evCache[0].clientX - evCache[1].clientX;
41-
const dy = evCache[0].clientY - evCache[1].clientY;
42-
43-
const curDiff = Math.abs(dx * dx + dy * dy);
53+
const curDiff = computeDistance(evCache[0], evCache[1]);
4454

4555
if (prevDiff > 0) {
4656
onZoom((curDiff / prevDiff) ** 0.25);

tests/grid/grid_component.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import {
7575
scrollGrid,
7676
simulateClick,
7777
triggerMouseEvent,
78+
triggerPointerEvent,
7879
triggerTouchEvent,
7980
triggerWheelEvent,
8081
} from "../test_helpers/dom_helper";
@@ -2261,3 +2262,41 @@ describe("Header grouping shortcuts", () => {
22612262
});
22622263
});
22632264
});
2265+
2266+
describe("Pinch to zoom", () => {
2267+
beforeEach(async () => {
2268+
({ parent, model, fixture } = await mountSpreadsheet());
2269+
});
2270+
2271+
test("Can pinch to zoom in", async () => {
2272+
const grid = fixture.querySelector(".o-grid-overlay")!;
2273+
const moveDistance = 30;
2274+
triggerPointerEvent(grid, "pointerdown", 100, 100, { pointerId: 1, bubbles: true });
2275+
triggerPointerEvent(grid, "pointerdown", 120, 120, { pointerId: 2, bubbles: true });
2276+
2277+
// zoom in 30 px of distance
2278+
triggerPointerEvent(grid, "pointermove", 120 + moveDistance, 120 + moveDistance, {
2279+
pointerId: 2,
2280+
bubbles: true,
2281+
});
2282+
2283+
await nextTick();
2284+
expect(model.getters.getViewportZoomLevel()).toBe(1.58);
2285+
2286+
// go back to initial
2287+
triggerPointerEvent(grid, "pointermove", 120, 120, {
2288+
pointerId: 2,
2289+
bubbles: true,
2290+
});
2291+
await nextTick();
2292+
expect(model.getters.getViewportZoomLevel()).toBe(1);
2293+
2294+
// pinch to zoom out - closer pointers
2295+
triggerPointerEvent(grid, "pointermove", 120, 120, {
2296+
pointerId: 1,
2297+
bubbles: true,
2298+
});
2299+
await nextTick();
2300+
expect(model.getters.getViewportZoomLevel()).toBeLessThan(1);
2301+
});
2302+
});

tests/setup/jest.setup.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ function registerOwlTemplates() {
5454
}
5555
}
5656

57+
class PointerEventPolyfill extends MouseEvent {
58+
pointerId: number;
59+
constructor(type: string, eventInitDict: PointerEventInit) {
60+
super(type, eventInitDict);
61+
this.pointerId = eventInitDict.pointerId ?? 0;
62+
}
63+
}
64+
5765
beforeAll(() => {
5866
registerOwlTemplates();
5967
setDefaultSheetViewSize(1000);
@@ -82,6 +90,9 @@ beforeAll(() => {
8290
};
8391

8492
console.debug = () => {};
93+
94+
// @ts-ignore
95+
window.PointerEvent = PointerEventPolyfill;
8596
});
8697

8798
beforeEach(() => {

tests/test_helpers/dom_helper.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,31 @@ export function triggerKeyboardEvent(
299299
dispatchEvent(selector, ev);
300300
}
301301

302+
export function triggerPointerEvent(
303+
selector: DOMTarget,
304+
type: string,
305+
offsetX?: number,
306+
offsetY?: number,
307+
extra: PointerEventInit = { bubbles: true }
308+
) {
309+
if (type === "pointermove") {
310+
extra = { button: -1, ...extra };
311+
}
312+
const ev = new PointerEvent(type, {
313+
// this is only correct if we assume the target is positioned
314+
// at the very top left corner of the screen
315+
clientX: offsetX,
316+
clientY: offsetY,
317+
bubbles: true,
318+
...extra,
319+
});
320+
(ev as any).offsetX = offsetX;
321+
(ev as any).offsetY = offsetY;
322+
const target = getTarget(selector);
323+
target.dispatchEvent(ev);
324+
return ev;
325+
}
326+
302327
function dispatchEvent(selector: string | EventTarget, ev: Event) {
303328
if (typeof selector === "string") {
304329
const el = document.querySelector(selector);

0 commit comments

Comments
 (0)