toSvg method example (#4124)

An example of how to use the toSvg method on custom shape.

Resolves #4057 

### Change type

- [ ] `bugfix`
- [ ] `improvement`
- [ ] `feature`
- [ ] `api`
- [x] `other`

### Test plan

1. Create a shape...
2.

- [ ] Unit tests
- [ ] End to end tests

### Release notes

- [Examples App] added an example for the toSvg method on a custom
shape.
This commit is contained in:
Taha 2024-07-15 16:44:07 +01:00 committed by GitHub
parent fe3dc0d965
commit c5b2569bfc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 157 additions and 0 deletions

View file

@ -0,0 +1,144 @@
import { ReactElement } from 'react'
import {
Geometry2d,
HTMLContainer,
RecordProps,
Rectangle2d,
ShapeUtil,
SvgExportContext,
T,
TLBaseShape,
Tldraw,
} from 'tldraw'
import 'tldraw/tldraw.css'
// There's a guide at the bottom of this file!
type ICustomShape = TLBaseShape<
'my-custom-shape',
{
w: number
h: number
}
>
const LIGHT_FILL = '#ff8888'
const DARK_FILL = '#ffcccc'
export class MyShapeUtil extends ShapeUtil<ICustomShape> {
static override type = 'my-custom-shape' as const
static override props: RecordProps<ICustomShape> = {
w: T.number,
h: T.number,
}
getDefaultProps(): ICustomShape['props'] {
return {
w: 200,
h: 200,
}
}
override canEdit = () => false
override canResize = () => false
override isAspectRatioLocked = () => false
getGeometry(shape: ICustomShape): Geometry2d {
return new Rectangle2d({
width: shape.props.w,
height: shape.props.h,
isFilled: true,
})
}
component(_shape: ICustomShape) {
const isDarkmode = this.editor.user.getIsDarkMode()
return <HTMLContainer style={{ backgroundColor: isDarkmode ? DARK_FILL : LIGHT_FILL }} />
}
indicator(shape: ICustomShape) {
return this.getSvgRect(shape)
}
// [1]
override toSvg(
shape: ICustomShape,
ctx: SvgExportContext
): ReactElement | null | Promise<ReactElement | null> {
// ctx.addExportDef(getFontDef(shape))
const isDarkmode = ctx.isDarkMode
const fill = isDarkmode ? DARK_FILL : LIGHT_FILL
return this.getSvgRect(shape, { fill })
}
getSvgRect(shape: ICustomShape, props?: { fill: string }) {
return <rect width={shape.props.w} height={shape.props.h} {...props} />
}
// [2]
// override toBackgroundSvg(
// shape: ICustomShape,
// ctx: SvgExportContext
// ): ReactElement | null | Promise<ReactElement | null> {
// const isDarkmode = ctx.isDarkMode
// const fill = isDarkmode ? '#333' : '#efefef'
// return <rect width={shape.props.w} height={shape.props.h} fill={fill} />
// }
}
// [3]
// function getFontDef(shape: ICustomShape): SvgExportDef {
// //
// return {
// some unique key,
// key: 'my-custom-shape-font',
// getElement: async () => {
// return <style></style> element
// check out the defaultStyleDefs.tsx file for an example of how
// we do this for tldraw fonts
// },
// }
// }
const customShape = [MyShapeUtil]
export default function CustomShapeToSvgExample() {
return (
<div className="tldraw__editor">
<Tldraw
shapeUtils={customShape}
onMount={(editor) => {
editor.createShape({ type: 'my-custom-shape', x: 100, y: 100 })
}}
/>
</div>
)
}
/*
The "export as SVG/PNG" and "copy as SVG/PNG" actions use the `toSvg` or `toBackgroundSvg`
methods of a shape util. If a shape does not have a `toSvg` or `toBackgroundSvg` method
defined, it will default to an empty box.
For more information on creating a custom shape, check out the custom shape example.
[1]
This method should return a React element that represents the shape as an SVG element.
If your shape is HTML, then you will need to convert it to an SVG representation. In this
example we've used a `rect` element to represent the shape. Other shapes may require more
complex work to render them as SVGs, especially if they contain text. Check out [3] for more
info.
[2]
The `toBackgroundSvg` method is used to render a layer behind the shape when exporting as SVG.
We use this in the tldraw codebase to make the highlighter shape. It's commented out here as
we don't need it for this example.
[3]
If your shape contains text, you may need to add a font definition to the SVG. This is done
using the `addExportDef` method of the `SvgExportContext`. Your font def must contain a unique
key and a function that returns a React element. Check out the `getFontDefForExport` function
in the `defaultStyleDefs.tsx` file for an example of how this is done for tldraw fonts.
*/

View file

@ -0,0 +1,13 @@
---
title: Shape toSvg method
component: ./CustomShapeToSvgExample.tsx
category: shapes/tools
priority: 3
keywords: [basic, svg, custom, export, copy]
---
How to use the toSvg method of a shape util to determine how your custom shapes look when copied/exported as an image.
---
The "export as SVG/PNG" and "copy as SVG/PNG" actions use the `toSvg` or `toBackgroundSvg` methods of a shape util. If a shape does not have a `toSvg` or `toBackgroundSvg` method defined, it will default to an empty box.