react-pixi-plot
Version:
A React component rendering a zoomable and draggable PIXI.js scene. Intended to render 2d plots
136 lines • 6.35 kB
JavaScript
import { CustomPIXIComponent, AppContext } from 'react-pixi-fiber';
import * as PIXI from 'pixi.js';
import { preventGlobalMouseEvents, restoreGlobalMouseEvents } from '../../globalEvents';
import React from 'react';
import { PixiPlotContext } from '../../PlotContext';
const TYPE = 'DraggableContainer';
class DraggableContainerBehavior {
constructor() {
this.customDisplayObject = (props) => {
const instance = new PIXI.Container();
instance.interactive = true;
instance.hitArea = { contains: () => true };
instance.on('rightdown', (e) => {
this.draggedInstance = instance;
this.props = props;
this.dragAnchor = e.data.getLocalPosition(this.draggedInstance.parent);
e.stopPropagation();
this.captureMouseEvents();
});
instance.on('touchStart', (e) => {
const originalEvent = e.data.originalEvent;
this.draggedInstance = instance;
if (originalEvent.targetTouches.length === 1) {
this.dragAnchor = this.eventRendererPosition(originalEvent.targetTouches.item(0));
}
else if (this.dragAnchor) {
delete this.dragAnchor;
}
});
return instance;
};
this.customWillDetach = (instance) => {
instance.removeAllListeners();
};
/**
* Correlates clicks on the renderer that happen
* in screen space coordinates to renderer coordinates.
* Used to understand where the mouse event occurred inside the renderer.
* @method
* @param {MouseEvent} mouseEvent The mouse event.
* @returns {PIXI.Point} The Point object containing the coordinates of the mouse,
* in the renderer's coordinates system.
*/
this.eventRendererPosition = (mouseEvent) => {
const mousePosition = new PIXI.Point();
this.props.pixiInteractionManager
.mapPositionToPoint(mousePosition, mouseEvent.clientX, mouseEvent.clientY);
return mousePosition;
};
/**
* @todo stop the preventDefault for contextmenu event listener
* from preventing right clicking in the entire Lodestone application.
* Isolates mouse events to the visualization by adding event listeners
* and preventing mouse defaults and event propagation.
* Handles cancelling the right mouse context menu.
* @method
* @param {object} e The mouse event.
* @returns {undefined}
*/
this.captureMouseEvents = () => {
preventGlobalMouseEvents();
document.addEventListener('mouseup', this.mouseUpListener, true);
document.addEventListener('mousemove', this.mouseMoveListener, true);
document.addEventListener('contextmenu', ev => ev.preventDefault(), true);
};
/**
* Handles mouse button release.
* Restores event listeners that were removed
* during {@link PixiVisualization#captureMouseEvents|captureMouseEvents},
* enables holding shift and selecting more points,
* and removing points from selections with the right mouse button.
* @method
* @param {object} e The mouse up event.
* @returns {undefined}
*/
this.mouseUpListener = (e) => {
restoreGlobalMouseEvents();
document.removeEventListener('mouseup', this.mouseUpListener, true);
document.removeEventListener('mousemove', this.mouseMoveListener, true);
document.removeEventListener('contextmenu', ev => ev.preventDefault(), true);
e.preventDefault();
delete this.dragAnchor;
};
/*handleTouchEnd = (e: React.TouchEvent<HTMLDivElement>) => {
if (e.targetTouches.length === 0) { // no more touches
delete this.dragAnchor;
}
}*/
/* = (e: React.TouchEvent<HTMLDivElement>) => {
if (e.targetTouches.length === 1) {
const touch = e.targetTouches.item(0);
const touchPosition = this.eventRendererPosition(touch);
if (this.dragAnchor !== undefined) {
this.drag(this.dragAnchor, touchPosition);
this.dragAnchor = touchPosition;
}
}
}*/
/**
* Handles mouse movement events.
* If a selection has already been started this handles expanding the selection,
* and handles panning to the new position if a pan is occuring.
* @method
* @param {object} e The mouse move event.
* @returns {undefined}
*/
this.mouseMoveListener = (e) => {
if (this.dragAnchor !== undefined) {
e.stopPropagation();
e.preventDefault();
const mousePosition = this.eventRendererPosition(e);
this.drag(this.dragAnchor, mousePosition);
this.dragAnchor = mousePosition;
}
};
/**
* Pans the view inside of the renderer based on the mouse position.
* @method
* @param {object} mousePosition The position seen inside the event which triggered
* {@link PixiVisualization#mouseMoveListener|mouseMoveListener}.
*/
this.drag = (from, to) => {
const { position } = this.draggedInstance;
const nextXPos = position.x + (to.x - from.x);
const nextYPos = position.y + (to.y - from.y);
this.draggedInstance.position.set(nextXPos, nextYPos);
this.props.dispatch({ type: 'drag', payload: { position: { x: nextXPos, y: nextYPos } } });
};
}
}
const DraggablePIXI = CustomPIXIComponent(new DraggableContainerBehavior(), TYPE);
const DraggableContainer = (props) => {
return (React.createElement(PixiPlotContext.Consumer, null, context => React.createElement(AppContext.Consumer, null, app => React.createElement(DraggablePIXI, { pixiInteractionManager: app.renderer.plugins.interaction, dispatch: context.dispatch }, props.children))));
};
export default DraggableContainer;
//# sourceMappingURL=DraggableContainer.js.map