UNPKG

@tldraw/editor

Version:

tldraw infinite canvas SDK (editor).

132 lines (118 loc) 4.09 kB
import { Vec } from '../../../primitives/Vec' import { EASINGS } from '../../../primitives/easings' import { Editor } from '../../Editor' /** @public */ export class EdgeScrollManager { constructor(public editor: Editor) {} private _isEdgeScrolling = false private _edgeScrollDuration = -1 /** * Update the camera position when the mouse is close to the edge of the screen. * Run this on every tick when in a state where edge scrolling is enabled. * * @public */ updateEdgeScrolling(elapsed: number) { const { editor } = this const edgeScrollProximityFactor = this.getEdgeScroll() if (edgeScrollProximityFactor.x === 0 && edgeScrollProximityFactor.y === 0) { if (this._isEdgeScrolling) { this._isEdgeScrolling = false this._edgeScrollDuration = 0 } } else { if (!this._isEdgeScrolling) { this._isEdgeScrolling = true this._edgeScrollDuration = 0 } this._edgeScrollDuration += elapsed if (this._edgeScrollDuration > editor.options.edgeScrollDelay) { const eased = editor.options.edgeScrollEaseDuration > 0 ? EASINGS.easeInCubic( Math.min( 1, this._edgeScrollDuration / (editor.options.edgeScrollDelay + editor.options.edgeScrollEaseDuration) ) ) : 1 this.moveCameraWhenCloseToEdge({ x: edgeScrollProximityFactor.x * eased, y: edgeScrollProximityFactor.y * eased, }) } } } /** * Helper function to get the scroll proximity factor for a given position. * @param position - The mouse position on the axis. * @param dimension - The component dimension on the axis. * @param isCoarse - Whether the pointer is coarse. * @param insetStart - Whether the pointer is inset at the start of the axis. * @param insetEnd - Whether the pointer is inset at the end of the axis. * @internal */ private getEdgeProximityFactors( position: number, dimension: number, isCoarse: boolean, insetStart: boolean, insetEnd: boolean ) { const { editor } = this const dist = editor.options.edgeScrollDistance const pw = isCoarse ? editor.options.coarsePointerWidth : 0 // pointer width const pMin = position - pw const pMax = position + pw const min = insetStart ? 0 : dist const max = insetEnd ? dimension : dimension - dist if (pMin < min) { return Math.min(1, (min - pMin) / dist) } else if (pMax > max) { return -Math.min(1, (pMax - max) / dist) } return 0 } private getEdgeScroll() { const { editor } = this const { inputs: { currentScreenPoint: { x, y }, }, } = editor const screenBounds = editor.getViewportScreenBounds() const { isCoarsePointer, insets: [t, r, b, l], } = editor.getInstanceState() const proximityFactorX = this.getEdgeProximityFactors(x, screenBounds.w, isCoarsePointer, l, r) const proximityFactorY = this.getEdgeProximityFactors(y, screenBounds.h, isCoarsePointer, t, b) return { x: proximityFactorX, y: proximityFactorY, } } /** * Moves the camera when the mouse is close to the edge of the screen. * @public */ private moveCameraWhenCloseToEdge(proximityFactor: { x: number; y: number }) { const { editor } = this if (!editor.inputs.isDragging || editor.inputs.isPanning || editor.getCameraOptions().isLocked) return if (proximityFactor.x === 0 && proximityFactor.y === 0) return const screenBounds = editor.getViewportScreenBounds() // Determines how much the speed is affected by the screen size const screenSizeFactorX = screenBounds.w < 1000 ? 0.612 : 1 const screenSizeFactorY = screenBounds.h < 1000 ? 0.612 : 1 // Determines the base speed of the scroll const zoomLevel = editor.getZoomLevel() const pxSpeed = editor.user.getEdgeScrollSpeed() * editor.options.edgeScrollSpeed const scrollDeltaX = (pxSpeed * proximityFactor.x * screenSizeFactorX) / zoomLevel const scrollDeltaY = (pxSpeed * proximityFactor.y * screenSizeFactorY) / zoomLevel // update the camera const { x, y, z } = editor.getCamera() editor.setCamera(new Vec(x + scrollDeltaX, y + scrollDeltaY, z)) } }