Shape animation
This example demonstrates how to animate shapes using the editor's animation API. It shows:
- Basic shape animation - Animating a single shape's position using
animateShape() - Rotation animation - Spinning a shape 360 degrees
- Opacity animation - Fading shapes in and out
- Combined animations - Animating multiple properties (position, rotation, opacity) simultaneously
- Multiple shape animation - Animating all shapes at once with
animateShapes() - Different easing functions - Using
EASINGS.easeInOutCubic,EASINGS.easeInOutQuad, andEASINGS.easeOutCubic
The animation system automatically interpolates between the current and target values of animated properties over the specified duration.
import {
createShapeId,
EASINGS,
TLComponents,
Tldraw,
TldrawUiButton,
useEditor,
useValue,
} from 'tldraw'
import 'tldraw/tldraw.css'
import './shape-animation.css'
// [1]
function AnimationControls() {
const editor = useEditor()
// [2]
const animatePosition = () => {
const shape = editor.getOnlySelectedShape()
if (!shape) return
editor.animateShape(
{ ...shape, x: shape.x + 200, y: shape.y + 100 },
{ animation: { duration: 800, easing: EASINGS.easeInOutCubic } }
)
}
// [3]
const animateRotation = () => {
const shape = editor.getOnlySelectedShape()
if (!shape) return
editor.animateShape(
{ ...shape, rotation: shape.rotation + Math.PI * 2 },
{ animation: { duration: 1000, easing: EASINGS.easeInOutCubic } }
)
}
// [4]
const animateFade = () => {
const shape = editor.getOnlySelectedShape()
if (!shape) return
editor.animateShape(
{ ...shape, opacity: shape.opacity > 0.5 ? 0.2 : 1 },
{ animation: { duration: 600, easing: EASINGS.easeInOutQuad } }
)
}
// [5]
const animateAll = () => {
const shape = editor.getOnlySelectedShape()
if (!shape) return
editor.animateShape(
{
...shape,
x: shape.x + 150,
y: shape.y - 100,
rotation: shape.rotation + Math.PI,
opacity: 0.3,
},
{ animation: { duration: 1200, easing: EASINGS.easeInOutCubic } }
)
}
// [6]
const animateMultiple = () => {
const shapeIds = editor.getCurrentPageShapeIds()
const updates = Array.from(shapeIds).map((id) => {
const shape = editor.getShape(id)
if (!shape) return null
return {
...shape,
x: shape.x + (Math.random() - 0.5) * 200,
y: shape.y + (Math.random() - 0.5) * 200,
rotation: shape.rotation + (Math.random() - 0.5) * Math.PI,
}
})
editor.animateShapes(updates, { animation: { duration: 1000, easing: EASINGS.easeOutCubic } })
}
const hasOneSelected = useValue(
'has one selected',
() => editor.getSelectedShapeIds().length !== 1,
[editor]
)
return (
<div className="tlui-menu animation-controls">
<TldrawUiButton type="normal" disabled={hasOneSelected} onClick={animatePosition}>
Animate position
</TldrawUiButton>
<TldrawUiButton type="normal" disabled={hasOneSelected} onClick={animateRotation}>
Animate rotation
</TldrawUiButton>
<TldrawUiButton type="normal" disabled={hasOneSelected} onClick={animateFade}>
Fade in/out
</TldrawUiButton>
<TldrawUiButton type="normal" disabled={hasOneSelected} onClick={animateAll}>
Animate all
</TldrawUiButton>
<TldrawUiButton type="normal" onClick={animateMultiple}>
Animate multiple shapes
</TldrawUiButton>
</div>
)
}
const components: TLComponents = {
TopPanel: AnimationControls,
}
export default function AnimationShapesExample() {
return (
<div className="tldraw__editor">
<Tldraw
components={components}
onMount={(editor) => {
// Create some initial shapes for the demo
const id = createShapeId()
editor.createShapes([
{
id,
type: 'geo',
x: 200,
y: 200,
props: {
w: 100,
h: 100,
color: 'blue',
},
},
{
id: createShapeId(),
type: 'geo',
x: 400,
y: 300,
props: {
w: 80,
h: 80,
color: 'red',
geo: 'ellipse',
},
},
{
id: createShapeId(),
type: 'geo',
x: 600,
y: 200,
props: {
w: 120,
h: 90,
color: 'green',
geo: 'triangle',
},
},
])
// Select the first shape
editor.select(id)
}}
/>
</div>
)
}
/*
[1]
Create a component with buttons to trigger different animations. Use the `useEditor` hook to
access the editor instance.
[2]
`animateShape()` animates a single shape to new property values. Pass a partial shape with the
target values and animation options. The `easing` property accepts functions from the `EASINGS`
object. In this example, we move the shape to a new position with cubic easing.
[3]
Animate rotation by specifying a target rotation value in radians. Here we rotate the shape
360 degrees (2π radians) with smooth easing.
[4]
Opacity can be animated between 0 and 1. This creates a fade effect. We toggle between
low and high opacity values using quadratic easing.
[5]
Multiple properties can be animated simultaneously. This example combines position, rotation,
and opacity changes in a single animation.
[6]
`animateShapes()` animates multiple shapes at once. Build an array of shape partials and
pass them all together. All shapes will animate with the same duration and easing function.
*/
Is this page helpful?
Prev
Search text on the canvasNext
Align and distribute shapes