Files
2026-07-13 12:28:53 +08:00

114 lines
2.8 KiB
Plaintext

---
title: Transition
description: Enter, exit, and shared-element motion between pages.
---
`SlideTransition` describes what happens when the active `Page` changes. There
is no default animation: pages snap unless the deck exports a transition or an
incoming page defines its own override.
```tsx title="slides/q2-launch/index.tsx"
import type { Page, SlideTransition } from '@open-slide/core';
const Cover: Page = () => <section>…</section>;
const Agenda: Page = () => <section>…</section>;
export const transition: SlideTransition = {
duration: 220,
exit: {
duration: 140,
easing: 'cubic-bezier(0.4, 0, 1, 1)',
keyframes: [
{ opacity: 1, transform: 'translateY(0)' },
{ opacity: 0, transform: 'translateY(-4px)' },
],
},
enter: {
duration: 220,
delay: 80,
easing: 'cubic-bezier(0, 0, 0.2, 1)',
keyframes: [
{ opacity: 0, transform: 'translateY(6px)' },
{ opacity: 1, transform: 'translateY(0)' },
],
},
};
export default [Cover, Agenda] satisfies Page[];
```
`prefers-reduced-motion: reduce` is honored automatically.
## Where It Lives
There are two declaration surfaces:
- `export const transition` in the slide module sets the deck default.
- `Page.transition = …` sets an override for one page.
The incoming page wins. Navigating `Cover → Agenda` uses
`Agenda.transition ?? module.transition`; the selected `exit` runs on
`Cover`, and `enter` runs on `Agenda`. Navigating backward uses the page you
are returning to.
```tsx
const Cover: Page = () => <section>…</section>;
const Agenda: Page = () => <section>…</section>;
export const transition: SlideTransition = {
duration: 220,
enter: {
keyframes: [{ opacity: 0 }, { opacity: 1 }],
},
};
Agenda.transition = {
duration: 320,
enter: {
keyframes: [
{ opacity: 0, transform: 'scale(0.98)' },
{ opacity: 1, transform: 'scale(1)' },
],
},
};
```
## Direction
During a page transition, the wrapper exposes:
| Surface | Forward | Backward |
| --- | --- | --- |
| `--osd-dir` | `1` | `-1` |
| `data-osd-dir` | `forward` | `backward` |
Use it when motion has a literal direction:
```tsx
{
transform: 'translateX(calc(var(--osd-dir, 1) * 8px))',
}
```
Most decks should keep a single restrained motion family: short duration,
subtle distance, opacity included, and no different transition vocabulary on
every page.
## Shared Elements
`SlideTransition.sharedElements` enables [SharedElement](/docs/primitive/shared-element)
matching for objects that should visually continue across two pages.
```ts
export const transition: SlideTransition = {
duration: 600,
sharedElements: {
duration: 600,
easing: 'cubic-bezier(0.22, 1, 0.36, 1)',
},
};
```
For the full schema and a reusable family of examples, see
[SlideTransition](/docs/reference/slide-transitions).