annotate onthecanvas example (#2459)
I've changed this example a little bit to add some hints to the user, similar to the only-editor example. I also removed snaplines: null from the components object because it seemed a bit unnecessary. ### Change Type - [ ] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [x] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. Add a step-by-step description of how to test your PR here. 2. - [ ] Unit Tests - [ ] End to end tests ### Release Notes - annotate onthecanvas example
This commit is contained in:
parent
6fdd0987b0
commit
1cf6b4ff51
1 changed files with 58 additions and 13 deletions
|
@ -9,7 +9,9 @@ import {
|
||||||
import '@tldraw/tldraw/tldraw.css'
|
import '@tldraw/tldraw/tldraw.css'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
// The "OnTheCanvas" component is rendered on top of the canvas, but behind the UI.
|
// There's a guide at the bottom of this file!
|
||||||
|
|
||||||
|
// [1]
|
||||||
function MyComponent() {
|
function MyComponent() {
|
||||||
const [state, setState] = useState(0)
|
const [state, setState] = useState(0)
|
||||||
|
|
||||||
|
@ -20,46 +22,49 @@ function MyComponent() {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 50,
|
top: 50,
|
||||||
left: 50,
|
left: 50,
|
||||||
width: 'fit-content',
|
width: 200,
|
||||||
padding: 12,
|
padding: 12,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
backgroundColor: 'goldenrod',
|
backgroundColor: 'goldenrod',
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
pointerEvents: 'all',
|
|
||||||
userSelect: 'unset',
|
userSelect: 'unset',
|
||||||
|
boxShadow: '0 0 0 1px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.1)',
|
||||||
}}
|
}}
|
||||||
onPointerDown={stopEventPropagation}
|
onPointerDown={stopEventPropagation}
|
||||||
onPointerMove={stopEventPropagation}
|
onPointerMove={stopEventPropagation}
|
||||||
>
|
>
|
||||||
The count is {state}! <button onClick={() => setState((s) => s - 1)}>-1</button>
|
<p>The count is {state}! </p>
|
||||||
|
<button onClick={() => setState((s) => s - 1)}>-1</button>
|
||||||
|
<p>These components are on the canvas. They will scale with camera zoom like shapes.</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 150,
|
top: 210,
|
||||||
left: 150,
|
left: 150,
|
||||||
width: 128,
|
width: 200,
|
||||||
padding: 12,
|
padding: 12,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
backgroundColor: 'pink',
|
backgroundColor: 'pink',
|
||||||
zIndex: 99999999,
|
zIndex: 99999999,
|
||||||
pointerEvents: 'all',
|
|
||||||
userSelect: 'unset',
|
userSelect: 'unset',
|
||||||
|
boxShadow: '0 0 0 1px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.1)',
|
||||||
}}
|
}}
|
||||||
onPointerDown={stopEventPropagation}
|
onPointerDown={stopEventPropagation}
|
||||||
onPointerMove={stopEventPropagation}
|
onPointerMove={stopEventPropagation}
|
||||||
>
|
>
|
||||||
The count is {state}! <button onClick={() => setState((s) => s + 1)}>+1</button>
|
<p>The count is {state}! </p>
|
||||||
|
<button onClick={() => setState((s) => s + 1)}>+1</button>
|
||||||
|
<p>Create and select a shape to see the in front of the canvas component</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The "InFrontOfTheCanvas" component is rendered on top of the canvas, but behind the UI.
|
//[2]
|
||||||
const MyComponentInFront = track(() => {
|
const MyComponentInFront = track(() => {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
const selectionRotatedPageBounds = editor.getSelectionRotatedPageBounds()
|
const selectionRotatedPageBounds = editor.getSelectionRotatedPageBounds()
|
||||||
|
|
||||||
if (!selectionRotatedPageBounds) return null
|
if (!selectionRotatedPageBounds) return null
|
||||||
|
|
||||||
const pageCoordinates = Vec.Sub(
|
const pageCoordinates = Vec.Sub(
|
||||||
|
@ -73,21 +78,25 @@ const MyComponentInFront = track(() => {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: Math.max(64, pageCoordinates.y - 64),
|
top: Math.max(64, pageCoordinates.y - 64),
|
||||||
left: Math.max(64, pageCoordinates.x),
|
left: Math.max(64, pageCoordinates.x),
|
||||||
padding: 12,
|
borderRadius: 8,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
background: '#efefef',
|
background: '#efefef',
|
||||||
|
boxShadow: '0 0 0 1px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.1)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
This does not scale with the zoom
|
<p>This won't scale with zoom.</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// [3]
|
||||||
const components: TLEditorComponents = {
|
const components: TLEditorComponents = {
|
||||||
OnTheCanvas: MyComponent,
|
OnTheCanvas: MyComponent,
|
||||||
InFrontOfTheCanvas: MyComponentInFront,
|
InFrontOfTheCanvas: MyComponentInFront,
|
||||||
SnapLine: null,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [4]
|
||||||
export default function OnTheCanvasExample() {
|
export default function OnTheCanvasExample() {
|
||||||
return (
|
return (
|
||||||
<div className="tldraw__editor">
|
<div className="tldraw__editor">
|
||||||
|
@ -95,3 +104,39 @@ export default function OnTheCanvasExample() {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This example shows how you can use the onTheCanvas and inFrontOfTheCanvas components.
|
||||||
|
onTheCanvas components will behave similarly to shapes, they will scale with the zoom
|
||||||
|
and move when the page is panned. inFrontOfTheCanvas components don't scale with the
|
||||||
|
zoom, but still move when the page is panned.
|
||||||
|
|
||||||
|
For another example that shows how to customize components, check out the custom
|
||||||
|
components example.
|
||||||
|
|
||||||
|
To have a component that ignores the camera entirely, you should check out the custom
|
||||||
|
UI example.
|
||||||
|
|
||||||
|
|
||||||
|
[1]
|
||||||
|
This is our onTheCanvas component. We also stop event propagation on the pointer events
|
||||||
|
so that we don't accidentally select shapes when interacting with the component.
|
||||||
|
|
||||||
|
[2]
|
||||||
|
This is our inFrontOfTheCanvas component. We want to render this next to a selected shape,
|
||||||
|
so we need to make sure it's reactive to changes in the editor. We use the track function
|
||||||
|
to make sure the component is re-rendered whenever the selection changes. Check out the
|
||||||
|
Signia docs for more info: https://signia.tldraw.dev/docs/API/signia_react/functions/track
|
||||||
|
|
||||||
|
Using the editor instance we can get the bounds of the selection box and convert them to
|
||||||
|
screen coordinates. We then render the component at those coordinates.
|
||||||
|
|
||||||
|
|
||||||
|
[3]
|
||||||
|
This is where we define the object that will be passed to the Tldraw component prop.
|
||||||
|
|
||||||
|
[4]
|
||||||
|
This is where we render the Tldraw component. Let's pass the components object to the
|
||||||
|
components prop.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
Loading…
Reference in a new issue