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:
parent
fe3dc0d965
commit
c5b2569bfc
2 changed files with 157 additions and 0 deletions
|
@ -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.
|
||||
|
||||
*/
|
13
apps/examples/src/examples/toSvg-method-example/README.md
Normal file
13
apps/examples/src/examples/toSvg-method-example/README.md
Normal 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.
|
Loading…
Reference in a new issue