We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
/* https://github.com/GoogleChromeLabs/pinch-zoom */ import React, { Component } from 'react'; import PointerTracker, { Pointer } from 'pointer-tracker'; import { Point } from './PropsType'; let cachedSvg: SVGSVGElement; function getSVG(): SVGSVGElement { if (cachedSvg) { return cachedSvg; } cachedSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); return cachedSvg; } function createMatrix(): SVGMatrix { return getSVG().createSVGMatrix(); } function createPoint(): SVGPoint { return getSVG().createSVGPoint(); } function getDistance(a: Point, b?: Point): number { if (!b) return 0; return Math.sqrt((b.clientX - a.clientX) ** 2 + (b.clientY - a.clientY) ** 2); } function getMidpoint(a: Point, b?: Point): Point { if (!b) return a; return { clientX: (a.clientX + b.clientX) / 2, clientY: (a.clientY + b.clientY) / 2, }; } export interface PinchZoomProps { prefixCls?: string; className?: string; onChange?: Function; } export default class PinchZoom extends Component<PinchZoomProps, any> { private _container; private _transform: SVGMatrix = createMatrix(); private _positioningEl?: Element; constructor(props) { super(props); this._container = React.createRef(); } get x() { return this._transform.e; } get scale() { return this._transform.a; } get y() { return this._transform.f; } componentDidMount() { this._positioningEl = this._container.current.children[0]; const pointerTracker: PointerTracker = new PointerTracker(this._container.current, { start: (_pointer, event) => { // We only want to track 2 pointers at most if (pointerTracker.currentPointers.length === 2 || !this._positioningEl) return false; event.preventDefault(); return true; }, move: (previousPointers, _changePointers, event) => { // event.stopPropagation(); if (this.scale === 1) return this._onPointerMove(previousPointers, pointerTracker.currentPointers); // return fasle }, // end: () }); this._container.current.addEventListener('wheel', (event) => this._onWheel(event)); } _onWheel = ( event ) => { event.preventDefault(); const currentRect = this._positioningEl!.getBoundingClientRect(); let { deltaY } = event; const { ctrlKey, deltaMode } = event; if (deltaMode === 1) { // 1 is "lines", 0 is "pixels" // Firefox uses "lines" for some types of mouse deltaY *= 15; } // ctrlKey is true when pinch-zooming on a trackpad. const divisor = ctrlKey ? 100 : 300; const scaleDiff = 1 - deltaY / divisor; this.applyChange({ scaleDiff, originX: event.clientX - currentRect.left, originY: event.clientY - currentRect.top, allowChangeEvent: true, }); }; private _onPointerMove(previousPointers: Pointer[], currentPointers: Pointer[]) { if (!this._positioningEl) return; // Combine next points with previous points const currentRect = this._positioningEl.getBoundingClientRect(); // For calculating panning movement const prevMidpoint = getMidpoint(previousPointers[0], previousPointers[1]); const newMidpoint = getMidpoint(currentPointers[0], currentPointers[1]); // Midpoint within the element const originX = prevMidpoint.clientX - currentRect.left; const originY = prevMidpoint.clientY - currentRect.top; // Calculate the desired change in scale const prevDistance = getDistance(previousPointers[0], previousPointers[1]); const newDistance = getDistance(currentPointers[0], currentPointers[1]); const scaleDiff = prevDistance ? newDistance / prevDistance : 1; this.applyChange({ originX, originY, scaleDiff, panX: newMidpoint.clientX - prevMidpoint.clientX, panY: newMidpoint.clientY - prevMidpoint.clientY, }); } applyChange = (opts) => { const { panX = 0, panY = 0, originX = 0, originY = 0, scaleDiff = 1, } = opts; const matrix = createMatrix() // Translate according to panning. .translate(panX, panY) // Scale about the origin. .translate(originX, originY) // Apply current translate .translate(this.x, this.y) .scale(scaleDiff) .translate(-originX, -originY) // Apply current scale. .scale(this.scale); // Convert the transform into basic translate & scale. this.setTransform({ scale: matrix.a, x: matrix.e, y: matrix.f, }); } /** * Update the stage with a given scale/x/y. */ setTransform(opts) { const { scale = this.scale, } = opts; let { x = this.x, y = this.y, } = opts; // If we don't have an element to position, just set the value as given. // We'll check bounds later. if (!this._positioningEl) { this._updateTransform(scale, x, y); return; } // Get current layout const thisBounds = this._container.current.getBoundingClientRect(); const positioningElBounds = this._positioningEl.getBoundingClientRect(); // Not displayed. May be disconnected or display:none. // Just take the values, and we'll check bounds later. if (!thisBounds.width || !thisBounds.height) { this._updateTransform(scale, x, y); return; } // Create points for _positioningEl. let topLeft = createPoint(); topLeft.x = positioningElBounds.left - thisBounds.left; topLeft.y = positioningElBounds.top - thisBounds.top; let bottomRight = createPoint(); bottomRight.x = positioningElBounds.width + topLeft.x; bottomRight.y = positioningElBounds.height + topLeft.y; // Calculate the intended position of _positioningEl. const matrix = createMatrix() .translate(x, y) .scale(scale) // Undo current transform .multiply(this._transform.inverse()); topLeft = topLeft.matrixTransform(matrix); bottomRight = bottomRight.matrixTransform(matrix); // Ensure _positioningEl can't move beyond out-of-bounds. // Correct for x if (topLeft.x > thisBounds.width) { x += thisBounds.width - topLeft.x; } else if (bottomRight.x < 0) { x += -bottomRight.x; } // Correct for y if (topLeft.y > thisBounds.height) { y += thisBounds.height - topLeft.y; } else if (bottomRight.y < 0) { y += -bottomRight.y; } let x1 = x; let y1 = y; let s = scale; if (s <= 1) { s = 1; x1 = 0; y1 = 0; } this._updateTransform(s, x1, y1); } /** * Update transform values without checking bounds. This is only called in setTransform. */ private _updateTransform(scale: number, x: number, y: number) { // Avoid scaling to zero // Return if there's no change if ( scale === this.scale && x === this.x && y === this.y ) return; this._transform.e = x; this._transform.f = y; this._transform.d = this._transform.a = scale; this._container.current.style.setProperty('--x', this.x + 'px'); this._container.current.style.setProperty('--y', this.y + 'px'); this._container.current.style.setProperty('--scale', this.scale + ''); const { onChange } = this.props; if (typeof onChange === 'function') { onChange({ x, y, scale, }); } } render() { const { children, className } = this.props; return (<div ref={this._container} className={`${className} pinch-zoom`}>{children}</div>); } }
The text was updated successfully, but these errors were encountered:
发现 ios 不支持,且不能阻止冒泡
Sorry, something went wrong.
No branches or pull requests
The text was updated successfully, but these errors were encountered: