UNPKG

labo-components

Version:
174 lines (147 loc) 5.37 kB
import React from "react"; import PropTypes from "prop-types"; import IDUtil from "../../../../util/IDUtil"; const MODE_MOVE = 0; // move/drag the timeline const MODE_SET = 1; // set the current position/cursor // Handle zoom, drag, and set cursor position all in a single box that can wrap the dynamic timeline components class ZoomDragBox extends React.Component { constructor(props) { super(props); // refs this.ref = React.createRef(); // default values this.dragCursorOffset = this.props.dragCursorOffset ? this.props.dragCursorOffset : 10; this.zoomSensitivity = this.props.zoomSensitivity ? this.props.zoomSensitivity : 500; this.doubleClickTime = this.props.doubleClickTime ? this.props.doubleClickTime : 200; this.doubleClickOffset = this.props.doubleClickOffset ? this.props.doubleClickOffset : 5; this.boundingBox = { x: 0, y: 0, width: 0, height: 0 }; } componentDidMount() { this.ref.current.addEventListener("wheel", this.onWheel, { passive: false, }); } componentWillUnmount() { this.clearEventListeners(); this.ref.current.removeEventListener("wheel", this.onWheel); } onMouseDown = (e) => { const leftMouseButton = e.button === 0; const time = new Date().getTime(); let forceModeSet = false; // check for ctrlKey, which enables MODE_SET if (e.ctrlKey) { forceModeSet = true; } // check for double click, which enables MODE_SET if ( leftMouseButton && this.interactionVars && time - this.interactionVars.lastDown < this.doubleClickTime && e.pageX - this.interactionVars.downX < this.doubleClickOffset ) { forceModeSet = true; } // initialize interaction variables this.interactionVars = { downX: e.pageX, offsetX: 0, mode: forceModeSet ? MODE_SET : MODE_MOVE, lastDown: leftMouseButton ? time : 0, }; // If click is nearby the cursor, enable SET mode const cursorPos = this.props.start + (e.pageX - this.props.boundingBox.x) / this.props.pixelsPerSecond; if ( // near cursor Math.abs(cursorPos - this.props.position) < this.dragCursorOffset / this.props.pixelsPerSecond || // label region (left + right) (e.clientY - this.props.boundingBox.y < 25 && Math.abs(cursorPos - this.props.position) < 60 / this.props.pixelsPerSecond) ) { this.interactionVars.mode = MODE_SET; this.interactionVars.offsetX = cursorPos - this.props.position; } // add event listeners document.addEventListener("mousemove", this.onMouseMove); document.addEventListener("mouseup", this.onMouseUp); // trigger mousemove this.onMouseMove(e); }; onMouseMove = (e) => { e.preventDefault(); const cursorPos = (e.pageX - this.props.boundingBox.x) / this.props.pixelsPerSecond; switch (this.interactionVars.mode) { // set cursor case MODE_SET: // set position this.props.setPosition( this.props.start + cursorPos - this.interactionVars.offsetX, true ); return; case MODE_MOVE: // drag timeline const posDiff = (this.interactionVars.downX - e.pageX) / this.props.pixelsPerSecond; this.interactionVars.downX = e.pageX; this.props.onMove(posDiff, !e.shiftKey && !e.ctrlKey); return; default: console.error("Unknown mode:", this.interactionVars.mode); } }; onMouseUp = (e) => { this.clearEventListeners(); }; clearEventListeners = () => { document.removeEventListener("mousemove", this.onMouseMove); document.removeEventListener("mouseup", this.onMouseUp); }; // scroll wheel onWheel = (e) => { const perc = (e.pageX - this.props.boundingBox.x) / this.props.boundingBox.width; this.props.onZoom(1 - e.deltaY / this.zoomSensitivity, perc); // prevent scrolling e.preventDefault(); e.stopPropagation(); }; render() { return ( <div ref={this.ref} className={IDUtil.cssClassName("tl-zoom-drag-box")} onMouseDown={this.onMouseDown} > {this.props.children} </div> ); } } ZoomDragBox.propTypes = { start: PropTypes.number.isRequired, position: PropTypes.number.isRequired, setPosition: PropTypes.func.isRequired, onMove: PropTypes.func.isRequired, onZoom: PropTypes.func.isRequired, // optional dragCursorOffset: PropTypes.number, zoomSensitivity: PropTypes.number, doubleClickTime: PropTypes.number, doubleClickOffset: PropTypes.number, }; export default ZoomDragBox;