Last active
December 23, 2016 12:20
-
-
Save luccitan/95894f621444a0f33b10664e35c72968 to your computer and use it in GitHub Desktop.
React Pan & Zoom SVG template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Imports logic there */ | |
export default class PanAndZoomSVG extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
cursor: { x: 0, y: 0 }, | |
translate: { x: 0, y: 0 }, | |
scale: 1, | |
}; | |
} | |
componentDidMount() { | |
this.area.addEventListener('mousedown', this.handleMouseDown); | |
this.area.addEventListener('mousemove', this.handleMouseMove); | |
this.area.addEventListener('mouseup', this.handleMouseUp); | |
this.area.addEventListener('wheel', this.handleZoom); | |
} | |
componentWillUnmount() { | |
/* Removing the event listeners for the svg */ | |
this.area.removeEventListener('mousedown', this.handleMouseDown); | |
this.area.removeEventListener('mousemove', this.handleMouseMove); | |
this.area.removeEventListener('mouseup', this.handleMouseUp); | |
this.area.removeEventListener('wheel', this.handleZoom); | |
} | |
/** | |
* `mousedown` event handler | |
* @param {MouseEvent} e : event object | |
*/ | |
handleMouseDown = (e) => { | |
this.setState({ cursor: { x: e.clientX, y: e.clientY } }); | |
}; | |
/** | |
* `mousemove` event handler | |
* @param {MouseEvent} e : event object | |
*/ | |
handleMouseMove = (e) => { | |
/** Pan if the click is a left-click (e.which needed for Safari compatibility) */ | |
if (e.buttons === 1 || e.which == 1) { | |
this.setState({ | |
translate: { | |
x: this.state.translate.x + (e.clientX - this.state.cursor.x), | |
y: this.state.translate.y + (e.clientY - this.state.cursor.y), | |
}, | |
cursor: { | |
x: e.clientX, | |
y: e.clientY, | |
}, | |
}); | |
} | |
}; | |
/** | |
* `mouseup` event handler | |
*/ | |
handleMouseUp = () => { | |
this.setState({ cursor: { x: 0, y: 0 } }); | |
}; | |
/** | |
* `wheel` event handler | |
* @param {MouseEvent} e : event object | |
*/ | |
handleZoom = (e) => { | |
/** No max or min scaling are handled there, but it is highly recommanded for User Experience */ | |
const sign = e.deltaY > 0 ? -1 : 1; | |
const step = 0.1; // common step to change the SVG scale | |
const x = e.clientX, y = e.clientY; | |
const newScale = this.state.scale + (step * sign); | |
const dr = (newScale - this.state.scale) / this.state.scale; | |
const newTranslate = { | |
x: this.state.translate.x - ((e.clientX - this.state.translate.x) * dr), | |
y: this.state.translate.y - ((e.clientY - this.state.translate.y) * dr), | |
}; | |
this.setState({ | |
translate: { | |
x: this.state.translate.x + (e.clientX - this.state.cursor.x), | |
y: this.state.translate.y + (e.clientY - this.state.cursor.y), | |
}, | |
scale: newScale, | |
}); | |
}, | |
render() { | |
const translate = `translate(${this.state.translate.x}, ${this.state.translate.y})`; | |
const scale = `scale(${this.state.scale})`; | |
const transform = `${translate} ${scale}`; | |
return ( | |
<div ref={(area) => { this.area = area; }} {/* personal logic there */}> | |
<svg transform={transform} {/* personal logic there */} /> | |
</div> | |
); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment