UNPKG

react-pixi-plot

Version:

A React component rendering a zoomable and draggable PIXI.js scene. Intended to render 2d plots

111 lines 5.39 kB
import { CustomPIXIComponent, AppContext } from 'react-pixi-fiber'; import * as PIXI from 'pixi.js'; import React from 'react'; import normalizeWheel from 'normalize-wheel'; import { distance } from '../../utils'; import { PixiPlotContext } from '../../PlotContext'; const TYPE = 'ZoomableContainer'; /** * Sets the zoom of the view. * @method * @param delta The magitude of the zoom change. * @param mousePosition The location of the mouse when the zoom occured. */ const zoom = (instance, factorX, factorY, mousePosition) => { const { scale, position } = instance; const localPositionBefore = instance.toLocal(mousePosition); const nextXScale = factorX * scale.x; const nextYScale = factorY * scale.y; scale.set(nextXScale, nextYScale); const localPositionAfter = instance.toLocal(mousePosition); // reposition the container so that the mouse points to the same position after zooming const nextXPos = position.x + (localPositionAfter.x - localPositionBefore.x) * scale.x; const nextYPos = position.y + (localPositionAfter.y - localPositionBefore.y) * scale.y; position.set(nextXPos, nextYPos); instance._dispatch({ type: 'zoom', payload: { position: { x: nextXPos, y: nextYPos }, scale: { x: nextXScale, y: nextYScale }, } }); }; class ZoomableContainerBehavior { constructor() { this.customDisplayObject = (props) => { const instance = new PIXI.Container(); instance._dispatch = props.dispatch; props.app.view.addEventListener('wheel', (e) => { this.props = props; const normalizedEvent = normalizeWheel(e); const mousePosition = new PIXI.Point(); this.props.app.renderer. plugins.interaction. mapPositionToPoint(mousePosition, e.clientX, e.clientY); const zoomFactor = Math.pow(2, -normalizedEvent.pixelY / 500); zoom(instance, zoomFactor, zoomFactor, mousePosition); e.stopPropagation(); e.preventDefault(); }); /*props.app.view.addEventListener('touchstart', (e) => { const position = new PIXI.Point(e.touches.item(0).clientX, e.touches.item(0).clientY); const found = props.app.renderer.plugins.interaction.hitTest(position, zoomable); if (found) zoomable.emit('starttouch', e); });*/ return instance; }; this.customWillDetach = (instance) => { instance.removeAllListeners(); }; this.handleTouchStart = (e) => { if (e.targetTouches.length === 2) { const a = this.getTouchPosition(e.targetTouches.item(0)); const b = this.getTouchPosition(e.targetTouches.item(1)); this.initialPinchDistance = distance(a, b); } }; this.handleTouchEnd = (e) => { if (e.data.originalEvent.targetTouches.length === 1) { delete this.initialPinchDistance; } }; this.handleTouchMove = (e) => { const targetTouches = e.data.originalEvent.targetTouches; if (targetTouches.length === 2) { const a = this.getTouchPosition(targetTouches.item(0)); const b = this.getTouchPosition(targetTouches.item(1)); const newPinchDistance = distance(a, b); const zoomFactor = newPinchDistance / this.initialPinchDistance; zoom(e.target, zoomFactor, zoomFactor, a); } }; this.getTouchPosition = (mouseEvent) => { const mousePosition = new PIXI.Point(); this.props.app.renderer.plugins.interaction .mapPositionToPoint(mousePosition, mouseEvent.clientX, mouseEvent.clientY); return mousePosition; }; } customApplyProps(displayObject, oldProps, newProps) { this.applyDisplayObjectProps(oldProps, newProps); if (oldProps.dispatch !== newProps.dispatch) { displayObject._dispatch = newProps.dispatch; } if (oldProps.appWidth !== newProps.appWidth || oldProps.appWidth !== newProps.appHeight) { const updateZoom = () => { const bounds = displayObject.getBounds(); if (bounds.height !== 0 && bounds.width !== 0) { zoom(displayObject, newProps.appWidth / displayObject.width, newProps.appHeight / displayObject.height, new PIXI.Point()); } else { /** * Before the first render, the bounds are not calculated, so we wait a bit and try again */ setTimeout(updateZoom, 10); } }; updateZoom(); } } } const ZoomablePIXI = CustomPIXIComponent(new ZoomableContainerBehavior(), TYPE); const ZoomableContainer = props => (React.createElement(PixiPlotContext.Consumer, null, context => React.createElement(AppContext.Consumer, null, app => React.createElement(ZoomablePIXI, { app: app, dispatch: context.dispatch, appHeight: context.state.appHeight, appWidth: context.state.appWidth }, props.children)))); export default ZoomableContainer; //# sourceMappingURL=ZoomableContainer.js.map