You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在纯HTML/JS环境下实现Tailwind UI组件的动画效果

纯HTML/JS实现TailwindUI模态框动画解决方案

原有代码问题梳理

  • 原生CSS语法错误:@keyframes中错误使用Tailwind类名格式opacity-0,正确属性写法为opacity: 0
  • 退出动画配置错误:modalExit类错误关联了入场动画modal-in
  • 动画逻辑错误:直接切换hidden类会让元素立刻消失,无法播放离场动画
  • 参数不匹配:动画时长、缓动函数没有匹配TailwindUI注释要求的规则

完整可运行代码

样式部分

<style>
/* 背景遮罩动画:完全匹配TailwindUI注释规则 */
.modal-overlay-enter {
  animation: overlay-in 300ms ease-out forwards;
}
.modal-overlay-leave {
  animation: overlay-out 200ms ease-in forwards;
}
@keyframes overlay-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes overlay-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

/* 模态面板动画:完全匹配TailwindUI注释规则 */
.modal-panel-enter {
  animation: panel-in 300ms ease-out forwards;
}
.modal-panel-leave {
  animation: panel-out 200ms ease-in forwards;
}
/* 移动端动画 */
@keyframes panel-in {
  from {
    opacity: 0;
    transform: translateY(1rem) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}
@keyframes panel-out {
  from {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  to {
    opacity: 0;
    transform: translateY(1rem) scale(0.95);
  }
}
/* 桌面端适配 */
@media (min-width: 640px) {
  @keyframes panel-in {
    from {
      opacity: 0;
      transform: translateY(0) scale(0.95);
    }
    to {
      opacity: 1;
      transform: translateY(0) scale(1);
    }
  }
  @keyframes panel-out {
    from {
      opacity: 1;
      transform: translateY(0) scale(1);
    }
    to {
      opacity: 0;
      transform: translateY(0) scale(0.95);
    }
  }
}
</style>

HTML结构部分

<button class="border border-red-500" onclick="toggleModal()">Toggle modal</button>

<div id="myModal" class="hidden fixed z-10 inset-0 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
  <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
    <!-- 背景遮罩层,添加id用于JS控制 -->
    <div id="modalOverlay" class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>

    <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>

    <!-- 模态面板,添加id用于JS控制 -->
    <div id="modalPanel" class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
      <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
        <div class="sm:flex sm:items-start">
          <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
            <svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
            </svg>
          </div>
          <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
            <h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">Deactivate account</h3>
            <div class="mt-2">
              <p class="text-sm text-gray-500">Are you sure you want to deactivate your account? All of your data will be permanently removed. This action cannot be undone.</p>
            </div>
          </div>
        </div>
      </div>
      <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
        <button onclick="toggleModal()" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">Deactivate</button>
        <button onclick="toggleModal()" type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">Cancel</button>
      </div>
    </div>
  </div>
</div>

JS逻辑部分

<script>
const modal = document.getElementById('myModal');
const modalOverlay = document.getElementById('modalOverlay');
const modalPanel = document.getElementById('modalPanel');

function toggleModal() {
  const isHidden = modal.classList.contains('hidden');
  if (isHidden) {
    // 显示模态框:先移除hidden类,再播放入场动画
    modal.classList.remove('hidden');
    modalOverlay.classList.add('modal-overlay-enter');
    modalPanel.classList.add('modal-panel-enter');
    // 入场动画时长300ms,结束后清理动画类
    setTimeout(() => {
      modalOverlay.classList.remove('modal-overlay-enter');
      modalPanel.classList.remove('modal-panel-enter');
    }, 300);
  } else {
    // 隐藏模态框:先播放离场动画,结束后再添加hidden类
    modalOverlay.classList.add('modal-overlay-leave');
    modalPanel.classList.add('modal-panel-leave');
    // 离场动画时长200ms,结束后隐藏元素并清理动画类
    setTimeout(() => {
      modal.classList.add('hidden');
      modalOverlay.classList.remove('modal-overlay-leave');
      modalPanel.classList.remove('modal-panel-leave');
    }, 200);
  }
}
</script>

实现说明

  • 无任何第三方框架/库依赖,完全使用原生HTML/CSS/JS实现
  • 所有动画参数100%匹配TailwindUI注释中的规则
  • 动画切换逻辑保证入场、离场动画都能完整播放,不会出现跳变

内容的提问来源于stack exchange,提问作者Rishav Sharan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 15:24:05