Skip to content

Commit

Permalink
Prevent pull down refresh with overscroll-behavior vs cancel touchMove
Browse files Browse the repository at this point in the history
  • Loading branch information
bradstiff committed Feb 2, 2019
1 parent e9aaf5a commit 7454d4f
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 24 deletions.
7 changes: 4 additions & 3 deletions examples/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ const flexContentStyle = {
const FlexContainerView = ({menu}) => (
<div style={{height: '100vh', display: 'flex', flexDirection: 'column'}}>
<nav style={{flex: 'none'}}>{menu}</nav>
<div style={{flex: 'none', textAlign: 'center'}}><span style={flexContentStyle}>The image container dimensions are dynamic based on CSS Flex</span></div>
<div style={{flex: 'none', textAlign: 'center'}}><span style={flexContentStyle}>The image fills 100% of the flex item in which it is</span></div>
<main style={{flex: 'auto', overflow: 'hidden', display: 'flex'}}>
<div style={{flex: 'none', alignSelf: 'center'}}>
<span style={flexContentStyle}>Sidebar</span>
</div>
<div style={{flex: 'auto', overflow: 'hidden'}}>
<div style={{flex: 'auto', overflow: 'hidden', position: 'relative'}}>
<div style={{position: 'absolute', height: '100%', width: '100%'}}>
<PinchZoomPan>
<img alt='Demo Image' src='http://picsum.photos/2560/1440?random' />
</PinchZoomPan>
</PinchZoomPan></div>
</div>
</main>
</div>
Expand Down
75 changes: 55 additions & 20 deletions src/PinchZoomPan.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export default class PinchZoomPan extends React.Component {
animation;
image;
isImageLoaded; //permits initial transform
shouldHandlePullDownRefresh;
originalOverscrollBehaviorY;

//event handlers
handleTouchStart = (event) => {
Expand All @@ -45,16 +47,10 @@ export default class PinchZoomPan extends React.Component {
handleTouchMove = (event) => {
const touches = event.touches;
if (touches.length === 2) {
//suppress viewport scaling
event.preventDefault();
this.pinchChange(touches);
}
else if (touches.length === 1) {
const swipingDown = this.pan(touches[0]) > 0;
if (swipingDown && this.state.top < 0) {
//suppress pull-down-refresh since swiping down will reveal the hidden overflow of the image
event.preventDefault();
}
this.pan(touches[0]) > 0;
}
}

Expand All @@ -69,7 +65,7 @@ export default class PinchZoomPan extends React.Component {
this.pointerUp(event.timeStamp);

//suppress mouseUp, in case of tap
event.preventDefault();
this.cancelEvent(event);
}

handleMouseDown = (event) => {
Expand Down Expand Up @@ -97,12 +93,12 @@ export default class PinchZoomPan extends React.Component {
if (event.deltaY > 0) {
if (this.state.scale > getMinScale(this.state, this.props)) {
this.zoomOut(point);
event.preventDefault();
this.cancelEvent(event);
}
} else if (event.deltaY < 0) {
if (this.state.scale < this.props.maxScale) {
this.zoomIn(point);
event.preventDefault();
this.cancelEvent(event);
}
}
}
Expand All @@ -129,7 +125,12 @@ export default class PinchZoomPan extends React.Component {
}

handleWindowResize = () => this.maybeHandleDimensionsChanged();
suppressEvent = event => event.preventDefault();

cancelEvent = event => {
if (event.cancelable) {
event.preventDefault();
}
}

handleRefImage = ref => {
if (this.image) {
Expand Down Expand Up @@ -383,6 +384,17 @@ export default class PinchZoomPan extends React.Component {
const childElement = React.Children.only(this.props.children);
const { zoomButtons, maxScale } = this.props;
const { top, left, scale } = this.state;

const transformStyle = this.isTransformInitialized && {
transform: `translate3d(${left}px, ${top}px, 0) scale(${scale})`,
transformOrigin: '0 0',
};

const style = {
cursor: 'pointer',
...transformStyle
};

return (
<div style={containerStyle}>
{zoomButtons && this.isImageReady && this.isTransformInitialized && <ZoomButtons
Expand All @@ -399,17 +411,10 @@ export default class PinchZoomPan extends React.Component {
onMouseMove: this.handleMouseMove,
onMouseUp: this.handleMouseUp,
onWheel: this.handleMouseWheel,
onDragStart: this.suppressEvent,
onDragStart: this.cancelEvent,
onLoad: this.handleImageLoad,
ref: this.handleRefImage,
style: this.isTransformInitialized
? {
cursor: 'pointer',
transform: `translate3d(${left}px, ${top}px, 0) scale(${scale})`,
transformOrigin: '0 0',
} : {
cursor: 'pointer'
}
style: style
})}
</div>
);
Expand All @@ -432,10 +437,13 @@ export default class PinchZoomPan extends React.Component {
componentDidMount() {
window.addEventListener("resize", this.handleWindowResize);
this.maybeHandleDimensionsChanged();
this.initShouldHandlePullDownRefresh();
this.maybePreventPullDownRefresh();
}

componentDidUpdate(prevProps, prevState) {
this.maybeHandleDimensionsChanged();
this.maybePreventPullDownRefresh();
}

componentWillUnmount() {
Expand All @@ -454,6 +462,33 @@ export default class PinchZoomPan extends React.Component {
this.state.top !== undefined;
}

initShouldHandlePullDownRefresh() {
try {
const isChrome = window.chrome || navigator.userAgent.match('CriOS');
const isTouch = 'ontouchstart' in document.documentElement;
const supportsOverscroll = CSS.supports('overscroll-behavior-y', 'none');
this.shouldHandlePullDownRefresh = isChrome && isTouch && supportsOverscroll;
} catch (e) {
this.shouldHandlePullDownRefresh = false;
}
}

maybePreventPullDownRefresh() {
if (!this.shouldHandlePullDownRefresh) {
return;
}
const overscrollBehaviorY = document.body.style.overscrollBehaviorY;
if (this.state.top < 0 && overscrollBehaviorY != 'none' && overscrollBehaviorY != 'contain' ) {
//disable pull down refresh so user can scroll image
this.originalOverscrollBehaviorY = overscrollBehaviorY;
document.body.style.overscrollBehaviorY = 'none';
} else if (this.state.top === 0 && overscrollBehaviorY === 'none' && this.originalOverscrollBehaviorY !== undefined) {
//restore original value
document.body.style.overscrollBehaviorY = this.originalOverscrollBehaviorY;
this.originalOverscrollBehaviorY = undefined;
}
}

calculateNegativeSpace(scale) {
//get difference in dimension between container and scaled image
const { containerDimensions, imageDimensions } = this.state;
Expand Down
3 changes: 2 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ module.exports = {
devServer: {
contentBase: path.join(__dirname, "examples/dist"),
port: 3001
}
},
devtool: 'source-map'
};

0 comments on commit 7454d4f

Please sign in to comment.