Transition
17:06Description
An enter/leave transition wrapper driven by CSS transition plus class switching (uni-app x on App does not support @keyframes, so it avoids them). Seven presets are built in — fade / slide-* / zoom-* — tunable via duration / timing-function / delay. Completion is judged by a duration + delay timer (App may not fire transitionend), so after-enter / after-leave fire after the configured time.
Compatibility
| Platform | Support |
|---|---|
| App | ✅ Supported (animation runs on CSS transition + class switching, not @keyframes; completion is timed by a timer, not transitionend) |
| H5 | ✅ Supported |
| WeChat Mini Program | ✅ Supported |
Examples
Basic Usage
show controls visibility with a fade transition by default; the component only handles the transition while content sizing is up to you.
View source
<template>
<ux-transition :show="show" name="fade" :duration="300">
<view class="card">
<text>过渡内容</text>
</view>
</ux-transition>
<ux-button text="切换显示" type="primary" shape="round" @click="show = !show" />
</template>
<script setup lang="uts">
const show = ref(true);
</script>Presets (name)
Seven presets: fade / slide-up / slide-down / slide-left / slide-right / zoom-in / zoom-out. Tap to switch and replay instantly.
View source
<template>
<!-- name 可选 fade / slide-up / slide-down / slide-left / slide-right / zoom-in / zoom-out -->
<ux-transition :show="show" :name="current" :duration="300">
<view class="card">
<text>过渡内容</text>
</view>
</ux-transition>
<ux-button text="重播动画" type="primary" shape="round" @click="replay" />
</template>
<script setup lang="uts">
const show = ref(true);
const current = ref('slide-up');
// 离场过渡结束后再进场,完成一次重播
const replay = () => {
show.value = false;
setTimeout(() => {
show.value = true;
}, 450);
};
</script>Duration
duration sets the enter/leave transition length in milliseconds.
View source
<template>
<ux-transition :show="show" name="slide-up" :duration="duration">
<view class="card">
<text>过渡内容</text>
</view>
</ux-transition>
<ux-button text="重播动画" type="primary" shape="round" @click="replay" />
</template>
<script setup lang="uts">
const show = ref(true);
const duration = ref(300);
const replay = () => {
show.value = false;
setTimeout(() => {
show.value = true;
}, duration.value + 150);
};
</script>Timing Function
timing-function customizes the easing curve; the bounce curve adds elasticity to the animation.
View source
<template>
<ux-transition :show="show" name="zoom-in" :duration="500" :timing-function="timingFunction">
<view class="card">
<text>过渡内容</text>
</view>
</ux-transition>
</template>
<script setup lang="uts">
const show = ref(true);
// 默认曲线(标准);回弹曲线可取 cubic-bezier(0.175, 0.885, 0.32, 1.275)
const timingFunction = ref('cubic-bezier(0.2, 0.8, 0.2, 1)');
</script>Delay
delay adds waiting time before the transition starts, applied to both enter and leave; completion events fire after duration + delay.
View source
<template>
<view class="row">
<text>延迟 (ms)</text>
<ux-number-box v-model="delay" :step="100" :min="0" :max="600" />
</view>
<ux-transition :show="show" name="slide-left" :duration="300" :delay="delay">
<view class="card">
<text>过渡内容</text>
</view>
</ux-transition>
</template>
<script setup lang="uts">
const show = ref(true);
const delay = ref(0);
</script>First Render (appear)
When appear=true the enter animation plays on first mount; remount is required to reproduce it (outer v-if rebuild).
View source
<template>
<view class="row">
<text>首次渲染播放进场动画</text>
<ux-switch v-model="appear" />
</view>
<!-- 外层 v-if 重挂:appear 只在组件首次挂载时生效,重新挂载才能复现 -->
<view v-if="mounted">
<ux-transition :show="true" name="zoom-out" :duration="500" :appear="appear">
<view class="card">
<text>过渡内容</text>
</view>
</ux-transition>
</view>
<ux-button text="重新挂载" type="primary" shape="round" @click="remount" />
</template>
<script setup lang="uts">
const appear = ref(true);
const mounted = ref(true);
const remount = () => {
mounted.value = false;
setTimeout(() => {
mounted.value = true;
}, 50);
};
</script>Events
Full lifecycle hooks: before-enter / enter / after-enter / enter-cancelled / before-leave / leave / after-leave / leave-cancelled.
View source
<template>
<ux-transition
:show="show"
name="slide-up"
:duration="400"
@before-enter="logEvent('before-enter')"
@enter="logEvent('enter')"
@after-enter="logEvent('after-enter')"
@enter-cancelled="logEvent('enter-cancelled')"
@before-leave="logEvent('before-leave')"
@leave="logEvent('leave')"
@after-leave="logEvent('after-leave')"
@leave-cancelled="logEvent('leave-cancelled')"
>
<view class="card">
<text>过渡内容</text>
</view>
</ux-transition>
<ux-button text="切换显示" type="primary" shape="round" @click="show = !show" />
<view class="log">
<text v-for="(item, index) in logs" :key="index">{{ item }}</text>
</view>
</template>
<script setup lang="uts">
const show = ref(true);
const logs = ref<string[]>([]);
const logEvent = (name: string) => {
logs.value = ([name] as string[]).concat(logs.value.slice(0, 7));
};
</script>Custom Styles (custom-class / custom-style)
custom-class extends the root class; custom-style applies inline styles.
View source
<template>
<ux-transition
custom-class="my-transition"
:custom-style="customStyle"
:show="show"
name="fade"
:duration="400"
>
<view class="card">
<text>过渡内容</text>
</view>
</ux-transition>
</template>
<script setup lang="uts">
const show = ref(true);
const customStyle = ref<UTSJSONObject>({
'background-color': '#edf4ff',
'border-radius': '8px'
});
</script>
<style scoped>
/* custom-class 外部类:为过渡根节点补充内边距 */
.my-transition {
padding: 12px;
}
</style>API
Props
| Property | Description | Type | Default | Version |
|---|---|---|---|---|
customStyle | Custom root element style | UTSJSONObject | - | - |
customClass | Custom root element style class | string | '' | - |
show | Whether the content is shown (default true) | Boolean | true | - |
name | Preset animation: fade / slide-up / slide-down / slide-left / slide-right / zoom-in / zoom-out (default fade) | String | 'fade' | - |
duration | Enter/leave animation duration in ms (default 300) | Number | 300 | - |
timingFunction | Easing curve (default cubic-bezier(0.2, 0.8, 0.2, 1)) | String | 'cubic-bezier(0.2, 0.8, 0.2, 1)' | - |
delay | Animation delay in ms, applied to both enter and leave (default 0) | Number | 0 | - |
appear | Whether to play the enter animation on first render (default false) | Boolean | false | - |
Events
| Event | Description | Params | Version |
|---|---|---|---|
before-enter | Entering begins | - | - |
enter | The enter animation actually starts (after the one-beat terminal-class switch following mount; in the re-open case it fires on the same beat as before-enter) | - | - |
after-enter | The enter animation ends | - | - |
enter-cancelled | The enter animation is interrupted by a leave before completing | - | - |
before-leave | Leaving begins | - | - |
leave | The leave animation starts (same beat as before-leave, reserved) | - | - |
after-leave | The leave animation ends (the DOM has been unmounted) | - | - |
leave-cancelled | The leave animation is interrupted by a re-enter before completing | - | - |
Slots
| Slot | Description | Version |
|---|---|---|
default | The content to transition (animated as a whole on enter/leave) | - |
Notes
appearonly takes effect on the component's first mount; togglingshowafterwards does not replay the enter animation — remount the component (outerv-if) to reproduce it.- Completion events (
after-enter/after-leave) are judged by aduration + delaytimer, nottransitionend(App may not fire it), so they may be one beat off the actual rendered frame. - DOM is unmounted only after
leavefinishes; interrupting an enter with a leave emitsenter-cancelled, and re-opening during a leave emitsleave-cancelled. - An unknown
namefalls back tofade. - On App,
transition-duration/transition-delaydo not support multi-value lists, so duration / delay / curve are emitted inline as single values and the start/end properties come from the preset classes.