diff --git a/__tests__/examples/example.tsx b/__tests__/examples/example.tsx
index c8c455e0..bd734e92 100644
--- a/__tests__/examples/example.tsx
+++ b/__tests__/examples/example.tsx
@@ -38,6 +38,10 @@ export const Example = (options: {
maxWidth: "100%",
maxHeight: "calc(100vh - 50px)",
}}
+ contentStyle={{
+ width: "100%",
+ height: "100%",
+ }}
>
Title
diff --git a/__tests__/features/pan-touch/pan-touch.base.spec.tsx b/__tests__/features/pan-touch/pan-touch.base.spec.tsx
index 4cfe849d..0ef44b3a 100644
--- a/__tests__/features/pan-touch/pan-touch.base.spec.tsx
+++ b/__tests__/features/pan-touch/pan-touch.base.spec.tsx
@@ -9,13 +9,11 @@ describe("Pan Touch [Base]", () => {
expect(content.style.transform).toBe("translate(0px, 0px) scale(1)");
pinch({ value: 1.5 });
await waitFor(() => {
- expect(content.style.transform).toBe(
- "translate(0px, 0px) scale(1.5012468827930179)",
- );
+ expect(content.style.transform).toBe("translate(0px, 0px) scale(1.5)");
});
touchPan({ x: 100, y: 100 });
expect(content.style.transform).toBe(
- "translate(100px, 100px) scale(1.5012468827930179)",
+ "translate(100px, 100px) scale(1.5)",
);
});
});
diff --git a/__tests__/features/pan/pan.base.spec.tsx b/__tests__/features/pan/pan.base.spec.tsx
index a4e469f1..256631e2 100644
--- a/__tests__/features/pan/pan.base.spec.tsx
+++ b/__tests__/features/pan/pan.base.spec.tsx
@@ -3,17 +3,35 @@ import { waitFor } from "@testing-library/react";
import { renderApp } from "../../utils/render-app";
describe("Pan [Base]", () => {
- describe("When panning in with controls button", () => {
+ describe("When panning to coords", () => {
+ it("should not change translate with disabled padding", async () => {
+ const { content, pan } = renderApp({
+ disablePadding: true,
+ });
+ expect(content.style.transform).toBe("translate(0px, 0px) scale(1)");
+ pan({ x: 100, y: 100 });
+ expect(content.style.transform).toBe("translate(0px, 0px) scale(1)");
+ });
+ it("should return to position with padding enabled", async () => {
+ const { content, pan } = renderApp({
+ disablePadding: false,
+ });
+
+ expect(content.style.transform).toBe("translate(0px, 0px) scale(1)");
+ pan({ x: 100, y: 100 });
+ expect(content.style.transform).toBe("translate(100px, 100px) scale(1)");
+ await waitFor(() => {
+ expect(content.style.transform).toBe("translate(0px, 0px) scale(1)");
+ });
+ });
it("should change translate css", async () => {
const { content, pan, zoom } = renderApp();
expect(content.style.transform).toBe("translate(0px, 0px) scale(1)");
zoom({ value: 1.5 });
- expect(content.style.transform).toBe(
- "translate(0px, 0px) scale(1.5009999999999448)",
- );
+ expect(content.style.transform).toBe("translate(0px, 0px) scale(1.5)");
pan({ x: 100, y: 100 });
expect(content.style.transform).toBe(
- "translate(100px, 100px) scale(1.5009999999999448)",
+ "translate(100px, 100px) scale(1.5)",
);
});
});
diff --git a/__tests__/features/pinch/pinch.base.spec.tsx b/__tests__/features/pinch/pinch.base.spec.tsx
index 2fee9de1..ec77817a 100644
--- a/__tests__/features/pinch/pinch.base.spec.tsx
+++ b/__tests__/features/pinch/pinch.base.spec.tsx
@@ -4,13 +4,12 @@ import { renderApp } from "../../utils/render-app";
describe("Pinch [Base]", () => {
describe("When zooming in with controls button", () => {
it("should increase css scale with animated zoom", async () => {
- const { content, pinch } = renderApp();
+ const { ref, content, pinch } = renderApp();
expect(content.style.transform).toBe("translate(0px, 0px) scale(1)");
pinch({ value: 1.5 });
await waitFor(() => {
- expect(content.style.transform).toBe(
- "translate(0px, 0px) scale(1.5012468827930179)",
- );
+ expect(content.style.transform).toBe("translate(0px, 0px) scale(1.5)");
+ expect(ref.current?.instance.transformState.scale).toBe(1.5);
});
});
});
diff --git a/__tests__/features/zoom/zoom.base.spec.tsx b/__tests__/features/zoom/zoom.base.spec.tsx
index ebee9f50..92978489 100644
--- a/__tests__/features/zoom/zoom.base.spec.tsx
+++ b/__tests__/features/zoom/zoom.base.spec.tsx
@@ -1,14 +1,16 @@
+import { waitFor } from "@testing-library/dom";
import { renderApp } from "../../utils/render-app";
describe("Zoom [Base]", () => {
describe("When zooming in with controls button", () => {
it("should increase css scale with animated zoom", async () => {
- const { content, zoom } = renderApp();
+ const { ref, content, zoom } = renderApp();
expect(content.style.transform).toBe("translate(0px, 0px) scale(1)");
zoom({ value: 1.5 });
- expect(content.style.transform).toBe(
- "translate(0px, 0px) scale(1.5009999999999448)",
- );
+ await waitFor(() => {
+ expect(content.style.transform).toBe("translate(0px, 0px) scale(1.5)");
+ expect(ref.current?.instance.transformState.scale).toBe(1.5);
+ });
});
});
});
diff --git a/__tests__/utils/render-app.tsx b/__tests__/utils/render-app.tsx
index 834eade5..0e971146 100644
--- a/__tests__/utils/render-app.tsx
+++ b/__tests__/utils/render-app.tsx
@@ -89,18 +89,26 @@ export const renderApp = (
fireEvent.mouseMove(content, { clientX: center[0], clientY: center[1] });
}
+ const step = 1;
+
const isZoomIn = ref.current.instance.transformState.scale < value;
while (true) {
if (
- isZoomIn
- ? ref.current.instance.transformState.scale <= value
- : ref.current.instance.transformState.scale >= value
+ (isZoomIn
+ ? ref.current.instance.transformState.scale < value
+ : ref.current.instance.transformState.scale > value) &&
+ ref.current.instance.transformState.scale !== value
) {
+ const isNearScale =
+ Math.abs(ref.current.instance.transformState.scale - value) < 0.01;
+
+ const newStep = isNearScale ? 0.35 : step;
+
fireEvent(
content,
new WheelEvent("wheel", {
bubbles: true,
- deltaY: isZoomIn ? -1 : 1,
+ deltaY: isZoomIn ? -newStep : newStep,
}),
);
} else {
@@ -127,12 +135,19 @@ export const renderApp = (
while (true) {
if (
- isZoomIn
- ? ref.current.instance.transformState.scale <= value
- : ref.current.instance.transformState.scale >= value
+ (isZoomIn
+ ? ref.current.instance.transformState.scale < value
+ : ref.current.instance.transformState.scale > value) &&
+ ref.current.instance.transformState.scale !== value
) {
- pinchValue[0] = pinchValue[0] + stepX;
- pinchValue[1] = pinchValue[1] + stepY;
+ const isNearScale =
+ Math.abs(ref.current.instance.transformState.scale - value) < 0.5;
+
+ const newStepX = isNearScale ? stepX / 10 : stepX;
+ const newStepY = isNearScale ? stepY / 10 : stepY;
+
+ pinchValue[0] = pinchValue[0] + newStepX;
+ pinchValue[1] = pinchValue[1] + newStepY;
touches = getPinchTouches(
content,
center,
@@ -155,6 +170,7 @@ export const renderApp = (
fireEvent.mouseDown(content);
fireEvent.mouseMove(content, { clientX: x, clientY: y });
fireEvent.mouseUp(content);
+ fireEvent.blur(content);
};
const touchPan: RenderApp["touchPan"] = ({ x, y }) => {
diff --git a/jest.config.ts b/jest.config.ts
index 53f86989..06b79afb 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -36,6 +36,6 @@ const config: Config.InitialOptions = {
"jest-watch-typeahead/filename",
"jest-watch-typeahead/testname",
],
- setupFilesAfterEnv: ["jest-extended/all"],
+ setupFilesAfterEnv: ["jest-extended/all", "./jest.setup.ts"],
};
export default config;
diff --git a/jest.setup.ts b/jest.setup.ts
index a8e02001..a846a6cf 100644
--- a/jest.setup.ts
+++ b/jest.setup.ts
@@ -16,3 +16,37 @@ jestPreviewConfigure({
autoPreview: true,
// publicFolder: "static", // No need to configure if `publicFolder` is `public`
});
+
+const getWidth = (element: HTMLElement) => {
+ const isPercentageWidth = element.style.width.includes("%");
+ const isPercentageHeight = element.style.height.includes("%");
+
+ let width = 0;
+ let height = 0;
+
+ const top = parseFloat(element.style.marginTop) || 0;
+ const left = parseFloat(element.style.marginLeft) || 0;
+
+ if (isPercentageWidth || isPercentageHeight) {
+ const parent = getWidth(element.parentNode as HTMLElement);
+ width = (parseFloat(element.style.width) * parent.width) / 100;
+ height = (parseFloat(element.style.height) * parent.height) / 100;
+ } else {
+ width = parseFloat(element.style.width);
+ height = parseFloat(element.style.height);
+ }
+
+ return {
+ width,
+ height,
+ top,
+ left,
+ };
+};
+
+// @ts-ignore
+window.HTMLElement.prototype.getBoundingClientRect = function () {
+ const size = getWidth(this);
+
+ return size;
+};
diff --git a/src/constants/state.constants.ts b/src/constants/state.constants.ts
index 647cba49..a91614a3 100644
--- a/src/constants/state.constants.ts
+++ b/src/constants/state.constants.ts
@@ -22,7 +22,7 @@ export const initialSetup: LibrarySetup = {
smooth: true,
detached: false,
wheel: {
- step: 0.03,
+ step: 0.015,
disabled: false,
wheelDisabled: false,
touchPadDisabled: false,
@@ -71,7 +71,8 @@ export const initialSetup: LibrarySetup = {
velocityAnimation: {
disabled: false,
sensitivity: 1,
- animationTime: 400,
+ maxStrength: 2.5,
+ animationTime: 300,
animationType: "easeOut",
equalToMove: true,
},
diff --git a/src/core/instance.core.ts b/src/core/instance.core.ts
index c8773481..50e9e3ba 100644
--- a/src/core/instance.core.ts
+++ b/src/core/instance.core.ts
@@ -487,10 +487,10 @@ export class ZoomPanPinch {
) {
if (scale !== this.transformState.scale) {
this.transformState.previousScale = this.transformState.scale;
- this.transformState.scale = scale;
+ this.transformState.scale = parseFloat(scale.toFixed(2));
}
- this.transformState.positionX = positionX;
- this.transformState.positionY = positionY;
+ this.transformState.positionX = parseFloat(positionX.toFixed(2));
+ this.transformState.positionY = parseFloat(positionY.toFixed(2));
this.applyTransformation();
const ctx = getContext(this);
diff --git a/src/core/pan/velocity.utils.ts b/src/core/pan/velocity.utils.ts
index 57d5422e..3409c827 100644
--- a/src/core/pan/velocity.utils.ts
+++ b/src/core/pan/velocity.utils.ts
@@ -37,10 +37,12 @@ export function getVelocityMoveTime(
velocity: number,
): number {
const { velocityAnimation } = contextInstance.setup;
- const { equalToMove, animationTime, sensitivity } = velocityAnimation;
+ const { equalToMove, animationTime, sensitivity, maxStrength } =
+ velocityAnimation;
if (equalToMove) {
- return animationTime * velocity * sensitivity;
+ const velocityValue = Math.min(velocity, maxStrength);
+ return animationTime * velocityValue * sensitivity;
}
return animationTime;
}
diff --git a/src/models/context.model.ts b/src/models/context.model.ts
index 15625f70..46634217 100644
--- a/src/models/context.model.ts
+++ b/src/models/context.model.ts
@@ -51,7 +51,7 @@ export type ReactZoomPanPinchProps = {
| React.ReactNode
| ((ref: ReactZoomPanPinchContentRef) => React.ReactNode);
ref?: React.Ref
;
- detached: boolean;
+ detached?: boolean;
initialScale?: number;
initialPositionX?: number;
initialPositionY?: number;
@@ -117,6 +117,7 @@ export type ReactZoomPanPinchProps = {
};
velocityAnimation?: {
disabled?: boolean;
+ maxStrength?: number;
sensitivity?: number;
animationTime?: number;
animationType?: keyof typeof animations;
diff --git a/src/stories/docs/props.tsx b/src/stories/docs/props.tsx
index 71216d3a..78a6d3b5 100644
--- a/src/stories/docs/props.tsx
+++ b/src/stories/docs/props.tsx
@@ -165,6 +165,18 @@ export const wrapperPropsTable: ComponentProps = {
description:
"We can provide custom transform function to provide different way of handling our transform logic. If we need performance we can import getMatrixTransformStyles functions and replace default one. WARNING: default transform prevents svg blur on the safari.",
},
+ smooth: {
+ type: ["boolean"],
+ defaultValue: String(initialSetup.smooth),
+ description:
+ "Enable smooth scrolling by multiplying the scroll delta with the smooth step factor.",
+ },
+ detached: {
+ type: ["boolean"],
+ defaultValue: String(initialSetup.smooth),
+ description:
+ "Allows to prevent CSS being applied to content element. It allows to add the functionality to canvas with some custom transforming logic.",
+ },
wheel: {
wheel: {
type: [""],
@@ -176,12 +188,6 @@ export const wrapperPropsTable: ComponentProps = {
defaultValue: String(initialSetup.wheel.step),
description: "The sensitivity of zooming with the wheel/touchpad.",
},
- smooth: {
- type: ["boolean"],
- defaultValue: String(initialSetup.wheel.smooth),
- description:
- "Enable smooth scrolling by multiplying the scroll delta with the smooth step factor.",
- },
disabled: {
type: ["boolean"],
defaultValue: String(initialSetup.wheel.disabled),
@@ -418,6 +424,12 @@ export const wrapperPropsTable: ComponentProps = {
defaultValue: String(initialSetup.velocityAnimation.disabled),
description: "Disable the double click feature.",
},
+ maxStrength: {
+ type: ["number"],
+ defaultValue: String(initialSetup.velocityAnimation.maxStrength),
+ description:
+ "The maximum strength of the velocity animation. The higher the value, the faster the animation will be allowed.",
+ },
sensitivity: {
type: ["number"],
defaultValue: String(initialSetup.velocityAnimation.animationTime),
diff --git a/src/stories/examples/content-rerendering/example.tsx b/src/stories/examples/content-rerendering/example.tsx
index f010e1ae..5511c272 100644
--- a/src/stories/examples/content-rerendering/example.tsx
+++ b/src/stories/examples/content-rerendering/example.tsx
@@ -40,7 +40,7 @@ export const Example: React.FC = (args: any) => {
background: "#444",
color: "white",
padding: "50px",
- minHeight: "300px",
+ minHeight: "100%",
width: "100%",
}}
>
diff --git a/src/stories/examples/stress/example.tsx b/src/stories/examples/stress/example.tsx
index 3ed15d7c..3ec3963c 100644
--- a/src/stories/examples/stress/example.tsx
+++ b/src/stories/examples/stress/example.tsx
@@ -11,7 +11,7 @@ export const Example: React.FC = (args: any) => {
return (
-
+
{Array.from(Array(1000).keys()).map((key) => (
![]({exampleImg})
diff --git a/src/stories/types/args.types.ts b/src/stories/types/args.types.ts
index a0588533..821b443c 100644
--- a/src/stories/types/args.types.ts
+++ b/src/stories/types/args.types.ts
@@ -16,15 +16,6 @@ export const argsTypes = {
defaultValue: { summary: "0" },
},
},
- "wheel.smooth": {
- defaultValue: initialSetup.wheel.smooth,
- control: {
- type: "boolean",
- },
- table: {
- defaultValue: { summary: "true" },
- },
- },
"wheel.disabled": {
defaultValue: initialSetup.wheel.disabled,
control: { type: "boolean" },
diff --git a/src/stories/utils/styles.module.css b/src/stories/utils/styles.module.css
index e25363c0..479057fe 100644
--- a/src/stories/utils/styles.module.css
+++ b/src/stories/utils/styles.module.css
@@ -1,3 +1,8 @@
+.content {
+ width: calc(100vw - 200px);
+ height: calc(100vh - 200px);
+}
+
.controlPanel {
position: absolute;
z-index: 2;
@@ -31,6 +36,4 @@
.grid {
display: grid;
grid-template-columns: repeat(100, 1fr);
- width: calc(100vw - 200px);
- height: calc(100vh - 200px);
}