refactor(theme): improve theme transition animation

This commit is contained in:
pengzhanbo 2025-04-01 21:28:27 +08:00
parent dc331337b7
commit 44c92faacf
13 changed files with 76 additions and 237 deletions

View File

@ -2,6 +2,8 @@
import { onClickOutside, useEventListener } from '@vueuse/core'
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
import '@vuepress/helper/transition/fade-in.css'
const props = defineProps<{
label: string
total: number
@ -44,10 +46,15 @@ useEventListener('scroll', updatePosition, { passive: true })
</script>
<template>
<span class="vp-annotation ignore-header" :class="{ active, [label]: true }" :aria-label="label">
<span ref="button" class="vpi-annotation" @click="active = !active" />
<span class="vp-annotation ignore-header" :class="{ active, [label]: true }">
<span
ref="button"
:aria-label="label"
class="vpi-annotation"
@click="active = !active"
/>
<ClientOnly>
<Transition name="fade">
<Transition name="fade-in">
<div
v-show="active" ref="popover"
class="annotations-popover" :class="{ list: list.length > 1 }"
@ -73,8 +80,8 @@ useEventListener('scroll', updatePosition, { passive: true })
position: relative;
top: -2px;
z-index: 2;
width: 1.2em;
height: 1.2em;
width: 1.5em;
height: 1.5em;
color: currentcolor;
cursor: pointer;
opacity: 0.5;

View File

@ -1,8 +1,10 @@
<script setup lang="ts">
import type { Ref } from 'vue'
import { FadeInExpandTransition } from '@vuepress/helper/client'
import { inject, ref, watch } from 'vue'
import { INJECT_COLLAPSE_KEY } from '../options.js'
import VPFadeInExpandTransition from './VPFadeInExpandTransition.vue'
import '@vuepress/helper/transition/fade-in-height-expand.css'
const props = defineProps<{
expand?: boolean
@ -54,13 +56,13 @@ function toggle() {
<slot name="title" />
</p>
</div>
<VPFadeInExpandTransition>
<FadeInExpandTransition>
<div v-show="expand" class="vp-collapse-content">
<div class="vp-collapse-content-inner">
<slot />
</div>
</div>
</VPFadeInExpandTransition>
</FadeInExpandTransition>
</div>
</template>

View File

@ -1,9 +1,11 @@
<script setup lang="ts">
import { FadeInExpandTransition } from '@vuepress/helper/client'
import { useResizeObserver } from '@vueuse/core'
import { useTemplateRef, watch } from 'vue'
import { ClientOnly, onContentUpdated } from 'vuepress/client'
import { useExpand } from '../composables/demo.js'
import '@vuepress/helper/transition/fade-in-height-expand.css'
import '../styles/demo.css'
const props = defineProps<{
@ -69,10 +71,14 @@ if (props.type === 'vue' && !__VUEPRESS_SSR__) {
</p>
</div>
<div class="demo-ctrl">
<span class="vpi-demo-code" @click="toggleCode" />
</div>
<div v-show="showCode" class="demo-code">
<slot name="code" />
<button type="button" aria-label="Toggle Code" @click="toggleCode">
<span class="vpi-demo-code" />
</button>
</div>
<FadeInExpandTransition>
<div v-show="showCode" class="demo-code">
<slot name="code" />
</div>
</FadeInExpandTransition>
</div>
</template>

View File

@ -1,8 +1,11 @@
<script setup lang="ts">
import type { DemoConfig } from '../composables/demo.js'
import { FadeInExpandTransition } from '@vuepress/helper/client'
import { useTemplateRef } from 'vue'
import { useExpand, useFence, useNormalDemo, useResources } from '../composables/demo.js'
import '@vuepress/helper/transition/fade-in.css'
import '@vuepress/helper/transition/fade-in-height-expand.css'
import '../styles/demo.css'
const props = defineProps<{
@ -91,7 +94,7 @@ const data = useFence(
</div>
<div v-if="resources.length" class="demo-resources">
<span ref="resourcesEl" class="vpi-demo-resources" title="Resources" aria-label="Resources" @click="toggleResources" />
<Transition name="fade">
<Transition name="fade-in">
<div v-show="showResources" class="demo-resources-container">
<div v-for="{ name, items } in resources" :key="name" class="demo-resources-list">
<p>{{ name }}</p>
@ -104,10 +107,14 @@ const data = useFence(
</div>
</Transition>
</div>
<span class="vpi-demo-code" @click="toggleCode" />
</div>
<div v-show="showCode" ref="fence" class="demo-code">
<slot />
<button type="button" aria-label="Toggle Code" @click="toggleCode">
<span class="vpi-demo-code" />
</button>
</div>
<FadeInExpandTransition>
<div v-show="showCode" ref="fence" class="demo-code">
<slot />
</div>
</FadeInExpandTransition>
</div>
</template>

View File

@ -1,154 +0,0 @@
<script setup lang="ts">
import { Transition, TransitionGroup } from 'vue'
const props = defineProps<{
group?: boolean
appear?: boolean
mode?: 'in-out' | 'out-in' | 'default'
onLeave?: () => void
onAfterLeave?: () => void
onAfterEnter?: () => void
width?: boolean
}>()
function handleBeforeLeave(el: HTMLElement): void {
if (props.width) {
el.style.maxWidth = `${el.offsetWidth}px`
}
else {
el.style.maxHeight = `${el.offsetHeight}px`
}
void el.offsetWidth
}
function handleLeave(el: HTMLElement): void {
if (props.width) {
el.style.maxWidth = '0'
}
else {
el.style.maxHeight = '0'
}
void el.offsetWidth
props.onLeave?.()
}
function handleAfterLeave(el: HTMLElement): void {
if (props.width) {
el.style.maxWidth = ''
}
else {
el.style.maxHeight = ''
}
props.onAfterLeave?.()
}
function handleEnter(el: HTMLElement): void {
el.style.transition = 'none'
if (props.width) {
const memorizedWidth = el.offsetWidth
el.style.maxWidth = '0'
void el.offsetWidth
el.style.transition = ''
el.style.maxWidth = `${memorizedWidth}px`
}
else {
const memorizedHeight = el.offsetHeight
el.style.maxHeight = '0'
void el.offsetWidth
el.style.transition = ''
el.style.maxHeight = `${memorizedHeight}px`
}
void el.offsetWidth
}
function handleAfterEnter(el: HTMLElement): void {
if (props.width) {
el.style.maxWidth = ''
}
else {
el.style.maxHeight = ''
}
props.onAfterEnter?.()
}
</script>
<template>
<component
:is="group ? TransitionGroup : Transition"
:name="width ? 'fade-in-width-expand' : 'fade-in-height-expand'"
:mode
:appear
@enter="handleEnter"
@after-enter="handleAfterEnter"
@before-leave="handleBeforeLeave"
@leave="handleLeave"
@after-leave="handleAfterLeave"
>
<slot />
</component>
</template>
<style>
.fade-in-height-expand-leave-from,
.fade-in-height-expand-enter-to,
.fade-in-width-expand-leave-from,
.fade-in-width-expand-enter-to {
opacity: 1;
}
.fade-in-height-expand-leave-to,
.fade-in-height-expand-enter-from {
padding-top: 0 !important;
padding-bottom: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
opacity: 0;
}
.fade-in-height-expand-leave-active {
overflow: hidden;
transition:
max-height cubic-bezier(0.4, 0, 0.2, 1) 0.3s,
opacity cubic-bezier(0, 0, 0.2, 1) 0.3s,
margin-top cubic-bezier(0.4, 0, 0.2, 1) 0.3s,
margin-bottom cubic-bezier(0.4, 0, 0.2, 1) 0.3s,
padding-top cubic-bezier(0.4, 0, 0.2, 1) 0.3s,
padding-bottom cubic-bezier(0.4, 0, 0.2, 1) 0.3s;
}
.fade-in-height-expand-enter-active {
overflow: hidden;
transition:
max-height cubic-bezier(0.4, 0, 0.2, 1) 0.3s,
opacity cubic-bezier(0.4, 0, 1, 1) 0.3s,
margin-top cubic-bezier(0.4, 0, 0.2, 1) 0.3s,
margin-bottom cubic-bezier(0.4, 0, 0.2, 1) 0.3s,
padding-top cubic-bezier(0.4, 0, 0.2, 1) 0.3s,
padding-bottom cubic-bezier(0.4, 0, 0.2, 1) 0.3s;
}
.fade-in-width-expand-leave-to,
.fade-in-width-expand-enter-from {
margin-right: 0 !important;
margin-left: 0 !important;
opacity: 0 !important;
}
.fade-in-width-expand-leave-active {
overflow: hidden;
transition:
max-width cubic-bezier(0.4, 0, 0.2, 1) 0.2s 0.1s,
opacity cubic-bezier(0.4, 0, 0.2, 1) 0.2s,
margin-right cubic-bezier(0.4, 0, 0.2, 1) 0.2s 0.1s,
margin-left cubic-bezier(0.4, 0, 0.2, 1) 0.2s 0.1s;
}
.fade-in-width-expand-enter-active {
overflow: hidden;
transition:
max-width cubic-bezier(0.4, 0, 0.2, 1) 0.2s,
opacity cubic-bezier(0.4, 0, 0.2, 1) 0.2s 0.1s,
margin-right cubic-bezier(0.4, 0, 0.2, 1) 0.2s,
margin-left cubic-bezier(0.4, 0, 0.2, 1) 0.2s;
}
</style>

View File

@ -1,3 +1 @@
export * from '../shared/index.js'
export { default as VPFadeInExpandTransition } from './components/VPFadeInExpandTransition.vue'

View File

@ -7,6 +7,8 @@ import { isLinkHttp } from 'vuepress/shared'
import { useBlogExtract, useData } from '../../composables/index.js'
import { inBrowser } from '../../utils/index.js'
import '@vuepress/helper/transition/fade-in.css'
const { theme } = useData()
const route = useRoute()
@ -60,7 +62,7 @@ const showBlogExtract = computed(() => {
<div class="vp-blog-extract" @click="open = !open">
<span class="vpi-blog-ext icon" />
</div>
<Transition name="fade">
<Transition name="fade-in">
<div v-show="open" class="blog-modal" @click.self="open = false">
<div class="blog-modal-container" :class="{ open: lazyOpen }">
<slot name="blog-extract-before" />
@ -154,17 +156,6 @@ const showBlogExtract = computed(() => {
z-index: var(--vp-z-index-overlay);
width: 100%;
background-color: rgba(0, 0, 0, 0.3);
opacity: 1;
}
.blog-modal.fade-enter-from,
.blog-modal.fade-leave-to {
opacity: 0;
}
.blog-modal.fade-leave-active,
.blog-modal.fade-enter-active {
transition: opacity 0.5s cubic-bezier(0.19, 1, 0.22, 1);
}
.blog-modal-container {

View File

@ -6,6 +6,8 @@ import VPNavScreenTranslates from '@theme/Nav/VPNavScreenTranslations.vue'
import { useScrollLock } from '@vueuse/core'
import { inBrowser } from '../../utils/index.js'
import '@vuepress/helper/transition/fade-in.css'
defineProps<{
open: boolean
}>()
@ -15,7 +17,7 @@ const isLocked = useScrollLock(inBrowser ? document.body : null)
<template>
<Transition
name="fade"
name="fade-in"
@enter="isLocked = true"
@after-leave="isLocked = false"
>

View File

@ -3,6 +3,8 @@ import { useElementSize, useWindowScroll, useWindowSize } from '@vueuse/core'
import { computed, onMounted, ref, shallowRef, watch } from 'vue'
import { useData } from '../composables/index.js'
import '@vuepress/helper/transition/fade-in.css'
const body = shallowRef<HTMLElement | null>()
const { height: bodyHeight } = useElementSize(body)
const { height: windowHeight } = useWindowSize()
@ -58,7 +60,7 @@ function handleClick() {
</script>
<template>
<Transition name="fade">
<Transition name="fade-in">
<button
v-show="!mustHidden && (show || isScrolling)"
type="button"

View File

@ -1,11 +1,13 @@
<script lang="ts" setup>
import '@vuepress/helper/transition/fade-in.css'
defineProps<{
show: boolean
}>()
</script>
<template>
<Transition name="fade">
<Transition name="fade-in">
<div v-if="show" class="vp-backdrop" />
</Transition>
</template>

View File

@ -2,6 +2,7 @@
import { hasGlobalComponent } from '@vuepress/helper/client'
import { resolveComponent } from 'vue'
import { useBulletinControl } from '../composables/index.js'
import '@vuepress/helper/transition/fade-in-scale-up.css'
const UserBulletin = hasGlobalComponent('Bulletin') ? resolveComponent('Bulletin') : null
const UserBulletinContent = hasGlobalComponent('BulletinContent') ? resolveComponent('BulletinContent') : null
@ -10,25 +11,27 @@ const { bulletin, showBulletin, enableBulletin, close } = useBulletinControl()
</script>
<template>
<component :is="UserBulletin" v-if="UserBulletin && enableBulletin && showBulletin" class="vp-bulletin" />
<div
v-else-if="bulletin && enableBulletin && showBulletin"
class="vp-bulletin preset" :class="{
border: bulletin.border ?? true,
[bulletin.layout ?? 'top-right']: true,
}"
>
<button type="button" class="close" @click="close">
<span class="vpi-close" />
</button>
<slot name="bulletin-content">
<h2 v-if="bulletin.title" v-html="bulletin.title" />
<div class="container">
<component :is="UserBulletinContent" v-if="UserBulletinContent" class="content vp-doc" />
<div v-else-if="bulletin.content" class="content vp-doc" v-html="bulletin.content" />
</div>
</slot>
</div>
<Transition name="fade-in-scale-up">
<component :is="UserBulletin" v-if="UserBulletin && enableBulletin && showBulletin" class="vp-bulletin" />
<div
v-else-if="bulletin && enableBulletin && showBulletin"
class="vp-bulletin preset" :class="{
border: bulletin.border ?? true,
[bulletin.layout ?? 'top-right']: true,
}"
>
<button type="button" class="close" @click="close">
<span class="vpi-close" />
</button>
<slot name="bulletin-content">
<h2 v-if="bulletin.title" v-html="bulletin.title" />
<div class="container">
<component :is="UserBulletinContent" v-if="UserBulletinContent" class="content vp-doc" />
<div v-else-if="bulletin.content" class="content vp-doc" v-html="bulletin.content" />
</div>
</slot>
</div>
</Transition>
</template>
<style>

View File

@ -5,6 +5,8 @@ import { onClickOutside } from '@vueuse/core'
import { nextTick, ref, watch } from 'vue'
import { useData } from '../composables/index.js'
import '@vuepress/helper/transition/fade-in-scale-up.css'
const props = defineProps<{
headers: MenuItem[]
navHeight: number
@ -56,7 +58,7 @@ function scrollToTop() {
<button v-else @click="scrollToTop">
{{ theme.returnToTopLabel || 'Return to top' }}
</button>
<Transition name="flyout">
<Transition name="fade-in-scale-up">
<div v-if="open" ref="items" class="items" @click="onItemClick">
<div class="header">
<a class="top-link" href="#" @click="scrollToTop">
@ -146,18 +148,4 @@ function scrollToTop() {
padding: 8px 0;
background-color: var(--vp-c-bg-soft);
}
.flyout-enter-active {
transition: all 0.2s ease-out;
}
.flyout-leave-active {
transition: all 0.15s ease-in;
}
.flyout-enter-from,
.flyout-leave-to {
opacity: 0;
transform: translateY(-16px);
}
</style>

View File

@ -17,21 +17,6 @@
}
/* ----------------- Transition ------------------------ */
.fade-enter-active {
transition: 0.25s ease !important;
transition-property: opacity;
}
.fade-leave-active {
transition: 0.25s cubic-bezier(0, 1, 0.3, 1) !important;
transition-property: opacity;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.fade-slide-y-enter-active {
transition: 0.15s ease !important;
transition-property: opacity, transform;