snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
This commit is contained in:
150
apps/docs/content/sdk-features/coordinates.mdx
Normal file
150
apps/docs/content/sdk-features/coordinates.mdx
Normal file
@@ -0,0 +1,150 @@
|
||||
---
|
||||
title: Coordinates
|
||||
created_at: 12/20/2025
|
||||
updated_at: 12/29/2025
|
||||
keywords:
|
||||
- coordinates
|
||||
- screen
|
||||
- page
|
||||
- viewport
|
||||
- transform
|
||||
reviewed_by: steveruizok
|
||||
status: published
|
||||
date: 12/20/2025
|
||||
readability: 9
|
||||
voice: 8
|
||||
completeness: 9
|
||||
accuracy: 9
|
||||
notes: Could use more questions-as-transitions per voice guide. Consider adding visual diagram and API reference links.
|
||||
---
|
||||
|
||||
The editor uses three coordinate systems: screen space, viewport space, and page space. When you move the mouse over the canvas, the browser gives you screen coordinates. To create a shape at that location, you need to convert those coordinates to page space.
|
||||
|
||||
The editor provides methods to convert between these coordinate systems. You'll use these when building custom tools, positioning DOM overlays, or responding to pointer events. The [Camera](/sdk-features/camera) article explains the camera values these conversions depend on.
|
||||
|
||||
| Method | Description |
|
||||
| -------------------------- | ------------------------------------------------------------------- |
|
||||
| [Editor#screenToPage](?) | Convert a screen point to page space. |
|
||||
| [Editor#pageToScreen](?) | Convert a page point to screen space. |
|
||||
| [Editor#pageToViewport](?) | Convert a page point to viewport space (relative to the container). |
|
||||
|
||||
## The three coordinate systems
|
||||
|
||||
### Screen space
|
||||
|
||||
Screen space uses pixel coordinates from the browser window's top-left corner. These are the values you get from `MouseEvent.clientX` and `MouseEvent.clientY`. Screen coordinates include any space outside the editor container, like browser chrome or other page content.
|
||||
|
||||
### Viewport space
|
||||
|
||||
Viewport space uses pixel coordinates from the editor container's top-left corner. This accounts for where the editor sits on the page. If the editor is embedded in a scrollable element or positioned away from the browser's origin, viewport coordinates differ from screen coordinates by that offset.
|
||||
|
||||
The editor tracks the container's position in [Editor#getViewportScreenBounds](?).
|
||||
|
||||
### Page space
|
||||
|
||||
Page space is the infinite canvas itself. A shape at `x: 100, y: 200` stays at those coordinates regardless of how the user pans or zooms. Top-level shapes store their position in page space; children of frames and groups store it relative to their parent. See [Parenting](/sdk-features/parenting) for the helpers that convert between shape space and page space.
|
||||
|
||||
The camera determines which part of page space is visible. When you zoom in, the same page-space region takes up more screen pixels. When you pan, different page-space coordinates come into view.
|
||||
|
||||
## Coordinate transformations
|
||||
|
||||
### Screen to page space
|
||||
|
||||
Use [Editor#screenToPage](?) to convert screen coordinates to page coordinates. This is the most common transformation. It accounts for the editor container's position, the camera position, and the zoom level:
|
||||
|
||||
```typescript
|
||||
// Convert mouse event coordinates to page space
|
||||
const pagePoint = editor.screenToPage({ x: event.clientX, y: event.clientY })
|
||||
|
||||
// Create a shape at the clicked location
|
||||
editor.createShape({
|
||||
type: 'geo',
|
||||
x: pagePoint.x,
|
||||
y: pagePoint.y,
|
||||
props: { w: 100, h: 100, geo: 'rectangle' },
|
||||
})
|
||||
```
|
||||
|
||||
### Page to screen space
|
||||
|
||||
Use [Editor#pageToScreen](?) to convert page coordinates to screen coordinates, for example to position a DOM element that lives outside the editor container:
|
||||
|
||||
```typescript
|
||||
// Convert shape position to screen coordinates
|
||||
const shape = editor.getShape(shapeId)
|
||||
if (!shape) return
|
||||
const screenPoint = editor.pageToScreen({ x: shape.x, y: shape.y })
|
||||
|
||||
// Position a DOM element at the shape's screen location
|
||||
element.style.left = `${screenPoint.x}px`
|
||||
element.style.top = `${screenPoint.y}px`
|
||||
```
|
||||
|
||||
### Page to viewport space
|
||||
|
||||
Use [Editor#pageToViewport](?) to convert page coordinates to viewport coordinates. This is `pageToScreen()` without the container offset, so use it for canvas rendering or for positioning elements inside the editor container:
|
||||
|
||||
```typescript
|
||||
// Get viewport coordinates for a page point
|
||||
const viewportPoint = editor.pageToViewport({ x: 500, y: 300 })
|
||||
|
||||
// Check if a point is visible in the viewport
|
||||
const viewportBounds = editor.getViewportScreenBounds()
|
||||
const isVisible =
|
||||
viewportPoint.x >= 0 &&
|
||||
viewportPoint.x <= viewportBounds.w &&
|
||||
viewportPoint.y >= 0 &&
|
||||
viewportPoint.y <= viewportBounds.h
|
||||
```
|
||||
|
||||
## Viewport bounds
|
||||
|
||||
Use [Editor#getViewportScreenBounds](?) to get the editor container's position and size in screen space:
|
||||
|
||||
```typescript
|
||||
const screenBounds = editor.getViewportScreenBounds()
|
||||
// screenBounds.x, screenBounds.y - container position in screen space
|
||||
// screenBounds.w, screenBounds.h - container dimensions in pixels
|
||||
```
|
||||
|
||||
Use [Editor#getViewportPageBounds](?) to get the visible area in page space:
|
||||
|
||||
```typescript
|
||||
const pageBounds = editor.getViewportPageBounds()
|
||||
// pageBounds.x, pageBounds.y - top-left corner of visible area in page space
|
||||
// pageBounds.w, pageBounds.h - visible area dimensions in page units
|
||||
```
|
||||
|
||||
Both are reactive, so you can read them inside `track` components or `useValue` and re-render when the user pans or zooms. At higher zoom levels, the visible page-space area is smaller.
|
||||
|
||||
## Using the inputs manager
|
||||
|
||||
The editor's inputs manager tracks the current pointer position, so you can read it from anywhere in your code:
|
||||
|
||||
```typescript
|
||||
// Get the current pointer position
|
||||
const screenPoint = editor.inputs.getCurrentScreenPoint()
|
||||
const pagePoint = editor.inputs.getCurrentPagePoint()
|
||||
|
||||
// Get the position where the current drag started
|
||||
const originScreenPoint = editor.inputs.getOriginScreenPoint()
|
||||
const originPagePoint = editor.inputs.getOriginPagePoint()
|
||||
```
|
||||
|
||||
Despite the name, the inputs manager's "screen point" is relative to the editor container, which this article calls viewport space. Don't pass it to `screenToPage()` and don't subtract `getViewportScreenBounds()` from it.
|
||||
|
||||
Pointer event info is different: its `point` property holds `clientX`/`clientY`, so it is in screen space and converts with `screenToPage()`:
|
||||
|
||||
```typescript
|
||||
editor.on('event', (event) => {
|
||||
if (event.type === 'pointer' && event.name === 'pointer_down') {
|
||||
const pagePoint = editor.screenToPage(event.point)
|
||||
// Use pagePoint for shape manipulation
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Related examples
|
||||
|
||||
- [Reactive inputs](/examples/editor-api/reactive-inputs) - Display page and screen coordinates reactively as the pointer moves.
|
||||
- [Selection UI](/examples/ui/selection-ui) - Use selection bounds in screen space to position custom UI around shapes.
|
||||
Reference in New Issue
Block a user