import React, {
PureComponent, createRef } from 'react'
import PropTypes from 'prop-types'
// interface POSIF {
// posX: number;
// posY: number;
// }
// interface IProps {
// // children: PropTypes.node.isRequired,
// className ?: string;
// style?: React.CSSProperties;
// minZoom?: number;
// maxZoom?: number;
// scrollVelocity?: number;
// onZoomChange?: (pos: POSIF) => void;
// onPanChange?: (pos: POSIF) => void;
// animDuration?: number;
// doubleTouchMaxDelay?: number;
// decelerationDuration?: number;
// }
// interface IState {
// zoom: number;
// posX: number;
// posY: number;
// cursor: string;
// transitionDuration: number;
// }
export default class PrismaZoom extends PureComponent {
static propTypes = {
children: PropTypes.node.isRequired,
className: PropTypes.string,
style: PropTypes.object,
minZoom: PropTypes.number,
maxZoom: PropTypes.number,
scrollVelocity: PropTypes.number,
onZoomChange: PropTypes.func,
onPanChange: PropTypes.func,
animDuration: PropTypes.number,
doubleTouchMaxDelay: PropTypes.number,
decelerationDuration: PropTypes.number
}
static defaultProps = {
// Class name to apply on the zoom wrapper
className: null,
// Style to apply on the zoom wrapper
style: {
},
// Minimum zoom ratio
minZoom: 1,
// Maximum zoom ratio
maxZoom: 5,
// Zoom increment or decrement on each scroll wheel detection
scrollVelocity: 0.2,
// Function called each time the zoom value changes
onZoomChange: null,
// Function called each time the posX or posY value changes (aka images was panned)
onPanChange: null,
// Animation duration (in seconds)
animDuration: 0.25,
// Max delay between two taps to consider a double tap (in milliseconds)
doubleTouchMaxDelay: 300,
// Decelerating movement duration after a mouse up or a touch end event (in milliseconds)
decelerationDuration: 750
}
static defaultState = {
// Transform scale value property
zoom: 1,
// Transform translateX value property
posX: 0,
// Transform translateY value property
posY: 0,
// Cursor style property
cursor: 'auto'
}
constructor (props) {
super(props)
// Reference to the main element
this.ref = createRef()
// Last cursor position
this.lastCursor = null
// Last touch position
this.lastTouch = null
// Last touch time in milliseconds
this.lastTouchTime = 0
// Last double tap time (used to limit multiple double tap) in milliseconds
this.lastDoubleTapTime = 0
// Last calculated distance between two fingers in pixels
this.lastTouchDistance = null
// Last request animation frame identifier
this.lastRequestAnimationId = null
this.state = {
...this.constructor.defaultState,
transitionDuration: props.animDuration
}
}
/**
* Calculates new translate positions for CSS transformations.
* @param {Number} x Relative (rect-based) X position in pixels
* @param {Number} y Relative (rect-based) Y position in pixels
* @param {Number} zoom Scale value
* @return {Array} New X and Y positions
*/
getNewPosition = (x, y, zoom) => {
const [prevZoom, prevPosX, prevPosY] = [this.state.zoom, this.state.posX, this.state.posY]
if (zoom === 1) {
return [0, 0]
}
if (zoom > prevZoom) {
// Get container coordinates
const rect = this.ref.current.getBoundingClientRect()
// Retrieve rectangle dimensions and mouse position
const [centerX, centerY] = [rect.width / 2, rect.height / 2]
const [relativeX, relativeY] = [x - rect.left - window.pageXOffset, y - rect.top - window.pageYOffset]
// If we are zooming down, we must try to center to mouse position
const [absX, absY] = [(centerX - relativeX) / prevZoom, (centerY - relativeY) / prevZoom]
const ratio = zoom - prevZoom
return [
prevPosX + (absX * ratio),
prevPosY + (absY * ratio)
]
} else {
// If we are zooming down, we shall re-center the element
return [
(prevPosX * (zoom - 1)) / (prevZoom - 1),
(prevPosY * (zoom - 1)) / (prevZoom - 1)
]
}
}
/**
* Calculates the narrowed shift for panning actions.
* @param {Number} shift Initial shift in pixels
* @param {Number} minLimit Minimum limit (left or top) in pixels
* @param {Number} maxLimit Maximum limit (right or bottom) in pixels
* @param {Number} minElement Left or top element position in pixels
* @param {Number} maxElement Right or bottom element position in pixels
* @return {Number} Narrowed shift
*/
getLimitedShift = (shift, minLimit, maxLimit, minElement, maxElement) => {
if (shift > 0) {
if (minElement > minLimit) {
// Forbid move if we are moving to left or top while we are already out minimum boudaries
return 0
} else if (minElement + shift > minLimit) {
// Lower the shift if we are going out boundaries
return minLimit - minElement
}
} else if (shift < 0) {
if (maxElement
React实现图片缩放效果
最新推荐文章于 2024-08-13 08:01:52 发布
本文介绍了如何在React应用中实现图片的缩放效果,通过利用React组件化思想和CSS样式技巧,详细讲解了从点击按钮到图片平滑缩放的完整过程,包括关键代码实现和效果展示。
摘要由CSDN通过智能技术生成