Skip to content

CSS 过渡动画

使用 hookTransition 实现基于 CSS 的进入/退出动画,类似 vue 的<transition><transition-group>

基础用法

typescript
import { hookTransition } from 'mve-dom-helper'

function TransitionDemo() {
  const visible = createSignal(false)

  const transition = hookTransition(visible.get, (callback, show) => {
    setTimeout(callback, 300) // 动画时长
  })

  fdom.div({
    children() {
      fdom.button({
        onClick: () => visible.set(!visible.get()),
        children: () => (visible.get() ? '隐藏' : '显示'),
      })

      renderIf(
        () => transition.didShow(),
        () => {
          fdom.div({
            className: () => `modal ${transition.className('fade')}`,
            children: '模态框内容',
          })
        }
      )
    },
  })
}

CSS 样式

css
/* 淡入淡出 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

/* 滑动 */
.slide-up-enter-active,
.slide-up-leave-active {
  transition: transform 0.3s ease;
}
.slide-up-enter-from,
.slide-up-leave-to {
  transform: translateY(100%);
}

/* 缩放 */
.fade-zoom-enter-active,
.fade-zoom-leave-active {
  transition: all 0.3s ease;
}
.fade-zoom-enter-from,
.fade-zoom-leave-to {
  opacity: 0;
  transform: scale(0.8);
}

弹窗组件示例

typescript
function Popup({ visible, placement = 'center', onClose, children }) {
  const transition = hookTransition(visible, (callback, show) => {
    setTimeout(() => {
      callback()
      if (show) console.log('弹窗打开')
      else console.log('弹窗关闭')
    }, 300)
  })

  renderIf(
    () => transition.didShow(),
    () => {
      renderPortal(document.body, () => {
        fdom.div({
          className: () => `popup-overlay ${transition.className('fade')}`,
          onClick: onClose,
          children() {
            fdom.div({
              className: () => {
                const animClass =
                  placement === 'center'
                    ? transition.className('fade-zoom')
                    : transition.className(`slide-${placement}`)
                return `popup popup--${placement} ${animClass}`
              },
              onClick: (e) => e.stopPropagation(),
              children,
            })
          },
        })
      })
    }
  )
}

API

typescript
hookTransition(
  show: GetValue<boolean>,           // 显示状态
  beginChange: (callback, show) => void, // 动画回调
  set?: Set<Element>                 // 强制回流元素
)

// 返回值
{
  className(prefix: string): string, // 获取CSS类名
  didShow(): boolean | "active",     // 是否渲染
  set: Set<Element>                  // 回流元素集合
}

类名规则

  • 进入: ${prefix}-enter-active ${prefix}-enter-from${prefix}-enter-active ${prefix}-enter-to
  • 离开: ${prefix}-leave-active ${prefix}-leave-from${prefix}-leave-active ${prefix}-leave-to

最佳实践

  • CSS 和 JS 动画时长保持一致
  • 使用 renderIf(() => transition.didShow()) 条件渲染
  • 组件销毁时清理定时器

基于 MVE 框架构建