Skip to content

Commit

Permalink
feat: add allowPinchZoom property, fixes #17
Browse files Browse the repository at this point in the history
  • Loading branch information
theKashey committed Oct 17, 2019
1 parent 6d39ac9 commit 2945796
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 65 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {RemoveScroll} from 'react-remove-scroll';
`RemoveScroll` accept following props
- `children`
- `[enabled]` - activate or deactivate component behaviour without removing it.
- `[allowPinchZoom=false]` - enabled "pinch-n-zoom" behavior. By default it might be prevented. However - pinch and zoom might break "scroll isolation", and __disabled by default__.
- `[noIsolation=false]` - disables outer event capturing. Event capturing is React friendly and unlikely be a problem.
But if you are using _shadowbox_ of some sort - you dont need it.
- `[inert=false]` - ☠️(be careful) disables events the rest of page completely using `pointer-events` expect the Lock(+shard).
Expand Down
2 changes: 1 addition & 1 deletion example/xyScroll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default function XYBox() {
return (
<div className="App">

<RemoveScroll enabled={true}>
<RemoveScroll enabled={true} allowPinchZoom={false}>
<div className="Sub">
<div className="SubSub">
<div className="container">
Expand Down
70 changes: 30 additions & 40 deletions src/SideEffect.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import * as React from 'react';
import {TouchEvent, useRef} from 'react';
import {RemoveScrollBar} from 'react-remove-scroll-bar';
import {styleSingleton} from 'react-style-singleton';
import {handleScroll, locationCouldBeScrolled} from './handleScroll';
import {nonPassive} from './aggresiveCapture';
import {Axis, IRemoveScrollEffectProps} from './types';
import {pinchOrZoom} from "./pinchAndZoom";

export const getTouchXY = (event: TouchEvent) =>
event.changedTouches
import { TouchEvent } from 'react';
import { RemoveScrollBar } from 'react-remove-scroll-bar';
import { styleSingleton } from 'react-style-singleton';
import { handleScroll, locationCouldBeScrolled } from './handleScroll';
import { nonPassive } from './aggresiveCapture';
import { Axis, IRemoveScrollEffectProps } from './types';

export const getTouchXY = (event: TouchEvent | WheelEvent) =>
'changedTouches' in event
? [event.changedTouches[0].clientX, event.changedTouches[0].clientY]
: [0, 0];

Expand All @@ -29,7 +28,9 @@ let idCounter = 0;
let lockStack: any[] = [];

export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {
const shouldPreventQueue = React.useRef<Array<{ name: string; delta: number[]; target: any; should: boolean }>>([]);
const shouldPreventQueue = React.useRef<
Array<{ name: string; delta: number[]; target: any; should: boolean }>
>([]);
const touchStartRef = React.useRef([0, 0]);
const activeAxis = React.useRef<Axis | undefined>();
const [id] = React.useState(idCounter++);
Expand Down Expand Up @@ -66,28 +67,18 @@ export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {
[props.inert, props.lockRef.current, props.shards]
);

const touchCache = useRef({});

const shouldCancelEvent = React.useCallback(
(event: any, parent: HTMLElement) => {
const touchHandler = pinchOrZoom(event, touchCache.current);
if (touchHandler){
if(touchHandler.action === 'zoom') {
console.log('allow zoom');
return false;
}
if(touchHandler.action === 'pinch') {
console.log('disallow pinch');
return true;
}
(event: WheelEvent | TouchEvent, parent: HTMLElement) => {
if ('touches' in event && event.touches.length === 2) {
return !lastProps.current.allowPinchZoom;
}

console.log(touchHandler);

const touch = touchHandler && touchHandler.coords;
const touch = getTouchXY(event);
const touchStart = touchStartRef.current;
const deltaX = touch ? touchStart[0] - touch[0] : event.deltaX;
const deltaY = touch ? touchStart[1] - touch[1] : event.deltaY;
const deltaX =
'deltaX' in event ? event.deltaX : touchStart[0] - touch[0];
const deltaY =
'deltaY' in event ? event.deltaY : touchStart[1] - touch[1];

let currentAxis: Axis | undefined;
let target: HTMLElement = event.target as any;
Expand Down Expand Up @@ -119,7 +110,11 @@ export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {
return false;
}

if (!activeAxis.current && event.changedTouches && (deltaX || deltaY)) {
if (
!activeAxis.current &&
'changedTouches' in event &&
(deltaX || deltaY)
) {
activeAxis.current = currentAxis;
}

Expand Down Expand Up @@ -179,7 +174,7 @@ export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {

const shouldCancel = React.useCallback(
(name: string, delta: number[], target: any, should: boolean) => {
const event = {name, delta, target, should};
const event = { name, delta, target, should };
shouldPreventQueue.current.push(event);
setTimeout(() => {
shouldPreventQueue.current = shouldPreventQueue.current.filter(
Expand All @@ -193,7 +188,6 @@ export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {
const scrollTouchStart = React.useCallback((event: any) => {
touchStartRef.current = getTouchXY(event);
activeAxis.current = undefined;
touchCache.current = {};
}, []);

const scrollWheel = React.useCallback((event: WheelEvent) => {
Expand Down Expand Up @@ -232,11 +226,7 @@ export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {
return () => {
lockStack = lockStack.filter(inst => inst !== Style);

document.removeEventListener(
'wheel',
shouldPrevent,
nonPassive as any
);
document.removeEventListener('wheel', shouldPrevent, nonPassive as any);
document.removeEventListener(
'touchmove',
shouldPrevent,
Expand All @@ -250,12 +240,12 @@ export function RemoveScrollSideCar(props: IRemoveScrollEffectProps) {
};
}, []);

const {removeScrollBar, inert} = props;
const { removeScrollBar, inert } = props;

return (
<React.Fragment>
{inert ? <Style styles={generateStyle(id)}/> : null}
{removeScrollBar ? <RemoveScrollBar gapMode="margin"/> : null}
{inert ? <Style styles={generateStyle(id)} /> : null}
{removeScrollBar ? <RemoveScrollBar gapMode="margin" /> : null}
</React.Fragment>
);
}
2 changes: 2 additions & 0 deletions src/UI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const RemoveScroll: RemoveScrollUIType = React.forwardRef<
sideCar,
noIsolation,
inert,
allowPinchZoom,
...rest
} = props;

Expand All @@ -65,6 +66,7 @@ const RemoveScroll: RemoveScrollUIType = React.forwardRef<
noIsolation={noIsolation}
inert={inert}
setCallbacks={setCallbacks}
allowPinchZoom={!!allowPinchZoom}
lockRef={ref}
/>
)}
Expand Down
43 changes: 19 additions & 24 deletions src/pinchAndZoom.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {Touch} from "react";
import { Touch } from 'react';

const ds = (ab: number[]) => (
(ab[0] <= 0 && ab[1] >= 0) || (ab[0] >= 0 && ab[1] <= 0)
);
const ds = (ab: number[]) =>
(ab[0] <= 0 && ab[1] >= 0) || (ab[0] >= 0 && ab[1] <= 0);

const sign = (x: number) => x < 0 ? -1 : 1;
const sign = (x: number) => (x < 0 ? -1 : 1);

export const pinchOrZoom = (event: TouchEvent, cache: Record<number, Touch>) => {
export const pinchOrZoom = (
event: TouchEvent,
cache: Record<number, Touch>
) => {
if (!event.changedTouches) {
return false;
}
Expand All @@ -16,49 +18,42 @@ export const pinchOrZoom = (event: TouchEvent, cache: Record<number, Touch>) =>
cache[event.touches[0].identifier],
cache[event.touches[1].identifier]
];
const newPoints: Touch[] = [
event.touches[0],
event.touches[1],
];
const newPoints: Touch[] = [event.touches[0], event.touches[1]];
if (oldPoints[0] && oldPoints[1]) {
// Calculate the difference between the start and move coordinates
const diffx = [
oldPoints[0].clientX - newPoints[0].clientX,
oldPoints[1].clientX - newPoints[1].clientX,
oldPoints[1].clientX - newPoints[1].clientX
];
const diffy = [
oldPoints[0].clientY - newPoints[0].clientY,
oldPoints[1].clientY - newPoints[1].clientY,
oldPoints[1].clientY - newPoints[1].clientY
];

console.log(diffx, diffy);

if (ds(diffx) || ds(diffy)) {
return {
action: 'zoom'
}
};
}

const mx = Math.max(Math.abs(diffx[0]), Math.abs(diffx[1]));
const my = Math.max(Math.abs(diffy[0]), Math.abs(diffy[1]));

return {
action: 'pinch',
coords: [
mx * sign(diffx[0]),
my * sign(diffx[1]),
]
}
coords: [mx * sign(diffx[0]), my * sign(diffx[1])]
};
}
}

Array.from(event.changedTouches).forEach(touch => cache[touch.identifier] = touch);
Array.from(event.changedTouches).forEach(
touch => (cache[touch.identifier] = touch)
);

return {
action: 'move',
coords: [
event.changedTouches[0].clientX,
event.changedTouches[0].clientY,
],
coords: [event.changedTouches[0].clientX, event.changedTouches[0].clientY]
};
};
};
2 changes: 2 additions & 0 deletions src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface ChildrenForward {
export interface IRemoveScrollSelfProps {
noIsolation?: boolean;
inert?: boolean;
allowPinchZoom?: boolean;

enabled?: boolean;
className?: string;
Expand All @@ -44,6 +45,7 @@ export type IRemoveScrollUIProps = IRemoveScrollProps & {
export interface IRemoveScrollEffectProps {
noIsolation?: boolean;
removeScrollBar?: boolean;
allowPinchZoom: boolean;
inert?: boolean;

shards?: Array<React.RefObject<any> | HTMLElement>;
Expand Down

0 comments on commit 2945796

Please sign in to comment.