From cda9bc8cd2047685991f8981581885016b08c533 Mon Sep 17 00:00:00 2001 From: Brad Date: Fri, 1 Feb 2019 19:50:37 -0700 Subject: [PATCH] Support doubleTapBehavior --- .npmignore | 1 + LICENSE | 2 +- README.md | 5 +++-- examples/src/index.js | 2 +- src/PinchZoomPan.js | 37 ++++++++++++++++++++++++++++++------- src/Utils.js | 2 +- 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/.npmignore b/.npmignore index 2eb55b2..a7e576b 100644 --- a/.npmignore +++ b/.npmignore @@ -5,4 +5,5 @@ examples .gitignore .prettierignore .vs +.vscode webpack.config.js \ No newline at end of file diff --git a/LICENSE b/LICENSE index 080f418..67cbdc0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Markus Englund +Copyright (c) 2018 - Present Brad Stiff Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5f79584..6cf96b3 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,11 @@ Prop | Type | Default | Description ------------|-----------|-----------|-------------------------------------------------------------------------------------------------------------------- initialTop | number | 0 | The initial top coordinate of the image with respect to the container. initialLeft | number | 0 | The initial left coordinate of the image wiht respect to the container. -initialScale| number | auto | The initial scale of the image. When `auto`, the image will be proportionally 'autofit' to the container. -minScale | number | auto | The minimum scale to which the image can be zoomed out. When `auto`, the minimum scale is the 'autofit' scale. +initialScale| number | 'auto' | The initial scale of the image. When `auto`, the image will be proportionally 'autofit' to the container. +minScale | number | 'auto' | The minimum scale to which the image can be zoomed out. When `auto`, the minimum scale is the 'autofit' scale. maxScale | number | 1 | The maximum scale to which the image can be zoomed in. zoomButtons | bool | true | Render plus (+) and minus (-) buttons on top of the image as another way to access the zoom feature. +doubleTapBehavior | 'reset' or 'zoom' | 'reset' | Whether to zoom in or reset to initial scale on double-click / double-tap. ## Development diff --git a/examples/src/index.js b/examples/src/index.js index 656339e..834525b 100644 --- a/examples/src/index.js +++ b/examples/src/index.js @@ -6,7 +6,7 @@ const ExplicitContainerView = ({menu}) => (
- + Demo Image
diff --git a/src/PinchZoomPan.js b/src/PinchZoomPan.js index 1f7f82d..abf0a4d 100644 --- a/src/PinchZoomPan.js +++ b/src/PinchZoomPan.js @@ -60,6 +60,7 @@ export default class PinchZoomPan extends React.Component { handleTouchEnd = (event) => { if (event.touches && event.touches.length > 0) return null; + this.cancelAnimation(); //We allow transient +/-5% over-pinching. //Animate the bounce back to constraints if applicable. @@ -83,6 +84,7 @@ export default class PinchZoomPan extends React.Component { } handleMouseUp = (event) => { + this.cancelAnimation(); this.pointerUp(event.timeStamp); if (this.mouseDown) { this.mouseDown = false; @@ -116,9 +118,17 @@ export default class PinchZoomPan extends React.Component { } } + handleZoomInClick = () => { + this.cancelAnimation(); + this.zoomIn(); + } + + handleZoomOutClick = () => { + this.cancelAnimation(); + this.zoomOut(); + } + handleWindowResize = () => this.maybeHandleDimensionsChanged(); - handleZoomInClick = () => this.zoomIn(); - handleZoomOutClick = () => this.zoomOut(); suppressEvent = event => event.preventDefault(); handleRefImage = ref => { @@ -164,13 +174,21 @@ export default class PinchZoomPan extends React.Component { pointerUp(timeStamp) { if (this.lastPointerUpTimeStamp && this.lastPointerUpTimeStamp + DOUBLE_TAP_THRESHOLD > timeStamp) { - //reset - this.applyInitialTransform(ANIMATION_SPEED); + this.doubleTap(this.lastPanPointerPosition); } this.lastPointerUpTimeStamp = timeStamp; } + doubleTap(pointerPosition) { + if (String(this.props.doubleTapBehavior).toLowerCase() === 'zoom' && this.state.scale * (1 + OVERZOOM_TOLERANCE) < this.props.maxScale) { + this.zoomIn(pointerPosition, ANIMATION_SPEED, 0.3); + } else { + //reset + this.applyInitialTransform(ANIMATION_SPEED); + } + } + move(top, left, tolerance, speed = 0) { if (!this.isTransformInitialized) { return; @@ -193,12 +211,12 @@ export default class PinchZoomPan extends React.Component { this.lastPinchLength = length; } - zoomIn(midpoint) { + zoomIn(midpoint, speed = 0, factor = 0.05) { midpoint = midpoint || { x: this.state.containerDimensions.width / 2, y: this.state.containerDimensions.height / 2 }; - this.zoom(this.state.scale * 1.05, midpoint, 0); + this.zoom(this.state.scale * (1 + factor), midpoint, 0, speed); } zoomOut(midpoint) { @@ -293,7 +311,10 @@ export default class PinchZoomPan extends React.Component { scale: snapToTarget(this.state.scale + (speed * translateScale), scale, SNAP_TOLERANCE), }; - this.setState(nextTransform, () => this.animation = requestAnimationFrame(frame)); + //animation runs until we reach the target + if (!isEqualTransform(nextTransform, this.state)) { + this.setState(nextTransform, () => this.animation = requestAnimationFrame(frame)); + } }; this.animation = requestAnimationFrame(frame); } else { @@ -458,6 +479,7 @@ PinchZoomPan.defaultProps = { minScale: 'auto', maxScale: 1, zoomButtons: true, + doubleTapBehavior: 'reset' }; PinchZoomPan.propTypes = { @@ -474,4 +496,5 @@ PinchZoomPan.propTypes = { ]), maxScale: PropTypes.number, zoomButtons: PropTypes.bool, + doubleTapBehavior: PropTypes.oneOf(['reset', 'zoom']), }; diff --git a/src/Utils.js b/src/Utils.js index f4e9732..ed6cad1 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -91,7 +91,7 @@ export const getMinScale = createSelector( state => state.imageDimensions, (state, props) => props.minScale, (containerDimensions, imageDimensions, minScaleProp) => - minScaleProp === 'auto' + String(minScaleProp).toLowerCase() === 'auto' ? calculateAutofitScale(containerDimensions, imageDimensions) : minScaleProp || 1 )