kopia lustrzana https://github.com/Tldraw/Tldraw
move camera state out, trim unused methods
rodzic
849f94851d
commit
0e50cd4f36
|
@ -127,11 +127,21 @@ The `behavior` property defines which logic should be used when calculating the
|
|||
|
||||
There are several `Editor` methods available for controlling the camera.
|
||||
|
||||
| Method | Description |
|
||||
| [Editor#zoomIn](?) | Zooms the camera in to the nearest zoom step. See the `constraints.zoomSteps` for more information. |
|
||||
| Method | Description |
|
||||
| ------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| [Editor#setCamera](?) | Moves the camera to the provided coordinates. |
|
||||
| [Editor#zoomIn](?) | Zooms the camera in to the nearest zoom step. See the `constraints.zoomSteps` for more information. |
|
||||
| [Editor#zoomOut](?) | Zooms the camera in to the nearest zoom step. See the `constraints.zoomSteps` for more information. |
|
||||
| [Editor#zoomToFit](?) | Zooms the camera in to the nearest zoom step. See the `constraints.zoomSteps` for more information. |
|
||||
| [Editor#zoomToBounds](?) | Moves the camera to fit the given bounding box. |
|
||||
| [Editor#zoomToSelection](?) | Moves the camera to fit the current selection. |
|
||||
| [Editor#zoomToUser](?) | Moves the camera to center on a user's cursor. |
|
||||
| [Editor#resetZoom](?) | Resets the zoom to 100% or to the `initialZoom` zoom level. |
|
||||
| [Editor#centerOnPoint](?) | Centers the camera on the given point. |
|
||||
| [Editor#pan](?) | Moves the camera's x and y position. |
|
||||
| [Editor#slideCamera](?) | Slides the camera in a given direction with a certain velocity and friction. |
|
||||
| [Editor#stopCameraAnimation](?) | Stops any camera animation. |
|
||||
|
||||
The [Editor#zoomOut](?) method zooms the camera in to the nearest zoom step. See the `constraints.zoomSteps` for more information.
|
||||
# Camera state
|
||||
|
||||
The [Editor#zoomToContent](?) method moves the camera to fit the current page content.
|
||||
|
||||
The [Editor#zoomToBounds](?) method moves the camera to fit the current page content.
|
||||
The camera may be in two states, `idle` or `moving`. You can get the current camera state with [Editor#getCameraState](?).
|
||||
|
|
|
@ -68,5 +68,5 @@ function createDemoShapes(editor: Editor) {
|
|||
},
|
||||
}))
|
||||
)
|
||||
.zoomToContent({ animation: { duration: 0 } })
|
||||
.zoomToFit({ animation: { duration: 0 } })
|
||||
}
|
||||
|
|
|
@ -58,5 +58,5 @@ function createDemoShapes(editor: Editor) {
|
|||
})),
|
||||
])
|
||||
|
||||
editor.zoomToContent({ animation: { duration: 0 } })
|
||||
editor.zoomToFit({ animation: { duration: 0 } })
|
||||
}
|
||||
|
|
|
@ -44,5 +44,5 @@ function createDemoShapes(editor: Editor) {
|
|||
},
|
||||
},
|
||||
])
|
||||
.zoomToContent({ animation: { duration: 0 } })
|
||||
.zoomToFit({ animation: { duration: 0 } })
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ const ZOOM_EVENT = {
|
|||
'reset-zoom': 'resetZoom',
|
||||
'zoom-to-fit': 'zoomToFit',
|
||||
'zoom-to-selection': 'zoomToSelection',
|
||||
'zoom-to-content': 'zoomToContent',
|
||||
}
|
||||
|
||||
export function getCodeSnippet(name: string, data: any) {
|
||||
|
@ -136,15 +135,11 @@ if (updates.length > 0) {
|
|||
} else if (name === 'fit-frame-to-content') {
|
||||
codeSnippet = `fitFrameToContent(editor, editor.getOnlySelectedShape().id)`
|
||||
} else if (name.startsWith('zoom-') || name === 'reset-zoom') {
|
||||
if (name === 'zoom-to-content') {
|
||||
codeSnippet = 'editor.zoomToContent()'
|
||||
} else {
|
||||
codeSnippet = `editor.${ZOOM_EVENT[name as keyof typeof ZOOM_EVENT]}(${
|
||||
name !== 'zoom-to-fit' && name !== 'zoom-to-selection'
|
||||
? 'editor.getViewportScreenCenter(), '
|
||||
: ''
|
||||
}{ duration: 320 })`
|
||||
}
|
||||
codeSnippet = `editor.${ZOOM_EVENT[name as keyof typeof ZOOM_EVENT]}(${
|
||||
name !== 'zoom-to-fit' && name !== 'zoom-to-selection'
|
||||
? 'editor.getViewportScreenCenter(), '
|
||||
: ''
|
||||
}{ duration: 320 })`
|
||||
} else if (name.startsWith('toggle-')) {
|
||||
if (name === 'toggle-lock') {
|
||||
codeSnippet = `editor.toggleLock(editor.getSelectedShapeIds())`
|
||||
|
|
|
@ -619,6 +619,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
batch(fn: () => void): this;
|
||||
bringForward(shapes: TLShape[] | TLShapeId[]): this;
|
||||
bringToFront(shapes: TLShape[] | TLShapeId[]): this;
|
||||
// (undocumented)
|
||||
readonly cameraState: CameraStateManager;
|
||||
cancel(): this;
|
||||
cancelDoubleClick(): void;
|
||||
// @internal (undocumented)
|
||||
|
@ -695,7 +697,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
getBaseZoom(): number;
|
||||
getCamera(): TLCamera;
|
||||
getCameraOptions(): TLCameraOptions;
|
||||
getCameraState(): "idle" | "moving";
|
||||
getCanRedo(): boolean;
|
||||
getCanUndo(): boolean;
|
||||
getCollaborators(): TLInstancePresence[];
|
||||
|
@ -869,7 +870,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
pageToScreen(point: VecLike): Vec;
|
||||
pageToViewport(point: VecLike): Vec;
|
||||
pan(offset: VecLike, opts?: TLCameraMoveOptions): this;
|
||||
panZoomIntoView(ids: TLShapeId[], opts?: TLCameraMoveOptions): this;
|
||||
popFocusedGroupId(): this;
|
||||
putContentOntoCurrentPage(content: TLContent, options?: {
|
||||
point?: VecLike;
|
||||
|
@ -960,10 +960,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
inset?: number;
|
||||
targetZoom?: number;
|
||||
} & TLCameraMoveOptions): this;
|
||||
zoomToContent(opts?: TLCameraMoveOptions): this;
|
||||
zoomToFit(opts?: TLCameraMoveOptions): this;
|
||||
zoomToSelection(opts?: TLCameraMoveOptions): this;
|
||||
zoomToShape(shapeId: TLShapeId, opts?: TLCameraMoveOptions): this;
|
||||
zoomToUser(userId: string, opts?: TLCameraMoveOptions): this;
|
||||
}
|
||||
|
||||
|
|
|
@ -7937,6 +7937,37 @@
|
|||
"isAbstract": false,
|
||||
"name": "bringToFront"
|
||||
},
|
||||
{
|
||||
"kind": "Property",
|
||||
"canonicalReference": "@tldraw/editor!Editor#cameraState:member",
|
||||
"docComment": "",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "readonly cameraState: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "CameraStateManager",
|
||||
"canonicalReference": "@tldraw/editor!~CameraStateManager:class"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isReadonly": true,
|
||||
"isOptional": false,
|
||||
"releaseTag": "Public",
|
||||
"name": "cameraState",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isStatic": false,
|
||||
"isProtected": false,
|
||||
"isAbstract": false
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#cancel:member(1)",
|
||||
|
@ -9937,37 +9968,6 @@
|
|||
"isAbstract": false,
|
||||
"name": "getCameraOptions"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#getCameraState:member(1)",
|
||||
"docComment": "/**\n * Whether the camera is moving or idle.\n *\n * @example\n * ```ts\n * editor.getCameraState()\n * ```\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "getCameraState(): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "\"idle\" | \"moving\""
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isStatic": false,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
"overloadIndex": 1,
|
||||
"parameters": [],
|
||||
"isOptional": false,
|
||||
"isAbstract": false,
|
||||
"name": "getCameraState"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#getCanRedo:member(1)",
|
||||
|
@ -15766,76 +15766,6 @@
|
|||
"isAbstract": false,
|
||||
"name": "pan"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#panZoomIntoView:member(1)",
|
||||
"docComment": "/**\n * Pan or pan/zoom the selected ids into view. This method tries to not change the zoom if possible.\n *\n * @param ids - The ids of the shapes to pan and zoom into view.\n *\n * @param opts - The camera move options.\n *\n * @example\n * ```ts\n * editor.panZoomIntoView([myShape.id])\n * editor.panZoomIntoView([myShape.id], { animation: { duration: 200 } })\n * ```\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "panZoomIntoView(ids: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLShapeId",
|
||||
"canonicalReference": "@tldraw/tlschema!TLShapeId:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", opts?: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLCameraMoveOptions",
|
||||
"canonicalReference": "@tldraw/editor!TLCameraMoveOptions:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "this"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isStatic": false,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 6,
|
||||
"endIndex": 7
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "ids",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 3
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "opts",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 4,
|
||||
"endIndex": 5
|
||||
},
|
||||
"isOptional": true
|
||||
}
|
||||
],
|
||||
"isOptional": false,
|
||||
"isAbstract": false,
|
||||
"name": "panZoomIntoView"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#popFocusedGroupId:member(1)",
|
||||
|
@ -19911,55 +19841,6 @@
|
|||
"isAbstract": false,
|
||||
"name": "zoomToBounds"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#zoomToContent:member(1)",
|
||||
"docComment": "/**\n * Move the camera to the nearest content.\n *\n * @param opts - The camera move options.\n *\n * @example\n * ```ts\n * editor.zoomToContent()\n * editor.zoomToContent({ animation: { duration: 200 } })\n * ```\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "zoomToContent(opts?: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLCameraMoveOptions",
|
||||
"canonicalReference": "@tldraw/editor!TLCameraMoveOptions:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "this"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isStatic": false,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "opts",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": true
|
||||
}
|
||||
],
|
||||
"isOptional": false,
|
||||
"isAbstract": false,
|
||||
"name": "zoomToContent"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#zoomToFit:member(1)",
|
||||
|
@ -20058,72 +19939,6 @@
|
|||
"isAbstract": false,
|
||||
"name": "zoomToSelection"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#zoomToShape:member(1)",
|
||||
"docComment": "/**\n * Animate the camera to a shape.\n *\n * @param shapeId - The id of the shape to animate to.\n *\n * @param opts - The camera move options.\n *\n * @example\n * ```ts\n * editor.zoomToShape(myShape.id)\n * editor.zoomToShape(myShape.id, { animation: { duration: 200 } })\n * ```\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "zoomToShape(shapeId: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLShapeId",
|
||||
"canonicalReference": "@tldraw/tlschema!TLShapeId:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", opts?: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "TLCameraMoveOptions",
|
||||
"canonicalReference": "@tldraw/editor!TLCameraMoveOptions:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "this"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isStatic": false,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "shapeId",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "opts",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": true
|
||||
}
|
||||
],
|
||||
"isOptional": false,
|
||||
"isAbstract": false,
|
||||
"name": "zoomToShape"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/editor!Editor#zoomToUser:member(1)",
|
||||
|
|
|
@ -649,7 +649,7 @@ function InFrontOfTheCanvasWrapper() {
|
|||
|
||||
function MovingCameraHitTestBlocker() {
|
||||
const editor = useEditor()
|
||||
const cameraState = useValue('camera state', () => editor.getCameraState(), [editor])
|
||||
const cameraState = useValue('camera state', () => editor.cameraState.getCameraState(), [editor])
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -66,7 +66,6 @@ import { TLUser, createTLUser } from '../config/createTLUser'
|
|||
import { checkShapesAndAddCore } from '../config/defaultShapes'
|
||||
import {
|
||||
ANIMATION_MEDIUM_MS,
|
||||
CAMERA_MOVING_TIMEOUT,
|
||||
CAMERA_SLIDE_FRICTION,
|
||||
COARSE_DRAG_DISTANCE,
|
||||
COLLABORATOR_IDLE_TIMEOUT,
|
||||
|
@ -108,6 +107,7 @@ import { notVisibleShapes } from './derivations/notVisibleShapes'
|
|||
import { parentsToChildren } from './derivations/parentsToChildren'
|
||||
import { deriveShapeIdsInCurrentPage } from './derivations/shapeIdsInCurrentPage'
|
||||
import { getSvgJsx } from './getSvgJsx'
|
||||
import { CameraStateManager } from './managers/CameraStateManager'
|
||||
import { ClickManager } from './managers/ClickManager'
|
||||
import { EnvironmentManager } from './managers/EnvironmentManager'
|
||||
import { HistoryManager } from './managers/HistoryManager'
|
||||
|
@ -218,6 +218,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
this.textMeasure = new TextManager(this)
|
||||
this._tickManager = new TickManager(this)
|
||||
this.cameraState = new CameraStateManager(this)
|
||||
|
||||
class NewRoot extends RootState {
|
||||
static override initial = initialState ?? ''
|
||||
|
@ -675,6 +676,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*/
|
||||
readonly user: UserPreferencesManager
|
||||
|
||||
readonly cameraState: CameraStateManager
|
||||
|
||||
/**
|
||||
* A helper for measuring text.
|
||||
*
|
||||
|
@ -696,6 +699,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*/
|
||||
readonly scribbles: ScribbleManager
|
||||
|
||||
/**
|
||||
* A manager for side effects and correct state enforcement. See {@link SideEffectManager} for details.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
readonly sideEffects: SideEffectManager<this>
|
||||
|
||||
/**
|
||||
* The current HTML element containing the editor.
|
||||
*
|
||||
|
@ -708,13 +718,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*/
|
||||
getContainer: () => HTMLElement
|
||||
|
||||
/**
|
||||
* A manager for side effects and correct state enforcement. See {@link SideEffectManager} for details.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
readonly sideEffects: SideEffectManager<this>
|
||||
|
||||
/**
|
||||
* Dispose the editor.
|
||||
*
|
||||
|
@ -2373,7 +2376,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
}
|
||||
}
|
||||
|
||||
this._tickCameraState()
|
||||
this.cameraState.tick()
|
||||
})
|
||||
|
||||
return this
|
||||
|
@ -2446,26 +2449,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the camera to the nearest content.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* editor.zoomToContent()
|
||||
* editor.zoomToContent({ animation: { duration: 200 } })
|
||||
* ```
|
||||
*
|
||||
* @param opts - The camera move options.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
zoomToContent(opts: TLCameraMoveOptions = { animation: { duration: 220 } }): this {
|
||||
const bounds = this.getSelectionPageBounds() ?? this.getCurrentPageBounds()
|
||||
if (!bounds) return this
|
||||
this.zoomToBounds(bounds, { targetZoom: Math.min(1, this.getZoomLevel()), ...opts })
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Zoom the camera to fit the current page's content in the viewport.
|
||||
*
|
||||
|
@ -2640,66 +2623,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Pan or pan/zoom the selected ids into view. This method tries to not change the zoom if possible.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* editor.panZoomIntoView([myShape.id])
|
||||
* editor.panZoomIntoView([myShape.id], { animation: { duration: 200 } })
|
||||
* ```
|
||||
*
|
||||
* @param ids - The ids of the shapes to pan and zoom into view.
|
||||
* @param opts - The camera move options.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
panZoomIntoView(ids: TLShapeId[], opts?: TLCameraMoveOptions): this {
|
||||
if (this.getCameraOptions().isLocked) return this
|
||||
|
||||
if (ids.length <= 0) return this
|
||||
const selectionBounds = Box.Common(compact(ids.map((id) => this.getShapePageBounds(id))))
|
||||
|
||||
const viewportPageBounds = this.getViewportPageBounds()
|
||||
|
||||
if (viewportPageBounds.h < selectionBounds.h || viewportPageBounds.w < selectionBounds.w) {
|
||||
this.zoomToBounds(selectionBounds, { targetZoom: this.getCamera().z, ...opts })
|
||||
|
||||
return this
|
||||
} else {
|
||||
const insetViewport = this.getViewportPageBounds()
|
||||
.clone()
|
||||
.expandBy(-32 / this.getZoomLevel())
|
||||
|
||||
let offsetX = 0
|
||||
let offsetY = 0
|
||||
if (insetViewport.maxY < selectionBounds.maxY) {
|
||||
// off bottom
|
||||
offsetY = insetViewport.maxY - selectionBounds.maxY
|
||||
} else if (insetViewport.minY > selectionBounds.minY) {
|
||||
// off top
|
||||
offsetY = insetViewport.minY - selectionBounds.minY
|
||||
} else {
|
||||
// inside y-bounds
|
||||
}
|
||||
|
||||
if (insetViewport.maxX < selectionBounds.maxX) {
|
||||
// off right
|
||||
offsetX = insetViewport.maxX - selectionBounds.maxX
|
||||
} else if (insetViewport.minX > selectionBounds.minX) {
|
||||
// off left
|
||||
offsetX = insetViewport.minX - selectionBounds.minX
|
||||
} else {
|
||||
// inside x-bounds
|
||||
}
|
||||
|
||||
const { x: cx, y: cy, z: cz } = this.getCamera()
|
||||
this.setCamera(new Vec(cx + offsetX, cy + offsetY, cz), opts)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Zoom the camera to fit a bounding box (in the current page space).
|
||||
*
|
||||
|
@ -2985,53 +2908,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate the camera to a shape.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* editor.zoomToShape(myShape.id)
|
||||
* editor.zoomToShape(myShape.id, { animation: { duration: 200 } })
|
||||
* ```
|
||||
*
|
||||
* @param shapeId - The id of the shape to animate to.
|
||||
* @param opts - The camera move options.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
zoomToShape(shapeId: TLShapeId, opts?: TLCameraMoveOptions): this {
|
||||
if (this.getCameraOptions().isLocked) return this
|
||||
|
||||
const activeArea = this.getViewportScreenBounds().clone().expandBy(-32)
|
||||
const viewportAspectRatio = activeArea.width / activeArea.height
|
||||
|
||||
const shapePageBounds = this.getShapePageBounds(shapeId)
|
||||
|
||||
if (!shapePageBounds) return this
|
||||
|
||||
const shapeAspectRatio = shapePageBounds.width / shapePageBounds.height
|
||||
|
||||
const targetViewportPage = shapePageBounds.clone()
|
||||
|
||||
const z = shapePageBounds.width / activeArea.width
|
||||
targetViewportPage.width += (activeArea.minX + activeArea.maxX) * z
|
||||
targetViewportPage.height += (activeArea.minY + activeArea.maxY) * z
|
||||
targetViewportPage.x -= activeArea.minX * z
|
||||
targetViewportPage.y -= activeArea.minY * z
|
||||
|
||||
if (shapeAspectRatio > viewportAspectRatio) {
|
||||
targetViewportPage.height = shapePageBounds.width / viewportAspectRatio
|
||||
targetViewportPage.y -= (targetViewportPage.height - shapePageBounds.height) / 2
|
||||
} else {
|
||||
targetViewportPage.width = shapePageBounds.height * viewportAspectRatio
|
||||
targetViewportPage.x -= (targetViewportPage.width - shapePageBounds.width) / 2
|
||||
}
|
||||
|
||||
this._animateToViewport(targetViewportPage, opts)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
// Viewport
|
||||
|
||||
/** @internal */
|
||||
|
@ -3101,7 +2977,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
}
|
||||
}
|
||||
|
||||
this._tickCameraState()
|
||||
this.cameraState.tick()
|
||||
this.updateRenderingBounds()
|
||||
|
||||
return this
|
||||
|
@ -3403,58 +3279,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
return this
|
||||
}
|
||||
|
||||
// Camera state
|
||||
|
||||
private _cameraState = atom('camera state', 'idle' as 'idle' | 'moving')
|
||||
|
||||
/**
|
||||
* Whether the camera is moving or idle.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* editor.getCameraState()
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getCameraState() {
|
||||
return this._cameraState.get()
|
||||
}
|
||||
|
||||
// Camera state does two things: first, it allows us to subscribe to whether
|
||||
// the camera is moving or not; and second, it allows us to update the rendering
|
||||
// shapes on the canvas. Changing the rendering shapes may cause shapes to
|
||||
// unmount / remount in the DOM, which is expensive; and computing visibility is
|
||||
// also expensive in large projects. For this reason, we use a second bounding
|
||||
// box just for rendering, and we only update after the camera stops moving.
|
||||
|
||||
private _cameraStateTimeoutRemaining = 0
|
||||
private _lastUpdateRenderingBoundsTimestamp = Date.now()
|
||||
|
||||
private _decayCameraStateTimeout = (elapsed: number) => {
|
||||
this._cameraStateTimeoutRemaining -= elapsed
|
||||
|
||||
if (this._cameraStateTimeoutRemaining <= 0) {
|
||||
this.off('tick', this._decayCameraStateTimeout)
|
||||
this._cameraState.set('idle')
|
||||
this.updateRenderingBounds()
|
||||
}
|
||||
}
|
||||
|
||||
private _tickCameraState = () => {
|
||||
// always reset the timeout
|
||||
this._cameraStateTimeoutRemaining = CAMERA_MOVING_TIMEOUT
|
||||
|
||||
const now = Date.now()
|
||||
|
||||
// If the state is idle, then start the tick
|
||||
if (this._cameraState.__unsafe__getWithoutCapture() === 'idle') {
|
||||
this._lastUpdateRenderingBoundsTimestamp = now // don't render right away
|
||||
this._cameraState.set('moving')
|
||||
this.on('tick', this._decayCameraStateTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
getUnorderedRenderingShapes(
|
||||
// The rendering state. We use this method both for rendering, which
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import { atom } from '@tldraw/state'
|
||||
import { CAMERA_MOVING_TIMEOUT } from '../../constants'
|
||||
import { Editor } from '../Editor'
|
||||
|
||||
export class CameraStateManager {
|
||||
constructor(public editor: Editor) {}
|
||||
|
||||
// Camera state
|
||||
// Camera state does two things: first, it allows us to subscribe to whether
|
||||
// the camera is moving or not; and second, it allows us to update the rendering
|
||||
// shapes on the canvas. Changing the rendering shapes may cause shapes to
|
||||
// unmount / remount in the DOM, which is expensive; and computing visibility is
|
||||
// also expensive in large projects. For this reason, we use a second bounding
|
||||
// box just for rendering, and we only update after the camera stops moving.
|
||||
private _cameraState = atom('camera state', 'idle' as 'idle' | 'moving')
|
||||
|
||||
/**
|
||||
* Whether the camera is moving or idle.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* editor.getCameraState()
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getCameraState() {
|
||||
return this._cameraState.get()
|
||||
}
|
||||
|
||||
private _cameraStateTimeoutRemaining = 0
|
||||
|
||||
private _decayCameraStateTimeout = (elapsed: number) => {
|
||||
this._cameraStateTimeoutRemaining -= elapsed
|
||||
|
||||
if (this._cameraStateTimeoutRemaining <= 0) {
|
||||
this.editor.off('tick', this._decayCameraStateTimeout)
|
||||
this._cameraState.set('idle')
|
||||
this.editor.updateRenderingBounds()
|
||||
}
|
||||
}
|
||||
|
||||
public tick = () => {
|
||||
// always reset the timeout
|
||||
this._cameraStateTimeoutRemaining = CAMERA_MOVING_TIMEOUT
|
||||
|
||||
// If the state is idle, then start the tick
|
||||
if (this._cameraState.__unsafe__getWithoutCapture() === 'idle') {
|
||||
this._cameraState.set('moving')
|
||||
this.editor.on('tick', this._decayCameraStateTimeout)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1297,7 +1297,12 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
|||
readonlyOk: true,
|
||||
onSelect(source) {
|
||||
trackEvent('zoom-to-content', { source })
|
||||
editor.zoomToContent()
|
||||
const bounds = editor.getSelectionPageBounds() ?? editor.getCurrentPageBounds()
|
||||
if (!bounds) return
|
||||
editor.zoomToBounds(bounds, {
|
||||
targetZoom: Math.min(1, editor.getZoomLevel()),
|
||||
animation: { duration: 220 },
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
Ładowanie…
Reference in New Issue