UNPKG

react-zoom-pan-pinch

Version:
140 lines (121 loc) 4.27 kB
/* eslint-disable no-param-reassign */ import { ReactZoomPanPinchContext } from "../../models"; import { handleCancelAnimation } from "../animations/animations.utils"; import { handleAlignToScaleBounds } from "../zoom/zoom.logic"; import { calculatePinchZoom, calculateTouchMidPoint, getTouchDistance, } from "./pinch.utils"; import { getMouseBoundedPosition, handleCalculateBounds, } from "../bounds/bounds.utils"; import { handleCalculateZoomPositions } from "../zoom/zoom.utils"; import { getPaddingValue } from "core/pan/panning.utils"; const getTouchCenter = (event: TouchEvent) => { let totalX = 0; let totalY = 0; // Sum up the positions of all touches for (let i = 0; i < 2; i += 1) { totalX += event.touches[i].clientX; totalY += event.touches[i].clientY; } // Calculate the average position const x = totalX / 2; const y = totalY / 2; return { x, y }; }; export const handlePinchStart = ( contextInstance: ReactZoomPanPinchContext, event: TouchEvent, ): void => { const distance = getTouchDistance(event); contextInstance.pinchStartDistance = distance; contextInstance.lastDistance = distance; contextInstance.pinchStartScale = contextInstance.state.scale; contextInstance.isPanning = false; contextInstance.isPinching = true; contextInstance.pinchPreviousCenter = getTouchCenter(event); handleCancelAnimation(contextInstance); }; export const handlePinchZoom = ( contextInstance: ReactZoomPanPinchContext, event: TouchEvent, ): void => { const { contentComponent, pinchStartDistance, wrapperComponent, pinchPreviousCenter, } = contextInstance; const { scale } = contextInstance.state; const { limitToBounds, centerZoomedOut, zoomAnimation, autoAlignment, pinch, panning, } = contextInstance.setup; const { disabled, size } = zoomAnimation; const { allowPanning } = pinch; // if one finger starts from outside of wrapper if (pinchStartDistance === null || !contentComponent) return; const midPoint = calculateTouchMidPoint(event, scale, contentComponent); // if touches goes off of the wrapper element if (!Number.isFinite(midPoint.x) || !Number.isFinite(midPoint.y)) return; const currentDistance = getTouchDistance(event); const newScale = calculatePinchZoom(contextInstance, currentDistance); const center = getTouchCenter(event); // pan should be scale invariant. const scaleDiff = scale / newScale; const panX = (center.x - (pinchPreviousCenter?.x || 0)) * scaleDiff; const panY = (center.y - (pinchPreviousCenter?.y || 0)) * scaleDiff; if (newScale === scale && panX === 0 && panY === 0) return; contextInstance.pinchPreviousCenter = center; const bounds = handleCalculateBounds(contextInstance, newScale); const isPaddingDisabled = disabled || size === 0 || centerZoomedOut; const isLimitedToBounds = limitToBounds && isPaddingDisabled; const { x, y } = handleCalculateZoomPositions( contextInstance, midPoint.x, midPoint.y, newScale, bounds, isLimitedToBounds, ); contextInstance.pinchMidpoint = midPoint; contextInstance.lastDistance = currentDistance; if (panning.disabled || !allowPanning) { contextInstance.setState(newScale, x, y); } else { const { sizeX, sizeY } = autoAlignment; const paddingValueX = getPaddingValue(contextInstance, sizeX, newScale); const paddingValueY = getPaddingValue(contextInstance, sizeY, newScale); const newPositionX = x + panX; const newPositionY = y + panY; const { x: finalX, y: finalY } = getMouseBoundedPosition( newPositionX, newPositionY, bounds, limitToBounds, paddingValueX, paddingValueY, wrapperComponent, ); contextInstance.setState(newScale, finalX, finalY); } }; export const handlePinchStop = ( contextInstance: ReactZoomPanPinchContext, ): void => { const { pinchMidpoint } = contextInstance; contextInstance.velocity = null; contextInstance.lastDistance = null; contextInstance.pinchMidpoint = null; contextInstance.pinchStartScale = null; contextInstance.pinchStartDistance = null; contextInstance.isPinching = false; handleAlignToScaleBounds(contextInstance, pinchMidpoint?.x, pinchMidpoint?.y); };