Skip to content

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.

arkts
@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.

arkts
@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.

arkts
// 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.

arkts
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 → instant

Transition group ​

STransitionGroup animates inserted and removed items; the remaining items move to their new positions with a layout animation (Vue FLIP move).

arkts
@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 &lt;Transition mode> around &lt;component :is> / &lt;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(...))).

  • pageBuilder receives the key; build the view from that argument (not from parent state) so the leaving view keeps its content. Use @LocalBuilder so this stays 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 because direction is a universal attribute): 'forward' | 'back' | 'auto'. 'auto' compares index (numeric, e.g. a tab index), then positions in order, 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.
PropTypeDefaultDescription
currentstring''
nameSparkTransitionName'fade'
modeSparkTransitionMode
SparkTransitionMode = 'default' | 'out-in' | 'in-out'
'default'
navDirectionSparkNavDirection
SparkNavDirection = 'forward' | 'back' | 'auto'
'auto'
orderstring[][]Ordered keys for navDirection: 'auto' (later = forward).
indexnumber-1Numeric position of current for navDirection: 'auto' (-1 = unused).
appearbooleanfalse
distancestring'100%'
clipContentbooleantrue
motionKeystring'transition'
speedSparkSpeed
SparkSpeed = 'fast' | 'normal' | 'slow'
'normal'
durationnumber-1
easestring''
enterEffectSparkTransitionEffectFactory | undefinedundefined
leaveEffectSparkTransitionEffectFactory | undefinedundefined
EventTypeDefaultDescription
onBeforeEnter(key: string) => void() => { }
onAfterEnter(key: string) => void() => { }
onBeforeLeave(key: string) => void() => { }
onAfterLeave(key: string) => void() => { }

Slots: pageBuilder

SPageTransition ​

Page-level SSwitchTransition (Vue &lt;RouterView v-slot="{ Component }">&lt;Transition :name>): direction-aware Material shared-axis X by default, history-based back detection unless order / index is given.

PropTypeDefaultDescription
currentstring''
nameSparkTransitionName'shared-axis-x'
modeSparkTransitionMode
SparkTransitionMode = 'default' | 'out-in' | 'in-out'
'default'
navDirectionSparkNavDirection
SparkNavDirection = 'forward' | 'back' | 'auto'
'auto'
orderstring[][]
indexnumber-1
appearbooleanfalse
distancestring'100%'
clipContentbooleantrue
motionKeystring'page-transition'
speedSparkSpeed
SparkSpeed = 'fast' | 'normal' | 'slow'
'normal'
durationnumber-1
easestring''
enterEffectSparkTransitionEffectFactory | undefinedundefined
leaveEffectSparkTransitionEffectFactory | undefinedundefined
EventTypeDefaultDescription
onBeforeEnter(key: string) => void() => { }
onAfterEnter(key: string) => void() => { }
onBeforeLeave(key: string) => void() => { }
onAfterLeave(key: string) => void() => { }

Slots: pageBuilder

STransition ​

Vue &lt;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.

PropTypeDefaultDescription
showbooleanfalseMount (true) or remove (false) the content.
nameSparkTransitionName'fade'
appearbooleanfalseAnimate the initial render as well (Vue appear).
distancestring'100%'Slide distance of the 'slide-*' presets (length string; default 100 % of the content).
motionKeystring'transition'
speedSparkSpeed
SparkSpeed = 'fast' | 'normal' | 'slow'
'normal'
durationnumber-1Per-instance duration in ms (-1 = from the motion key).
easestring''Per-instance easing ('ease-out', 'cubic-bezier(...)', 'spring(0.4, 0.8)'); '' = from the motion key.
enterEffectSparkTransitionEffectFactory | undefinedundefined
leaveEffectSparkTransitionEffectFactory | undefinedundefined
EventTypeDefaultDescription
onBeforeEnter() => void() => { }
onAfterEnter() => void() => { }
onBeforeLeave() => void() => { }
onAfterLeave() => void() => { }

Slots: children (trailing closure)

STransitionGroup ​

Vue &lt;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.

PropTypeDefaultDescription
itemsstring[][]
nameSparkTransitionName'fade'
axisSparkTransitionGroupAxis
SparkTransitionGroupAxis = 'vertical' | 'horizontal'
'vertical'
gapnumber0
distancestring'100%'Slide distance of the 'slide-*' presets (default 100 % of the item).
motionKeystring'transition-group'
speedSparkSpeed
SparkSpeed = 'fast' | 'normal' | 'slow'
'normal'
durationnumber-1
easestring''
enterEffectSparkTransitionEffectFactory | undefinedundefined
leaveEffectSparkTransitionEffectFactory | undefinedundefined

Slots: itemBuilder

MIT Licensed