Skip to content

Instantly share code, notes, and snippets.

@luccitan
Last active December 23, 2016 12:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luccitan/95894f621444a0f33b10664e35c72968 to your computer and use it in GitHub Desktop.
Save luccitan/95894f621444a0f33b10664e35c72968 to your computer and use it in GitHub Desktop.
React Pan & Zoom SVG template
/** 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