diff --git a/docs/tools/helper/client.md b/docs/tools/helper/client.md index 0a01e9400d..a2fde8a386 100644 --- a/docs/tools/helper/client.md +++ b/docs/tools/helper/client.md @@ -163,3 +163,74 @@ onMounted(() => { ``` ::: + +## Component + +### FadeInExpandTransition + +Provides fade-in transition effects when block-level elements expand, supporting both `height` or `width` properties. + +**Props:** + +```ts +interface FadeInExpandTransitionProps { + /** + * Whether to group transitions + */ + group?: boolean + /** + * Transition mode + */ + mode?: 'default' | 'in-out' | 'out-in' + + /** + * Whether to switch to the transition of `width` + * + * @default false + */ + width?: boolean + + appear?: boolean + onLeave?: () => void + onAfterEnter?: () => void + onAfterLeave?: () => void +} +``` + +**Import Styles:** + +Transition animations require importing the following CSS files as needed: + +- `@vuepress/helper/transition/fade-in-height-expand.css` - `height` transition animation + +- `@vuepress/helper/transition/fade-in-width-expand.css` - `width` transition animation + +::: tip Only one CSS file needs to be imported + +::: + +**Usage:** + +```vue + + + +``` diff --git a/docs/tools/helper/style.md b/docs/tools/helper/style.md index 72e8469d0a..469b182785 100644 --- a/docs/tools/helper/style.md +++ b/docs/tools/helper/style.md @@ -9,3 +9,67 @@ The following styles are provided. ## Normalize `@vuepress/helper/normalize.css` is a CSS file that normalizes the default styles of the browser. It is recommended to import it in community themes. + +## Transitions + +`@vuepress/helper/transition/*.css` is a collection of CSS files that provide transitions for elements. It is recommended to import for use as needed in community themes. + +- `fade-in.css` +- `fade-in-up.css` +- `fade-in-down.css` +- `fade-in-left.css` +- `fade-in-right.css` +- `fade-in-scale-up.css` +- `slide-in-up.css` +- `slide-in-down.css` +- `slide-in-left.css` +- `slide-in-right.css` + +**Usage:** + +```vue + + + +``` + +**CSS Variables:** + +```css +:root { + /* general transitions variables */ + + --transition-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --transition-ease-out: cubic-bezier(0, 0, 0.2, 1); + --transition-ease-in: cubic-bezier(0.4, 0, 1, 1); + --transition-duration: 0.3s; + --transition-enter-duration: var(--transition-duration); + --transition-leave-duration: 0.2s; + --transition-delay: 0.1s; + + /* specific transitions variables */ + + --transition-fade-in-up-offset: 10px; + --transition-fade-in-down-offset: -10px; + --transition-fade-in-left-offset: 10px; + --transition-fade-in-right-offset: -10px; + + --transition-fade-in-scale-up-scale: 0.9; + --transition-fade-in-scale-up-duration: 0.2s; + --transition-fade-in-scale-up-origin: inherit; + + --transition-slide-in-up-offset: 100%; + --transition-slide-in-down-offset: -100%; + --transition-slide-in-left-offset: 100%; + --transition-slide-in-right-offset: -100%; +} +``` diff --git a/docs/zh/tools/helper/client.md b/docs/zh/tools/helper/client.md index 57c3c2e813..6b3b46a977 100644 --- a/docs/zh/tools/helper/client.md +++ b/docs/zh/tools/helper/client.md @@ -163,3 +163,72 @@ onMounted(() => { ``` ::: + +## 组件{#component} + +### FadeInExpandTransition + +为块级元素的展开提供淡入淡出过渡效果,支持 `height` 或 `width` 属性。 + +**Props:** + +```ts +interface FadeInExpandTransitionProps { + /** + * 是否分组过渡 + */ + group?: boolean + /** + * 过渡模式 + */ + mode?: 'default' | 'in-out' | 'out-in' + + /** + * 是否切换为 `width` 的过渡 + * + * @default false + */ + width?: boolean + + appear?: boolean + onLeave?: () => void + onAfterEnter?: () => void + onAfterLeave?: () => void +} +``` + +**导入样式:** + +过渡动画需要按需引入以下 CSS 文件: + +- `@vuepress/helper/transition/fade-in-height-expand.css` - `height` 过渡动画 +- `@vuepress/helper/transition/fade-in-width-expand.css` - `width` 过渡动画 + +::: tip 只需要引入其中一个 CSS 文件 +::: + +**Usage:** + +```vue + + + +``` diff --git a/docs/zh/tools/helper/style.md b/docs/zh/tools/helper/style.md index 27eb4c2e10..d38b1ae907 100644 --- a/docs/zh/tools/helper/style.md +++ b/docs/zh/tools/helper/style.md @@ -9,3 +9,67 @@ icon: paintbrush-vertical ## 规范化 `@vuepress/helper/normalize.css` 是一个 CSS 文件,用于规范化浏览器的默认样式。推荐在社区主题中引入它。 + +## 过渡{#transitions} + +`@vuepress/helper/transition/*.css` 是一组提供元素过渡效果的CSS文件集合,推荐在社区主题中按需导入使用。 + +- `fade-in.css` +- `fade-in-up.css` +- `fade-in-down.css` +- `fade-in-left.css` +- `fade-in-right.css` +- `fade-in-scale-up.css` +- `slide-in-up.css` +- `slide-in-down.css` +- `slide-in-left.css` +- `slide-in-right.css` + +**使用:** + +```vue + + + +``` + +**CSS 变量:** + +```css +:root { + /* general transitions variables */ + + --transition-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --transition-ease-out: cubic-bezier(0, 0, 0.2, 1); + --transition-ease-in: cubic-bezier(0.4, 0, 1, 1); + --transition-duration: 0.3s; + --transition-enter-duration: var(--transition-duration); + --transition-leave-duration: 0.2s; + --transition-delay: 0.1s; + + /* specific transitions variables */ + + --transition-fade-in-up-offset: 10px; + --transition-fade-in-down-offset: -10px; + --transition-fade-in-left-offset: 10px; + --transition-fade-in-right-offset: -10px; + + --transition-fade-in-scale-up-scale: 0.9; + --transition-fade-in-scale-up-duration: 0.2s; + --transition-fade-in-scale-up-origin: inherit; + + --transition-slide-in-up-offset: 100%; + --transition-slide-in-down-offset: -100%; + --transition-slide-in-left-offset: 100%; + --transition-slide-in-right-offset: -100%; +} +``` diff --git a/tools/helper/package.json b/tools/helper/package.json index 922d1efe4c..8240e7f589 100644 --- a/tools/helper/package.json +++ b/tools/helper/package.json @@ -33,6 +33,7 @@ "./colors.css": "./lib/client/styles/colors.css", "./normalize.css": "./lib/client/styles/normalize.css", "./sr-only.css": "./lib/client/styles/sr-only.css", + "./transition/*.css": "./lib/client/styles/transition/*.css", "./scss/*.scss": { "sass": "./scss/*.scss" }, diff --git a/tools/helper/src/client/components/Transitions/FadeInExpandTransition.ts b/tools/helper/src/client/components/Transitions/FadeInExpandTransition.ts new file mode 100644 index 0000000000..e696e7b798 --- /dev/null +++ b/tools/helper/src/client/components/Transitions/FadeInExpandTransition.ts @@ -0,0 +1,89 @@ +import type { Component, PropType } from 'vue' +import { Transition, TransitionGroup, defineComponent, h } from 'vue' + +export const FadeInExpandTransition = defineComponent({ + name: 'FadeInExpandTransition', + props: { + /* Whether to group transitions */ + group: Boolean, + appear: Boolean, + /* Whether to switch to the transition of `width` */ + width: Boolean, + mode: String as PropType<'default' | 'in-out' | 'out-in'>, + onLeave: Function, + onAfterLeave: Function, + onAfterEnter: Function, + }, + setup(props, { slots }) { + const handleBeforeLeave = (el: HTMLElement): void => { + if (props.width) { + el.style.maxWidth = `${el.offsetWidth}px` + } else { + el.style.maxHeight = `${el.offsetHeight}px` + } + void el.offsetWidth + } + + const handleLeave = (el: HTMLElement): void => { + if (props.width) { + el.style.maxWidth = '0' + } else { + el.style.maxHeight = '0' + } + void el.offsetWidth + props.onLeave?.() + } + + const handleAfterLeave = (el: HTMLElement): void => { + if (props.width) { + el.style.maxWidth = '' + } else { + el.style.maxHeight = '' + } + props.onAfterLeave?.() + } + + const 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 + } + + const handleAfterEnter = (el: HTMLElement): void => { + if (props.width) { + el.style.maxWidth = '' + } else { + el.style.maxHeight = '' + } + props.onAfterEnter?.() + } + + return () => + h( + (props.group ? TransitionGroup : Transition) as Component, + { + name: props.width ? 'fade-in-width-expand' : 'fade-in-height-expand', + appear: props.appear, + mode: props.mode, + onEnter: handleEnter, + onAfterEnter: handleAfterEnter, + onBeforeLeave: handleBeforeLeave, + onLeave: handleLeave, + onAfterLeave: handleAfterLeave, + }, + slots, + ) + }, +}) diff --git a/tools/helper/src/client/components/Transitions/index.ts b/tools/helper/src/client/components/Transitions/index.ts new file mode 100644 index 0000000000..cb4bc9553c --- /dev/null +++ b/tools/helper/src/client/components/Transitions/index.ts @@ -0,0 +1 @@ +export * from './FadeInExpandTransition.js' diff --git a/tools/helper/src/client/components/index.ts b/tools/helper/src/client/components/index.ts index 6341852eb3..28869f6655 100644 --- a/tools/helper/src/client/components/index.ts +++ b/tools/helper/src/client/components/index.ts @@ -1 +1,2 @@ export * from './LoadingIcon.js' +export * from './Transitions/index.js' diff --git a/tools/helper/src/client/styles/transition/fade-in-down.scss b/tools/helper/src/client/styles/transition/fade-in-down.scss new file mode 100644 index 0000000000..ef477c624d --- /dev/null +++ b/tools/helper/src/client/styles/transition/fade-in-down.scss @@ -0,0 +1,26 @@ +@import './vars.css'; + +:root { + --transition-fade-in-down-offset: -10px; +} + +.fade-in-down { + &-enter-from, + &-leave-to { + opacity: 0; + transform: translateY(var(--transition-fade-in-down-offset)); + } + + &-enter-to, + &-leave-from { + opacity: 1; + transform: translateY(0); + } + + &-leave-active, + &-enter-active { + transition: + opacity var(--transition-duration) var(--transition-ease-in-out), + transform var(--transition-duration) var(--transition-ease-in-out); + } +} diff --git a/tools/helper/src/client/styles/transition/fade-in-height-expand.scss b/tools/helper/src/client/styles/transition/fade-in-height-expand.scss new file mode 100644 index 0000000000..b4327fe940 --- /dev/null +++ b/tools/helper/src/client/styles/transition/fade-in-height-expand.scss @@ -0,0 +1,40 @@ +@import './vars.css'; + +.fade-in-height-expand { + &-leave-from, + &-enter-to { + opacity: 1; + } + + &-leave-to, + &-enter-from { + margin-top: 0 !important; + margin-bottom: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; + + opacity: 0; + } + + &-leave-active { + overflow: hidden; + transition: + max-height var(--transition-duration) var(--transition-ease-in-out), + opacity var(--transition-duration) var(--transition-ease-out), + margin-top var(--transition-duration) var(--transition-ease-in-out), + margin-bottom var(--transition-duration) var(--transition-ease-in-out), + padding-top var(--transition-duration) var(--transition-ease-in-out), + padding-bottom var(--transition-duration) var(--transition-ease-in-out); + } + + &-enter-active { + overflow: hidden; + transition: + max-height var(--transition-duration) var(--transition-ease-in-out), + opacity var(--transition-duration) var(--transition-ease-in), + margin-top var(--transition-duration) var(--transition-ease-in-out), + margin-bottom var(--transition-duration) var(--transition-ease-in-out), + padding-top var(--transition-duration) var(--transition-ease-in-out), + padding-bottom var(--transition-duration) var(--transition-ease-in-out); + } +} diff --git a/tools/helper/src/client/styles/transition/fade-in-left.scss b/tools/helper/src/client/styles/transition/fade-in-left.scss new file mode 100644 index 0000000000..b962170c48 --- /dev/null +++ b/tools/helper/src/client/styles/transition/fade-in-left.scss @@ -0,0 +1,26 @@ +@import './vars.css'; + +:root { + --transition-fade-in-left-offset: 10px; +} + +.fade-in-left { + &-enter-from, + &-leave-to { + opacity: 0; + transform: translateX(var(--transition-fade-in-left-offset)); + } + + &-enter-to, + &-leave-from { + opacity: 1; + transform: translateX(0); + } + + &-leave-active, + &-enter-active { + transition: + opacity var(--transition-duration) var(--transition-ease-in-out), + transform var(--transition-duration) var(--transition-ease-in-out); + } +} diff --git a/tools/helper/src/client/styles/transition/fade-in-right.scss b/tools/helper/src/client/styles/transition/fade-in-right.scss new file mode 100644 index 0000000000..edde3fa473 --- /dev/null +++ b/tools/helper/src/client/styles/transition/fade-in-right.scss @@ -0,0 +1,26 @@ +@import './vars.css'; + +:root { + --transition-fade-in-right-offset: -10px; +} + +.fade-in-left { + &-enter-from, + &-leave-to { + opacity: 0; + transform: translateX(var(--transition-fade-in-right-offset)); + } + + &-enter-to, + &-leave-from { + opacity: 1; + transform: translateX(0); + } + + &-leave-active, + &-enter-active { + transition: + opacity var(--transition-duration) var(--transition-ease-in-out), + transform var(--transition-duration) var(--transition-ease-in-out); + } +} diff --git a/tools/helper/src/client/styles/transition/fade-in-scale-up.scss b/tools/helper/src/client/styles/transition/fade-in-scale-up.scss new file mode 100644 index 0000000000..8deff2fdce --- /dev/null +++ b/tools/helper/src/client/styles/transition/fade-in-scale-up.scss @@ -0,0 +1,39 @@ +@import './vars.css'; + +:root { + --transition-fade-in-scale-up-scale: 0.9; + --transition-fade-in-scale-up-duration: 0.2s; + --transition-fade-in-scale-up-origin: inherit; +} + +.fade-in-scale-up { + &-leave-active { + transition: + opacity var(--transition-fade-in-scale-up-duration) + var(--transition-ease-in), + transform var(--transition-fade-in-scale-up-duration) + var(--transition-ease-in); + transform-origin: var(--transition-fade-in-scale-up-origin); + } + + &-enter-active { + transition: + opacity var(--transition-fade-in-scale-up-duration) + var(--transition-ease-out), + transform var(--transition-fade-in-scale-up-duration) + var(--transition-ease-out); + transform-origin: var(--transition-fade-in-scale-up-origin); + } + + &-enter-from, + &-leave-to { + opacity: 0; + transform: scale(var(--transition-fade-in-scale-up-scale)); + } + + &-leave-from, + &-enter-to { + opacity: 1; + transform: scale(1); + } +} diff --git a/tools/helper/src/client/styles/transition/fade-in-up.scss b/tools/helper/src/client/styles/transition/fade-in-up.scss new file mode 100644 index 0000000000..85e829d828 --- /dev/null +++ b/tools/helper/src/client/styles/transition/fade-in-up.scss @@ -0,0 +1,26 @@ +@import './vars.css'; + +:root { + --transition-fade-in-up-offset: 10px; +} + +.fade-in-up { + &-enter-from, + &-leave-to { + opacity: 0; + transform: translateY(var(--transition-fade-in-up-offset)); + } + + &-enter-to, + &-leave-from { + opacity: 1; + transform: translateY(0); + } + + &-leave-active, + &-enter-active { + transition: + opacity var(--transition-duration) var(--transition-ease-in-out), + transform var(--transition-duration) var(--transition-ease-in-out); + } +} diff --git a/tools/helper/src/client/styles/transition/fade-in-width-expand.scss b/tools/helper/src/client/styles/transition/fade-in-width-expand.scss new file mode 100644 index 0000000000..70c2fdca5e --- /dev/null +++ b/tools/helper/src/client/styles/transition/fade-in-width-expand.scss @@ -0,0 +1,37 @@ +@import './vars.css'; + +.fade-in-width-expand { + &-leave-from, + &-enter-to { + opacity: 1; + } + + &-leave-to, + &-enter-from { + margin-right: 0 !important; + margin-left: 0 !important; + opacity: 0 !important; + } + + &-leave-active { + overflow: hidden; + transition: + max-width var(--transition-duration) var(--transition-ease-in-out) + var(--transition-delay), + opacity var(--transition-duration) var(--transition-ease-in-out), + margin-right var(--transition-duration) var(--transition-ease-in-out) + var(--transition-delay), + margin-left var(--transition-duration) var(--transition-ease-in-out) + var(--transition-delay); + } + + &-enter-active { + overflow: hidden; + transition: + max-width var(--transition-duration) var(--transition-ease-in-out), + opacity var(--transition-duration) var(--transition-ease-in-out) + var(--transition-delay), + margin-right var(--transition-duration) var(--transition-ease-in-out), + margin-left var(--transition-duration) var(--transition-ease-in-out); + } +} diff --git a/tools/helper/src/client/styles/transition/fade-in.scss b/tools/helper/src/client/styles/transition/fade-in.scss new file mode 100644 index 0000000000..58abfebb4b --- /dev/null +++ b/tools/helper/src/client/styles/transition/fade-in.scss @@ -0,0 +1,18 @@ +@import './vars.css'; + +.fade-in { + &-enter-active, + &-leave-active { + transition: all var(--transition-duration) var(--transition-ease-in-out) !important; + } + + &-enter-from, + &-leave-to { + opacity: 0; + } + + &-leave-from, + &-enter-to { + opacity: 1; + } +} diff --git a/tools/helper/src/client/styles/transition/slide-in-down.scss b/tools/helper/src/client/styles/transition/slide-in-down.scss new file mode 100644 index 0000000000..e4e583b300 --- /dev/null +++ b/tools/helper/src/client/styles/transition/slide-in-down.scss @@ -0,0 +1,27 @@ +@import './vars.css'; + +:root { + --transition-slide-in-down-offset: -100%; +} + +.slide-in-down { + &-leave-active { + transition: transform var(--transition-leave-duration) + var(--transition-ease-in); + } + + &-enter-active { + transition: transform var(--transition-enter-duration) + var(--transition-ease-out); + } + + &-enter-to, + &-leave-from { + transform: translateY(0); + } + + &-enter-from, + &-leave-to { + transform: translateY(var(--transition-slide-in-down-offset)); + } +} diff --git a/tools/helper/src/client/styles/transition/slide-in-left.scss b/tools/helper/src/client/styles/transition/slide-in-left.scss new file mode 100644 index 0000000000..6bcf3e8b25 --- /dev/null +++ b/tools/helper/src/client/styles/transition/slide-in-left.scss @@ -0,0 +1,27 @@ +@import './vars.css'; + +:root { + --transition-slide-in-left-offset: 100%; +} + +.slide-in-left { + &-leave-active { + transition: transform var(--transition-leave-duration) + var(--transition-ease-in); + } + + &-enter-active { + transition: transform var(--transition-enter-duration) + var(--transition-ease-out); + } + + &-enter-to, + &-leave-from { + transform: translateX(0); + } + + &-enter-from, + &-leave-to { + transform: translateX(var(--transition-slide-in-left-offset)); + } +} diff --git a/tools/helper/src/client/styles/transition/slide-in-right.scss b/tools/helper/src/client/styles/transition/slide-in-right.scss new file mode 100644 index 0000000000..c0cfc6b474 --- /dev/null +++ b/tools/helper/src/client/styles/transition/slide-in-right.scss @@ -0,0 +1,27 @@ +@import './vars.css'; + +:root { + --transition-slide-in-right-offset: -100%; +} + +.slide-in-right { + &-leave-active { + transition: transform var(--transition-leave-duration) + var(--transition-ease-in); + } + + &-enter-active { + transition: transform var(--transition-enter-duration) + var(--transition-ease-out); + } + + &-enter-to, + &-leave-from { + transform: translateX(0); + } + + &-enter-from, + &-leave-to { + transform: translateX(var(--transition-slide-in-right-offset)); + } +} diff --git a/tools/helper/src/client/styles/transition/slide-in-up.scss b/tools/helper/src/client/styles/transition/slide-in-up.scss new file mode 100644 index 0000000000..b9e90d9124 --- /dev/null +++ b/tools/helper/src/client/styles/transition/slide-in-up.scss @@ -0,0 +1,27 @@ +@import './vars.css'; + +:root { + --transition-slide-in-up-offset: 100%; +} + +.slide-in-up { + &-leave-active { + transition: transform var(--transition-leave-duration) + var(--transition-ease-in); + } + + &-enter-active { + transition: transform var(--transition-enter-duration) + var(--transition-ease-out); + } + + &-enter-to, + &-leave-from { + transform: translateY(0); + } + + &-enter-from, + &-leave-to { + transform: translateY(var(--transition-slide-in-up-offset)); + } +} diff --git a/tools/helper/src/client/styles/transition/vars.scss b/tools/helper/src/client/styles/transition/vars.scss new file mode 100644 index 0000000000..7c9c9bb696 --- /dev/null +++ b/tools/helper/src/client/styles/transition/vars.scss @@ -0,0 +1,9 @@ +:root { + --transition-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --transition-ease-out: cubic-bezier(0, 0, 0.2, 1); + --transition-ease-in: cubic-bezier(0.4, 0, 1, 1); + --transition-duration: 0.3s; + --transition-enter-duration: var(--transition-duration); + --transition-leave-duration: 0.2s; + --transition-delay: 0.1s; +}