Skip to content

Commit

Permalink
Ntrfcs 42 : Zoom, Panning and Mobile click issue (#299)
Browse files Browse the repository at this point in the history
* restrict infinity zooming and panning, click issue in mobile view

* changeset changes

* fixed lint issues and testcase issues
  • Loading branch information
SrujanaSaka20 authored Jan 10, 2025
1 parent 4abd50d commit edeaa09
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/three-ladybugs-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ticketevolution/seatmaps-client": patch
---

Fix mobile click issues and restrict infinite zooming and panning behavior. Limit zoom in to 10% of the original size and prevent zooming out beyond the original size.
29 changes: 26 additions & 3 deletions packages/seatmaps-client/src/TicketMap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -461,11 +461,19 @@ export class TicketMap extends Component<Props & DefaultProps, State> {
* Interaction Callbacks
*/

onMouseOver = ({ clientX, clientY, target }: React.MouseEvent<HTMLElement>) =>
onMouseOver = ({
clientX,
clientY,
target,
}: React.MouseEvent<HTMLElement>) => {
if (this.state.isTouchDevice) return;
this.doHover(target as HTMLElement, clientX, clientY);
};

onMouseOut = ({ relatedTarget }: React.MouseEvent<HTMLElement>) =>
onMouseOut = ({ relatedTarget }: React.MouseEvent<HTMLElement>) => {
if (this.state.isTouchDevice) return;
this.doHoverCleanup(relatedTarget as HTMLElement);
};

onMouseMove = ({ nativeEvent }: React.MouseEvent<HTMLElement>) =>
this.setState({
Expand All @@ -475,15 +483,29 @@ export class TicketMap extends Component<Props & DefaultProps, State> {

onClick = () => this.doSelect();

onTouchStart = (e: React.TouchEvent<HTMLElement>) => {
const section = this.getSectionFromTarget(e.target as HTMLElement);
if (section) {
this.setState({
currentHoveredSection: section,
});
this.highlightSection(section);
}
};

onTouchMove = () => {
this.setState({ dragging: true });
};

onTouchEnd = (e: React.TouchEvent<HTMLElement>) => {
const section = this.state.currentHoveredSection;
if (section) {
this.doSelect(section);
this.setState({ currentHoveredSection: undefined });
}
if (this.state.dragging) {
e.preventDefault();
}

this.setState({ dragging: false });
};

Expand Down Expand Up @@ -602,6 +624,7 @@ export class TicketMap extends Component<Props & DefaultProps, State> {
onMouseOut={this.onMouseOut}
onMouseMove={this.onMouseMove}
onClick={this.onClick}
onTouchStart={this.onTouchStart}
onTouchEnd={this.onTouchEnd}
onTouchMove={this.onTouchMove}
style={{
Expand Down
28 changes: 14 additions & 14 deletions packages/seatmaps-client/src/__tests__/utils/zoom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,18 +181,18 @@ describe("zoom", () => {
it("zomms out", () => {
zoomApi.zoomOut(0.1);

expect(mockPoint.height).toBe(11);
expect(mockPoint.width).toBe(11);
expect(mockPoint.x).toBe(9.5);
expect(mockPoint.y).toBe(9.5);
expect(mockPoint.height).toBe(10);
expect(mockPoint.width).toBe(10);
expect(mockPoint.x).toBe(10);
expect(mockPoint.y).toBe(10);
});

it("resets to original position after zoom", () => {
zoomApi.zoomOut(0.1);
expect(mockPoint.height).toBe(11);
expect(mockPoint.width).toBe(11);
expect(mockPoint.x).toBe(9.5);
expect(mockPoint.y).toBe(9.5);
expect(mockPoint.height).toBe(10);
expect(mockPoint.width).toBe(10);
expect(mockPoint.x).toBe(10);
expect(mockPoint.y).toBe(10);

zoomApi.reset();
expect(mockPoint.height).toBe(10);
Expand Down Expand Up @@ -246,8 +246,8 @@ describe("zoom", () => {

expect(mockPoint.height).toBe(10);
expect(mockPoint.width).toBe(10);
expect(mockPoint.x).toBe(12);
expect(mockPoint.y).toBe(12);
expect(mockPoint.x).toBe(10);
expect(mockPoint.y).toBe(10);
});

it("handleWheel when ctrl key is not pressed and delta mode is DOM_DELTA_LINE", () => {
Expand Down Expand Up @@ -275,10 +275,10 @@ describe("zoom", () => {
} as unknown as Event;
triggerEvent("wheel", e);

expect(mockPoint.height).toBe(10.651041666666668);
expect(mockPoint.width).toBe(10.48828125);
expect(mockPoint.x).toBe(9.755859375);
expect(mockPoint.y).toBe(9.674479166666666);
expect(mockPoint.height).toBe(10);
expect(mockPoint.width).toBe(10);
expect(mockPoint.x).toBe(10);
expect(mockPoint.y).toBe(10);
});

it("handleWheel when ctrl key is pressed and delta mode is DOM_DELTA_LINE", () => {
Expand Down
63 changes: 56 additions & 7 deletions packages/seatmaps-client/src/utils/zoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,22 +101,53 @@ export function initializeZoom(svg: SVGSVGElement) {
const originalViewboxWidth = viewbox.width;
const originalViewboxHeight = viewbox.height;

function constrainPan() {
const minX = originalViewboxX;
const maxX = originalViewboxX + originalViewboxWidth - viewbox.width;

const minY = originalViewboxY;
const maxY = originalViewboxY + originalViewboxHeight - viewbox.height;

viewbox.x = Math.max(minX, Math.min(maxX, viewbox.x));
viewbox.y = Math.max(minY, Math.min(maxY, viewbox.y));
}

function translate(x: number, y: number) {
viewbox.x += x;
viewbox.y += y;
constrainPan();
}

function scale(scale: number) {
const MIN_ZOOM = 0.1; // Minimum zoom scale (10% of the original size)
const MAX_ZOOM = 1.0; // Maximum zoom scale (100% of the original size)

function scale(scaleFactor: number) {
// Calculate the new dimensions of the viewbox
const newHeight = viewbox.height * scaleFactor;
const newWidth = viewbox.width * scaleFactor;

// Ensure the new dimensions are within the min and max zoom bounds
const minViewboxHeight = originalViewboxHeight * MIN_ZOOM;
const maxViewboxHeight = originalViewboxHeight * MAX_ZOOM;

if (newHeight < minViewboxHeight || newHeight > maxViewboxHeight) {
return; // Prevent scaling if the zoom level is outside the allowed bounds
}

// Apply the scaling
const initialViewboxHeight = viewbox.height;
const initialViewboxWidth = viewbox.width;

viewbox.height *= scale;
viewbox.width *= scale;
viewbox.height = newHeight;
viewbox.width = newWidth;

// Adjust the viewbox to keep the zoom centered
translate(
0 - (viewbox.width - initialViewboxWidth) / 2,
0 - (viewbox.height - initialViewboxHeight) / 2,
);

constrainPan(); // Ensure panning stays within bounds after scaling
}

// decreases the size of the map, relative to the viewport size
Expand Down Expand Up @@ -281,15 +312,33 @@ export function initializeZoom(svg: SVGSVGElement) {
// Handle controlled scrolls as zoom inputs.
const delta = deltaY;

viewbox.height =
viewbox.height * (1 + (delta / window.innerHeight) * ZOOM_COEFFICIENT);
viewbox.width =
viewbox.width * (1 + (delta / window.innerWidth) * ZOOM_COEFFICIENT);
// Calculate the scaling factor
const scaleFactor = 1 + (delta / window.innerHeight) * ZOOM_COEFFICIENT;

// Determine the new dimensions
const newHeight = viewbox.height * scaleFactor;
const newWidth = viewbox.width * scaleFactor;

// Enforce zoom restrictions
const minViewboxHeight = originalViewboxHeight * MIN_ZOOM;
const maxViewboxHeight = originalViewboxHeight * MAX_ZOOM;

// Check and enforce the minimum and maximum zoom
if (newHeight < minViewboxHeight || newHeight > maxViewboxHeight) {
return;
}

// Apply the scaling
viewbox.height = newHeight;
viewbox.width = newWidth;

// Adjust the viewbox to keep the zoom centered
translate(
0 - (viewbox.width - initialViewboxWidth) / 2,
0 - (viewbox.height - initialViewboxHeight) / 2,
);

constrainPan(); // Ensure panning stays within bounds after scaling
} else {
// Handle non-controlled scrolls as pan inputs.
translate(
Expand Down

0 comments on commit edeaa09

Please sign in to comment.