Transition
Vue-style <Transition>, keyed switchers and <TransitionGroup>, built on ArkUI component transitions (TransitionEffect.asymmetric + if / ForEach keys). Timing comes from SparkAnim: --motion-transition-* is configurable and motion off makes every switch instant.
Transitions are pure animation, which the web preview cannot reproduce, so the examples on this page show code only. See them on a HarmonyOS device or in the gallery app.
Show / hide
STransition mounts and removes its content with show (v-if). Lifecycle events: onBeforeEnter, onAfterEnter, onBeforeLeave, onAfterLeave.
@Local open: boolean = true;
SButton({ text: 'Toggle' }).onClick(() => this.open = !this.open)
STransition({ show: this.open, name: 'slide-down', distance: '24vp',
onAfterEnter: () => { ... }, onAfterLeave: () => { ... } }) {
DemoPane({ title: 'Hello', body: '...' })
}Switch transition
SSwitchTransition renders pageBuilder(current) and animates between the old and the new view; both are mounted only while the transition runs. mode 'out-in' / 'in-out' sequences them, 'default' runs both at once. Going to an earlier key in order reverses direction-aware presets.
@Local view: string = 'inbox';
@LocalBuilder
renderView(key: string) {
// build from the key argument, so the leaving view keeps its own content
DemoPane({ title: key, body: '...' })
}
SSwitchTransition({
current: this.view,
pageBuilder: this.renderView,
name: 'shared-axis-x', // fade | slide-* | scale | zoom | fade-through | shared-axis-x/y/z | none
mode: 'out-in', // 'default' (simultaneous) | 'out-in' | 'in-out'
order: ['inbox', 'drafts', 'sent'], // navDirection 'auto': later key = forward
onBeforeLeave: (k: string) => { ... },
onAfterEnter: (k: string) => { ... }
})Page transition
SPageTransition is the page-level switcher (RouterView + Transition): shared-axis X by default, direction from index, order or the visit history. Wrap the gallery's DocContent the same way to animate between doc pages.
// Vue: <RouterView v-slot="{ Component }"><Transition name="slide-left">…
@Local step: number = 0;
@LocalBuilder
renderStep(key: string) { DemoPane({ title: key }) }
SPageTransition({
current: STEPS[this.step],
index: this.step, // or order: STEPS, or neither (visit history: going back = back)
pageBuilder: this.renderStep,
name: 'slide-left' // default 'shared-axis-x'
})
SButton({ text: 'Back' }).onClick(() => this.step--) // reverses the slide
SButton({ text: 'Next' }).onClick(() => this.step++)
// gallery shell (pages/Index.ets): animate between doc pages
@LocalBuilder renderPage(key: string) { DocContent({ page: key }) }
SPageTransition({ current: this.page, pageBuilder: this.renderPage, order: NAV_ORDER, name: 'fade-through' })Custom effects & timing
enterEffect / leaveEffect take any TransitionEffect (inheriting the resolved duration and curve); duration and ease override the motion key per instance.
STransition({
show: this.open,
duration: 600, // per-instance override (ms); motion off still wins
ease: 'spring(0.5, 0.7)',
enterEffect: (env: SparkTransitionEnv) => TransitionEffect.rotate({ z: 1, angle: -90 })
.combine(TransitionEffect.scale({ x: 0.5, y: 0.5 })).combine(TransitionEffect.OPACITY),
leaveEffect: (env: SparkTransitionEnv) => TransitionEffect.translate({ x: 120 })
.combine(TransitionEffect.OPACITY)
}) { ... }
// theme: --motion-transition-duration: 300ms; --motion-transition-ease: ease-out;
// --motion-page-transition-*, --motion-transition-group-*; --motion-enabled: false → instantTransition group
STransitionGroup animates inserted and removed items; the remaining items move to their new positions with a layout animation (Vue FLIP move).
@Local ids: string[] = ['Apple', 'Banana', 'Cherry'];
@LocalBuilder
renderItem(key: string) { DemoRow({ text: key }) }
STransitionGroup({ items: this.ids, itemBuilder: this.renderItem, name: 'slide-left', distance: '40vp', gap: 8 })
// assign a new array: insert / remove animate, the other rows glide to their new place
this.ids = [...];API
Extracted from the @Param / @Event / @BuilderParam declarations in the library sources.
SSwitchTransition
Keyed content switcher (Vue <Transition mode> around <component :is> / <RouterView v-slot>): renders pageBuilder(current) and, when current changes, animates from the old view to the new one. Both views are mounted only while the transition runs (ForEach keyed slots + .transition(TransitionEffect.asymmetric(...))).
pageBuilderreceives the key; build the view from that argument (not from parent state) so the leaving view keeps its content. Use@LocalBuildersothisstays the parent.mode: 'default' (simultaneous, views overlap in a Stack), 'out-in' (old leaves, then new enters), 'in-out' (new enters, then old leaves).navDirection(named so becausedirectionis a universal attribute): 'forward' | 'back' | 'auto'. 'auto' comparesindex(numeric, e.g. a tab index), then positions inorder, then the visit history (returning to the previous key = back). Direction-aware presets (slide-, zoom, shared-axis-) reverse on back.- Lifecycle events receive the key: onBeforeEnter / onAfterEnter / onBeforeLeave / onAfterLeave.
clipContent(default true) clips sliding views to the switcher's bounds.
| Prop | Type | Default | Description |
|---|---|---|---|
current | string | '' | |
name | SparkTransitionName | 'fade' | |
mode | SparkTransitionModeSparkTransitionMode = 'default' | 'out-in' | 'in-out' | 'default' | |
navDirection | SparkNavDirectionSparkNavDirection = 'forward' | 'back' | 'auto' | 'auto' | |
order | string[] | [] | Ordered keys for navDirection: 'auto' (later = forward). |
index | number | -1 | Numeric position of current for navDirection: 'auto' (-1 = unused). |
appear | boolean | false | |
distance | string | '100%' | |
clipContent | boolean | true | |
motionKey | string | 'transition' | |
speed | SparkSpeedSparkSpeed = 'fast' | 'normal' | 'slow' | 'normal' | |
duration | number | -1 | |
ease | string | '' | |
enterEffect | SparkTransitionEffectFactory | undefined | undefined | |
leaveEffect | SparkTransitionEffectFactory | undefined | undefined |
| Event | Type | Default | Description |
|---|---|---|---|
onBeforeEnter | (key: string) => void | () => { } | |
onAfterEnter | (key: string) => void | () => { } | |
onBeforeLeave | (key: string) => void | () => { } | |
onAfterLeave | (key: string) => void | () => { } |
Slots: pageBuilder
SPageTransition
Page-level SSwitchTransition (Vue <RouterView v-slot="{ Component }"><Transition :name>): direction-aware Material shared-axis X by default, history-based back detection unless order / index is given.
| Prop | Type | Default | Description |
|---|---|---|---|
current | string | '' | |
name | SparkTransitionName | 'shared-axis-x' | |
mode | SparkTransitionModeSparkTransitionMode = 'default' | 'out-in' | 'in-out' | 'default' | |
navDirection | SparkNavDirectionSparkNavDirection = 'forward' | 'back' | 'auto' | 'auto' | |
order | string[] | [] | |
index | number | -1 | |
appear | boolean | false | |
distance | string | '100%' | |
clipContent | boolean | true | |
motionKey | string | 'page-transition' | |
speed | SparkSpeedSparkSpeed = 'fast' | 'normal' | 'slow' | 'normal' | |
duration | number | -1 | |
ease | string | '' | |
enterEffect | SparkTransitionEffectFactory | undefined | undefined | |
leaveEffect | SparkTransitionEffectFactory | undefined | undefined |
| Event | Type | Default | Description |
|---|---|---|---|
onBeforeEnter | (key: string) => void | () => { } | |
onAfterEnter | (key: string) => void | () => { } | |
onBeforeLeave | (key: string) => void | () => { } | |
onAfterLeave | (key: string) => void | () => { } |
Slots: pageBuilder
STransition
Vue <Transition> for one conditional element (v-if): the content is inserted / removed when show changes and animates with an official component transition (.transition(TransitionEffect.asymmetric(...), onFinish)).
Timing: motion key motionKey (default 'transition', --motion-transition-duration\|ease\|enabled) at speed, overridable per instance with duration (ms) / ease; --motion-enabled: false makes it instant. Keyed content (switch between pages / views) → SSwitchTransition; lists → STransitionGroup.
| Prop | Type | Default | Description |
|---|---|---|---|
show | boolean | false | Mount (true) or remove (false) the content. |
name | SparkTransitionName | 'fade' | |
appear | boolean | false | Animate the initial render as well (Vue appear). |
distance | string | '100%' | Slide distance of the 'slide-*' presets (length string; default 100 % of the content). |
motionKey | string | 'transition' | |
speed | SparkSpeedSparkSpeed = 'fast' | 'normal' | 'slow' | 'normal' | |
duration | number | -1 | Per-instance duration in ms (-1 = from the motion key). |
ease | string | '' | Per-instance easing ('ease-out', 'cubic-bezier(...)', 'spring(0.4, 0.8)'); '' = from the motion key. |
enterEffect | SparkTransitionEffectFactory | undefined | undefined | |
leaveEffect | SparkTransitionEffectFactory | undefined | undefined |
| Event | Type | Default | Description |
|---|---|---|---|
onBeforeEnter | () => void | () => { } | |
onAfterEnter | () => void | () => { } | |
onBeforeLeave | () => void | () => { } | |
onAfterLeave | () => void | () => { } |
Slots: children (trailing closure)
STransitionGroup
Vue <TransitionGroup>: a keyed list whose inserted / removed items animate with the preset (or custom effects), while the remaining items glide to their new positions (Vue's FLIP move class). The move is an ArkUI layout animation: the rendered list is updated inside SparkAnim.runWith (animateTo + applySync), so position changes caused by the update animate with the same duration / curve.
Update items by assigning a new array (this.ids = [...]); keys must be unique and stable.
| Prop | Type | Default | Description |
|---|---|---|---|
items | string[] | [] | |
name | SparkTransitionName | 'fade' | |
axis | SparkTransitionGroupAxisSparkTransitionGroupAxis = 'vertical' | 'horizontal' | 'vertical' | |
gap | number | 0 | |
distance | string | '100%' | Slide distance of the 'slide-*' presets (default 100 % of the item). |
motionKey | string | 'transition-group' | |
speed | SparkSpeedSparkSpeed = 'fast' | 'normal' | 'slow' | 'normal' | |
duration | number | -1 | |
ease | string | '' | |
enterEffect | SparkTransitionEffectFactory | undefined | undefined | |
leaveEffect | SparkTransitionEffectFactory | undefined | undefined |
Slots: itemBuilder