import * as React from 'react' ; import { applyToPoint , identity , inverse , scale , translate , transform , toCSS , } from 'transformation-matrix' ; import round from 'lodash/round' ; import mapValues from 'lodash/mapValues' ; import * as cx from 'classnames' ; const roundCoordinate = ( n ) => round ( n , 3 ) ; type Coords = { x : number ; y : number ; } ; type Props = React . HTMLAttributes < HTMLDivElement > & { originX : number ; originY : number ; scaleFactor : number ; onZoom ( scale , translateX , translateY ) : any ; } ; const classes = require ( './style.scss' ) ; class Zoomable extends React . PureComponent < Props , { } > { eventTarget : HTMLDivElement ; scrollable : HTMLDivElement ; private matrix = identity ( ) ; setEventTarget = ( node ) => this . eventTarget = node ; setScrollable = ( node ) => this . scrollable = node ; applyZoom ( f , { x : mouseX , y : mouseY } ) { this . props . onZoom ( this . props . scaleFactor * f , mouseX , mouseY ) ; } applyPan ( ) { } /**
     * Gets the original mouse position relative to the element
     * as if the coordinates were on the element without the transformations
     * applied to it.
     */ getMouseCoords ( ev ) { const { top , left } = this . eventTarget . getBoundingClientRect ( ) ; const { e : translateX , f : translateY } = this . matrix ; const x = ev . clientX - Math . round ( left ) + translateX ; const y = ev . clientY - Math . round ( top ) + translateY ; const inverseMatrix = inverse ( this . matrix ) ; const originalPoint = applyToPoint ( inverseMatrix , { x , y } ) ; return mapValues ( originalPoint , roundCoordinate ) as typeof originalPoint ; } handleMouseDown = ( e ) => { this . applyZoom ( e . button === 2 ? 0.9 : 1.1 , this . getMouseCoords ( e ) ) ; } ; handleMouseWheel = ( e ) => { const delta = e . deltaY ; this . applyZoom ( delta > 0 ? 0.9 : 1.1 , this . getMouseCoords ( e ) ) ; } ; handleContextMenu = ( e ) => { e . preventDefault ( ) ; } ; componentWillReceiveProps ( newProps ) { const { scaleFactor , originX , originY } = newProps ; this . matrix = transform ( identity ( ) , translate ( originX , originY ) , scale ( scaleFactor , scaleFactor ) , translate ( - originX , - originY ) ) ; console . log ( { ... newProps , matrix : this . matrix } ) ; } render ( ) { const { style , className , children , scaleFactor , onZoom , originX , originY , ... rest , } = this . props ; return ( < div ref = { this . setScrollable } className = { cx ( classes . root , className ) } onContextMenu = { this . handleContextMenu } onMouseDown = { this . handleMouseDown } onWheel = { this . handleMouseWheel } > < div { ... rest } ref = { this . setEventTarget } className = { classes . zoomable } style = { { ... style , transform : toCSS ( this . matrix ) , transformOrigin : '0 0' , } } > { children } < /div> < /div>); } } ; export default Zoomable ;