Improve edge scrolling (#3950)
This PR: - moves the edge scrolling logic into a manager - adds a new Editor option for `edgeScrollDelay` - adds a new Editor option for `edgeScrollEaseDuration` When in a state that would trigger an edge scroll, a delay is added before the scrolling starts. When scrolling does start, it is eased in by a certain duration. ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `improvement` — Improving existing features ### Test Plan 1. Drag shapes, resize, or drag select to the edge of the screen 2. The screen should move - [x] Unit Tests ### Release Notes - Add a delay and easing to edge scrolling. --------- Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
This commit is contained in:
parent
ccc673b5af
commit
6c7b8febbf
12 changed files with 294 additions and 97 deletions
|
@ -679,8 +679,10 @@ export const defaultTldrawOptions: {
|
|||
readonly defaultSvgPadding: 32;
|
||||
readonly doubleClickDurationMs: 450;
|
||||
readonly dragDistanceSquared: 16;
|
||||
readonly edgeScrollDelay: 200;
|
||||
readonly edgeScrollDistance: 8;
|
||||
readonly edgeScrollSpeed: 20;
|
||||
readonly edgeScrollEaseDuration: 200;
|
||||
readonly edgeScrollSpeed: 25;
|
||||
readonly flattenImageBoundsExpand: 64;
|
||||
readonly flattenImageBoundsPadding: 16;
|
||||
readonly followChaseViewportSnap: 2;
|
||||
|
@ -780,6 +782,14 @@ export class Edge2d extends Geometry2d {
|
|||
ul: number;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export class EdgeScrollManager {
|
||||
constructor(editor: Editor);
|
||||
// (undocumented)
|
||||
editor: Editor;
|
||||
updateEdgeScrolling(elapsed: number): void;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export class Editor extends EventEmitter<TLEventMap> {
|
||||
constructor({ store, user, shapeUtils, bindingUtils, tools, getContainer, cameraOptions, assetOptions, initialState, autoFocus, inferDarkMode, options, }: TLEditorOptions);
|
||||
|
@ -883,6 +893,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
distributeShapes(shapes: TLShape[] | TLShapeId[], operation: 'horizontal' | 'vertical'): this;
|
||||
duplicatePage(page: TLPage | TLPageId, createId?: TLPageId): this;
|
||||
duplicateShapes(shapes: TLShape[] | TLShapeId[], offset?: VecLike): this;
|
||||
edgeScrollManager: EdgeScrollManager;
|
||||
readonly environment: EnvironmentManager;
|
||||
// @internal (undocumented)
|
||||
externalAssetContentHandlers: {
|
||||
|
@ -1754,9 +1765,6 @@ export interface MatModel {
|
|||
f: number;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function moveCameraWhenCloseToEdge(editor: Editor): void;
|
||||
|
||||
// @internal (undocumented)
|
||||
export function normalizeWheel(event: React.WheelEvent<HTMLElement> | WheelEvent): {
|
||||
x: number;
|
||||
|
@ -2573,8 +2581,12 @@ export interface TldrawOptions {
|
|||
// (undocumented)
|
||||
readonly dragDistanceSquared: number;
|
||||
// (undocumented)
|
||||
readonly edgeScrollDelay: number;
|
||||
// (undocumented)
|
||||
readonly edgeScrollDistance: number;
|
||||
// (undocumented)
|
||||
readonly edgeScrollEaseDuration: number;
|
||||
// (undocumented)
|
||||
readonly edgeScrollSpeed: number;
|
||||
// (undocumented)
|
||||
readonly flattenImageBoundsExpand: number;
|
||||
|
|
|
@ -136,6 +136,7 @@ export {
|
|||
type TLBindingUtilConstructor,
|
||||
} from './lib/editor/bindings/BindingUtil'
|
||||
export { ClickManager, type TLClickState } from './lib/editor/managers/ClickManager'
|
||||
export { EdgeScrollManager } from './lib/editor/managers/EdgeScrollManager'
|
||||
export { EnvironmentManager } from './lib/editor/managers/EnvironmentManager'
|
||||
export { HistoryManager } from './lib/editor/managers/HistoryManager'
|
||||
export { ScribbleManager, type ScribbleItem } from './lib/editor/managers/ScribbleManager'
|
||||
|
@ -361,7 +362,6 @@ export {
|
|||
setPointerCapture,
|
||||
stopEventPropagation,
|
||||
} from './lib/utils/dom'
|
||||
export { moveCameraWhenCloseToEdge } from './lib/utils/edgeScrolling'
|
||||
export { getIncrementedName } from './lib/utils/getIncrementedName'
|
||||
export { getPointerInfo } from './lib/utils/getPointerInfo'
|
||||
export { getSvgPathFromPoints } from './lib/utils/getSvgPathFromPoints'
|
||||
|
|
|
@ -125,6 +125,7 @@ import { parentsToChildren } from './derivations/parentsToChildren'
|
|||
import { deriveShapeIdsInCurrentPage } from './derivations/shapeIdsInCurrentPage'
|
||||
import { getSvgJsx } from './getSvgJsx'
|
||||
import { ClickManager } from './managers/ClickManager'
|
||||
import { EdgeScrollManager } from './managers/EdgeScrollManager'
|
||||
import { EnvironmentManager } from './managers/EnvironmentManager'
|
||||
import { FocusManager } from './managers/FocusManager'
|
||||
import { HistoryManager } from './managers/HistoryManager'
|
||||
|
@ -692,6 +693,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
this.root.enter(undefined, 'initial')
|
||||
|
||||
this.edgeScrollManager = new EdgeScrollManager(this)
|
||||
this.focusManager = new FocusManager(this, autoFocus)
|
||||
this.disposables.add(this.focusManager.dispose.bind(this.focusManager))
|
||||
|
||||
|
@ -791,6 +793,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*/
|
||||
readonly sideEffects: StoreSideEffects<TLRecord>
|
||||
|
||||
/**
|
||||
* A manager for moving the camera when the mouse is at the edge of the screen.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
edgeScrollManager: EdgeScrollManager
|
||||
|
||||
/**
|
||||
* A manager for ensuring correct focus. See FocusManager for details.
|
||||
*
|
||||
|
|
128
packages/editor/src/lib/editor/managers/EdgeScrollManager.ts
Normal file
128
packages/editor/src/lib/editor/managers/EdgeScrollManager.ts
Normal file
|
@ -0,0 +1,128 @@
|
|||
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.
|
||||
* @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))
|
||||
}
|
||||
}
|
|
@ -37,6 +37,8 @@ export interface TldrawOptions {
|
|||
readonly collaboratorCheckIntervalMs: number
|
||||
readonly cameraMovingTimeoutMs: number
|
||||
readonly hitTestMargin: number
|
||||
readonly edgeScrollDelay: number
|
||||
readonly edgeScrollEaseDuration: number
|
||||
readonly edgeScrollSpeed: number
|
||||
readonly edgeScrollDistance: number
|
||||
readonly coarsePointerWidth: number
|
||||
|
@ -73,7 +75,9 @@ export const defaultTldrawOptions = {
|
|||
collaboratorCheckIntervalMs: 1200,
|
||||
cameraMovingTimeoutMs: 64,
|
||||
hitTestMargin: 8,
|
||||
edgeScrollSpeed: 20,
|
||||
edgeScrollDelay: 200,
|
||||
edgeScrollEaseDuration: 200,
|
||||
edgeScrollSpeed: 25,
|
||||
edgeScrollDistance: 8,
|
||||
coarsePointerWidth: 12,
|
||||
coarseHandleRadius: 20,
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
import { Editor } from '../editor/Editor'
|
||||
import { Vec } from '../primitives/Vec'
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @internal
|
||||
*/
|
||||
function getEdgeProximityFactor(
|
||||
editor: Editor,
|
||||
position: number,
|
||||
dimension: number,
|
||||
isCoarse: boolean,
|
||||
insetStart: boolean,
|
||||
insetEnd: boolean
|
||||
) {
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the camera when the mouse is close to the edge of the screen.
|
||||
* @public
|
||||
*/
|
||||
export function moveCameraWhenCloseToEdge(editor: Editor) {
|
||||
if (!editor.inputs.isDragging || editor.inputs.isPanning || editor.getCameraOptions().isLocked)
|
||||
return
|
||||
|
||||
const {
|
||||
inputs: {
|
||||
currentScreenPoint: { x, y },
|
||||
},
|
||||
} = editor
|
||||
const zoomLevel = editor.getZoomLevel()
|
||||
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
|
||||
|
||||
const {
|
||||
isCoarsePointer,
|
||||
insets: [t, r, b, l],
|
||||
} = editor.getInstanceState()
|
||||
const proximityFactorX = getEdgeProximityFactor(editor, x, screenBounds.w, isCoarsePointer, l, r)
|
||||
const proximityFactorY = getEdgeProximityFactor(editor, y, screenBounds.h, isCoarsePointer, t, b)
|
||||
|
||||
if (proximityFactorX === 0 && proximityFactorY === 0) return
|
||||
|
||||
// Determines the base speed of the scroll
|
||||
const pxSpeed = editor.user.getEdgeScrollSpeed() * editor.options.edgeScrollSpeed
|
||||
const scrollDeltaX = (pxSpeed * proximityFactorX * screenSizeFactorX) / zoomLevel
|
||||
const scrollDeltaY = (pxSpeed * proximityFactorY * screenSizeFactorY) / zoomLevel
|
||||
|
||||
const camera = editor.getCamera()
|
||||
|
||||
editor.setCamera(new Vec(camera.x + scrollDeltaX, camera.y + scrollDeltaY, camera.z))
|
||||
}
|
|
@ -12,8 +12,8 @@ import {
|
|||
TLPointerEventInfo,
|
||||
TLShape,
|
||||
TLShapeId,
|
||||
TLTickEventInfo,
|
||||
Vec,
|
||||
moveCameraWhenCloseToEdge,
|
||||
pointInPolygon,
|
||||
polygonsIntersect,
|
||||
} from '@tldraw/editor'
|
||||
|
@ -62,8 +62,9 @@ export class Brushing extends StateNode {
|
|||
this.editor.updateInstanceState({ brush: null })
|
||||
}
|
||||
|
||||
override onTick = () => {
|
||||
moveCameraWhenCloseToEdge(this.editor)
|
||||
override onTick = ({ elapsed }: TLTickEventInfo) => {
|
||||
const { editor } = this
|
||||
editor.edgeScrollManager.updateEdgeScrolling(elapsed)
|
||||
}
|
||||
|
||||
override onPointerMove = () => {
|
||||
|
|
|
@ -14,11 +14,11 @@ import {
|
|||
TLShapeId,
|
||||
TLShapePartial,
|
||||
TLTextShape,
|
||||
TLTickEventInfo,
|
||||
Vec,
|
||||
VecLike,
|
||||
areAnglesCompatible,
|
||||
compact,
|
||||
moveCameraWhenCloseToEdge,
|
||||
} from '@tldraw/editor'
|
||||
import { kickoutOccludedShapes } from '../selectHelpers'
|
||||
|
||||
|
@ -72,8 +72,9 @@ export class Resizing extends StateNode {
|
|||
this.updateShapes()
|
||||
}
|
||||
|
||||
override onTick = () => {
|
||||
moveCameraWhenCloseToEdge(this.editor)
|
||||
override onTick = ({ elapsed }: TLTickEventInfo) => {
|
||||
const { editor } = this
|
||||
editor.edgeScrollManager.updateEdgeScrolling(elapsed)
|
||||
}
|
||||
|
||||
override onPointerMove: TLEventHandlers['onPointerMove'] = () => {
|
||||
|
|
|
@ -10,10 +10,10 @@ import {
|
|||
TLPointerEventInfo,
|
||||
TLShape,
|
||||
TLShapePartial,
|
||||
TLTickEventInfo,
|
||||
Vec,
|
||||
compact,
|
||||
isPageId,
|
||||
moveCameraWhenCloseToEdge,
|
||||
} from '@tldraw/editor'
|
||||
import {
|
||||
NOTE_ADJACENT_POSITION_SNAP_RADIUS,
|
||||
|
@ -101,12 +101,13 @@ export class Translating extends StateNode {
|
|||
this.dragAndDropManager.clear()
|
||||
}
|
||||
|
||||
override onTick = () => {
|
||||
override onTick = ({ elapsed }: TLTickEventInfo) => {
|
||||
const { editor } = this
|
||||
this.dragAndDropManager.updateDroppingNode(
|
||||
this.snapshot.movingShapes,
|
||||
this.updateParentTransforms
|
||||
)
|
||||
moveCameraWhenCloseToEdge(this.editor)
|
||||
editor.edgeScrollManager.updateEdgeScrolling(elapsed)
|
||||
}
|
||||
|
||||
override onPointerMove = () => {
|
||||
|
|
|
@ -4,7 +4,12 @@ import { TestEditor } from '../TestEditor'
|
|||
let editor: TestEditor
|
||||
|
||||
beforeEach(() => {
|
||||
editor = new TestEditor()
|
||||
editor = new TestEditor({
|
||||
options: {
|
||||
edgeScrollDelay: 0,
|
||||
edgeScrollEaseDuration: 0,
|
||||
},
|
||||
})
|
||||
editor.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
|
||||
})
|
||||
|
||||
|
@ -227,11 +232,13 @@ describe('CameraOptions.panSpeed', () => {
|
|||
.forceTick()
|
||||
expect(editor.getCamera()).toMatchObject({ x: 0, y: 0, z: 1.01 }) // 1 + 1
|
||||
})
|
||||
|
||||
it('Does not effect hand tool panning', () => {
|
||||
editor.setCameraOptions({ ...DEFAULT_CAMERA_OPTIONS, panSpeed: 2 })
|
||||
editor.setCurrentTool('hand').pointerDown(0, 0).pointerMove(5, 10).forceTick()
|
||||
expect(editor.getCamera()).toMatchObject({ x: 5, y: 10, z: 1 })
|
||||
})
|
||||
|
||||
it('Effects spacebar panning (2x)', () => {
|
||||
editor.setCameraOptions({ ...DEFAULT_CAMERA_OPTIONS, panSpeed: 2 })
|
||||
editor
|
||||
|
@ -241,6 +248,7 @@ describe('CameraOptions.panSpeed', () => {
|
|||
.forceTick()
|
||||
expect(editor.getCamera()).toMatchObject({ x: 10, y: 20, z: 1 })
|
||||
})
|
||||
|
||||
it('Effects spacebar panning (0.5x)', () => {
|
||||
editor.setCameraOptions({ ...DEFAULT_CAMERA_OPTIONS, panSpeed: 0.5 })
|
||||
editor
|
||||
|
@ -250,6 +258,7 @@ describe('CameraOptions.panSpeed', () => {
|
|||
.forceTick()
|
||||
expect(editor.getCamera()).toMatchObject({ x: 2.5, y: 5, z: 1 })
|
||||
})
|
||||
|
||||
it('Does not effect edge scroll panning', () => {
|
||||
const shapeId = createShapeId()
|
||||
const viewportScreenBounds = editor.getViewportScreenBounds()
|
||||
|
@ -261,11 +270,12 @@ describe('CameraOptions.panSpeed', () => {
|
|||
const shape = editor.getSelectedShapes()[0]
|
||||
editor.selectNone()
|
||||
// Move shape far beyond bounds to trigger edge scrolling at maximum speed
|
||||
editor.pointerDown(shape.x, shape.y).pointerMove(-5000, -5000).forceTick()
|
||||
expect(editor.getCamera()).toMatchObject({ x: 0, y: 0, z: 1 })
|
||||
editor.pointerDown(shape.x, shape.y).pointerMove(-5000, -5000)
|
||||
// At maximum speed and a zoom level of 1, the camera should move by 40px per tick if the screen
|
||||
// is wider than 1000 pixels, or by 40 * 0.612px if it is smaller.
|
||||
const newX = viewportScreenBounds.w < 1000 ? 40 * 0.612 : 40
|
||||
const newY = viewportScreenBounds.h < 1000 ? 40 * 0.612 : 40
|
||||
const newX = viewportScreenBounds.w < 1000 ? 25 * 0.612 : 25
|
||||
const newY = viewportScreenBounds.h < 1000 ? 25 * 0.612 : 25
|
||||
expect(editor.getCamera()).toMatchObject({ x: newX, y: newY, z: 1 })
|
||||
})
|
||||
})
|
||||
|
|
|
@ -16,7 +16,12 @@ const ids = {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
editor = new TestEditor()
|
||||
editor = new TestEditor({
|
||||
options: {
|
||||
edgeScrollDelay: 0,
|
||||
edgeScrollEaseDuration: 0,
|
||||
},
|
||||
})
|
||||
editor.setScreenBounds({ w: 3000, h: 3000, x: 0, y: 0 })
|
||||
})
|
||||
|
||||
|
@ -1951,3 +1956,88 @@ it('Ignores locked shapes when hovering', () => {
|
|||
editor.rightClick()
|
||||
expect(editor.getSelectedShapeIds()).toEqual([b.id])
|
||||
})
|
||||
|
||||
describe('Edge scrolling', () => {
|
||||
it('moves the camera correctly when delay and duration are zero', () => {
|
||||
editor = new TestEditor({
|
||||
options: {
|
||||
edgeScrollDelay: 0,
|
||||
edgeScrollEaseDuration: 0,
|
||||
},
|
||||
})
|
||||
editor.setScreenBounds({ w: 3000, h: 3000, x: 0, y: 0 })
|
||||
editor.user.updateUserPreferences({ edgeScrollSpeed: 1 })
|
||||
|
||||
editor.pointerMove(300, 300)
|
||||
editor.pointerDown()
|
||||
editor.pointerMove(0, 0)
|
||||
|
||||
expect(editor.getCamera()).toMatchObject({
|
||||
x: editor.options.edgeScrollSpeed,
|
||||
y: editor.options.edgeScrollSpeed,
|
||||
})
|
||||
|
||||
editor.forceTick()
|
||||
|
||||
expect(editor.getCamera()).toMatchObject({
|
||||
x: editor.options.edgeScrollSpeed * 2,
|
||||
y: editor.options.edgeScrollSpeed * 2,
|
||||
})
|
||||
})
|
||||
|
||||
it('moves the camera correctly when delay is 16 and duration are zero', () => {
|
||||
editor = new TestEditor({
|
||||
options: {
|
||||
edgeScrollDelay: 16,
|
||||
edgeScrollEaseDuration: 0,
|
||||
},
|
||||
})
|
||||
editor.setScreenBounds({ w: 3000, h: 3000, x: 0, y: 0 })
|
||||
editor.user.updateUserPreferences({ edgeScrollSpeed: 1 })
|
||||
|
||||
editor.pointerMove(300, 300)
|
||||
editor.pointerDown()
|
||||
editor.pointerMove(0, 0)
|
||||
|
||||
// one tick's length of delay
|
||||
expect(editor.getCamera()).toMatchObject({
|
||||
x: 0,
|
||||
y: 0,
|
||||
})
|
||||
|
||||
editor.forceTick()
|
||||
|
||||
expect(editor.getCamera()).toMatchObject({
|
||||
x: editor.options.edgeScrollSpeed,
|
||||
y: editor.options.edgeScrollSpeed,
|
||||
})
|
||||
})
|
||||
|
||||
it('moves the camera correctly when delay is 0 and duration is 32', () => {
|
||||
editor = new TestEditor({
|
||||
options: {
|
||||
edgeScrollDelay: 0,
|
||||
edgeScrollEaseDuration: 32,
|
||||
},
|
||||
})
|
||||
editor.setScreenBounds({ w: 3000, h: 3000, x: 0, y: 0 })
|
||||
editor.user.updateUserPreferences({ edgeScrollSpeed: 1 })
|
||||
|
||||
editor.pointerMove(300, 300)
|
||||
editor.pointerDown()
|
||||
editor.pointerMove(0, 0)
|
||||
|
||||
// one tick's length of delay
|
||||
expect(editor.getCamera()).toMatchObject({
|
||||
x: editor.options.edgeScrollSpeed * 0.125,
|
||||
y: editor.options.edgeScrollSpeed * 0.125,
|
||||
})
|
||||
|
||||
editor.forceTick()
|
||||
|
||||
expect(editor.getCamera()).toMatchObject({
|
||||
x: editor.options.edgeScrollSpeed * 1.125,
|
||||
y: editor.options.edgeScrollSpeed * 1.125,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -40,7 +40,12 @@ const ids = {
|
|||
|
||||
beforeEach(() => {
|
||||
console.error = jest.fn()
|
||||
editor = new TestEditor()
|
||||
editor = new TestEditor({
|
||||
options: {
|
||||
edgeScrollDelay: 0,
|
||||
edgeScrollEaseDuration: 0,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const getNumSnapPoints = (snap: SnapIndicator): number => {
|
||||
|
@ -139,10 +144,10 @@ describe('When translating...', () => {
|
|||
|
||||
const before = editor.getShape<TLGeoShape>(ids.box1)!
|
||||
|
||||
jest.advanceTimersByTime(100)
|
||||
editor.forceTick()
|
||||
editor
|
||||
// The change is bigger than expected because the camera moves
|
||||
.expectShapeToMatch({ id: ids.box1, x: -160, y: 10 })
|
||||
.expectShapeToMatch({ id: ids.box1, x: -65, y: 10 })
|
||||
// We'll continue moving in the x postion, but now we'll also move in the y position.
|
||||
// The speed in the y position is smaller since we are further away from the edge.
|
||||
.pointerMove(0, 25)
|
||||
|
@ -161,16 +166,21 @@ describe('When translating...', () => {
|
|||
editor.user.updateUserPreferences({ edgeScrollSpeed: 1 })
|
||||
editor.pointerDown(50, 50, ids.box1).pointerMove(1080, 50)
|
||||
|
||||
jest.advanceTimersByTime(100)
|
||||
editor.forceTick()
|
||||
editor.forceTick()
|
||||
editor.forceTick()
|
||||
editor
|
||||
// The change is bigger than expected because the camera moves
|
||||
.expectShapeToMatch({ id: ids.box1, x: 1160, y: 10 })
|
||||
.expectShapeToMatch({ id: ids.box1, x: 1115, y: 10 })
|
||||
.pointerMove(1080, 800)
|
||||
jest.advanceTimersByTime(100)
|
||||
|
||||
editor.forceTick()
|
||||
editor.forceTick()
|
||||
editor.forceTick()
|
||||
editor
|
||||
.expectShapeToMatch({ id: ids.box1, x: 1320, y: 845.68 })
|
||||
.expectShapeToMatch({ id: ids.box1, x: 1215, y: 805.9 })
|
||||
.pointerUp()
|
||||
.expectShapeToMatch({ id: ids.box1, x: 1340, y: 857.92 })
|
||||
.expectShapeToMatch({ id: ids.box1, x: 1240, y: 821.2 })
|
||||
})
|
||||
|
||||
it('translates multiple shapes', () => {
|
||||
|
|
Loading…
Reference in a new issue