更新移动端动画
This commit is contained in:
parent
679f03a904
commit
1e624c7169
@ -187,36 +187,12 @@ const hasActiveNavItem = activeItem || activeGroupId || activeSubItem ? true : f
|
|||||||
aria-label="打开菜单"
|
aria-label="打开菜单"
|
||||||
>
|
>
|
||||||
<span class="sr-only">打开菜单</span>
|
<span class="sr-only">打开菜单</span>
|
||||||
<svg
|
<!-- 替换SVG图标为CSS汉堡菜单 -->
|
||||||
class="h-6 w-6 block"
|
<div class="hamburger-menu">
|
||||||
id="menu-open-icon"
|
<span class="hamburger-line line-1"></span>
|
||||||
fill="none"
|
<span class="hamburger-line line-2"></span>
|
||||||
viewBox="0 0 24 24"
|
<span class="hamburger-line line-3"></span>
|
||||||
stroke="currentColor"
|
</div>
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M4 6h16M4 12h16M4 18h16"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
<svg
|
|
||||||
class="h-6 w-6 hidden"
|
|
||||||
id="menu-close-icon"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M6 18L18 6M6 6l12 12"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -272,16 +248,18 @@ const hasActiveNavItem = activeItem || activeGroupId || activeSubItem ? true : f
|
|||||||
data-mobile-menu-toggle={item.id}
|
data-mobile-menu-toggle={item.id}
|
||||||
>
|
>
|
||||||
<span>{item.text}</span>
|
<span>{item.text}</span>
|
||||||
<svg
|
<div class="mobile-menu-icon w-5 h-5 flex items-center justify-center">
|
||||||
class="mobile-menu-arrow h-5 w-5 text-gray-500 dark:text-gray-400"
|
<svg
|
||||||
fill="none"
|
class="mobile-menu-arrow w-4 h-4 text-gray-500 dark:text-gray-400"
|
||||||
viewBox="0 0 24 24"
|
fill="none"
|
||||||
stroke="currentColor"
|
viewBox="0 0 24 24"
|
||||||
>
|
stroke="currentColor"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
>
|
||||||
</svg>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mobile-submenu pl-4 border-l-2 border-gray-200 dark:border-gray-700 hidden overflow-hidden" data-parent-id={item.id}>
|
<div class="mobile-submenu pl-4 border-l-2 border-gray-200 dark:border-gray-700" data-parent-id={item.id}>
|
||||||
{item.items.map(subItem => (
|
{item.items.map(subItem => (
|
||||||
<a
|
<a
|
||||||
href={subItem.href}
|
href={subItem.href}
|
||||||
@ -325,6 +303,84 @@ const hasActiveNavItem = activeItem || activeGroupId || activeSubItem ? true : f
|
|||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<style is:global>
|
||||||
|
/* 汉堡菜单动画样式 */
|
||||||
|
.hamburger-menu {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburger-line {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
background-color: currentColor;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 菜单打开时的样式 */
|
||||||
|
[aria-expanded="true"] .hamburger-menu .line-1 {
|
||||||
|
transform: translateY(8px) rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[aria-expanded="true"] .hamburger-menu .line-2 {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[aria-expanded="true"] .hamburger-menu .line-3 {
|
||||||
|
transform: translateY(-8px) rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端子菜单展开动画 */
|
||||||
|
.mobile-menu-arrow {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端菜单图标容器 */
|
||||||
|
.mobile-menu-icon {
|
||||||
|
position: relative;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 子菜单展开/收起动画 - 完全重写 */
|
||||||
|
.mobile-submenu {
|
||||||
|
height: auto;
|
||||||
|
max-height: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
transition:
|
||||||
|
max-height 0.3s ease,
|
||||||
|
opacity 0.3s ease,
|
||||||
|
transform 0.3s ease,
|
||||||
|
visibility 0s linear 0.3s, /* 延迟visibility变化 */
|
||||||
|
padding 0.3s ease;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-submenu.menu-visible {
|
||||||
|
max-height: 500px; /* 足够大以容纳所有内容 */
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
transition:
|
||||||
|
max-height 0.3s ease,
|
||||||
|
opacity 0.3s ease,
|
||||||
|
transform 0.3s ease,
|
||||||
|
visibility 0s linear 0s, /* 立即改变visibility */
|
||||||
|
padding 0.3s ease;
|
||||||
|
padding-top: 0.25rem;
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
// 导航逻辑 - 自销毁模式
|
// 导航逻辑 - 自销毁模式
|
||||||
@ -1415,7 +1471,7 @@ const hasActiveNavItem = activeItem || activeGroupId || activeSubItem ? true : f
|
|||||||
if (submenu) {
|
if (submenu) {
|
||||||
const parentId = submenu.getAttribute('data-parent-id');
|
const parentId = submenu.getAttribute('data-parent-id');
|
||||||
const toggle = document.querySelector(`[data-mobile-menu-toggle="${parentId}"]`);
|
const toggle = document.querySelector(`[data-mobile-menu-toggle="${parentId}"]`);
|
||||||
if (toggle && submenu.classList.contains('hidden')) {
|
if (toggle && !submenu.classList.contains('menu-visible')) {
|
||||||
// 展开子菜单
|
// 展开子菜单
|
||||||
toggleSubmenu(parentId, true);
|
toggleSubmenu(parentId, true);
|
||||||
}
|
}
|
||||||
@ -1432,22 +1488,31 @@ const hasActiveNavItem = activeItem || activeGroupId || activeSubItem ? true : f
|
|||||||
function toggleSubmenu(parentId, forceOpen = null) {
|
function toggleSubmenu(parentId, forceOpen = null) {
|
||||||
const toggle = document.querySelector(`[data-mobile-menu-toggle="${parentId}"]`);
|
const toggle = document.querySelector(`[data-mobile-menu-toggle="${parentId}"]`);
|
||||||
const submenu = document.querySelector(`.mobile-submenu[data-parent-id="${parentId}"]`);
|
const submenu = document.querySelector(`.mobile-submenu[data-parent-id="${parentId}"]`);
|
||||||
const arrow = toggle ? toggle.querySelector('.mobile-menu-arrow') : null;
|
const menuIcon = toggle ? toggle.querySelector('.mobile-menu-icon') : null;
|
||||||
|
const arrow = menuIcon ? menuIcon.querySelector('.mobile-menu-arrow') : null;
|
||||||
|
|
||||||
if (!toggle || !submenu) return;
|
if (!toggle || !submenu) return;
|
||||||
|
|
||||||
// 确定是展开还是收起
|
// 确定是展开还是收起
|
||||||
const isHidden = submenu.classList.contains('hidden');
|
const isHidden = !submenu.classList.contains('menu-visible');
|
||||||
const shouldOpen = forceOpen !== null ? forceOpen : isHidden;
|
const shouldOpen = forceOpen !== null ? forceOpen : isHidden;
|
||||||
|
|
||||||
if (shouldOpen) {
|
if (shouldOpen) {
|
||||||
// 展开子菜单
|
// 展开子菜单 - 移除hidden类已不需要,因为我们使用visibility控制
|
||||||
submenu.classList.remove('hidden');
|
|
||||||
if (arrow) arrow.style.transform = 'rotate(180deg)';
|
// 使用requestAnimationFrame确保DOM更新后再添加过渡类
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
submenu.classList.add('menu-visible');
|
||||||
|
|
||||||
|
// 设置图标动画
|
||||||
|
if (menuIcon) menuIcon.style.transform = 'rotate(180deg)';
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// 收起子菜单
|
// 收起子菜单 - 移除可见类触发过渡效果
|
||||||
submenu.classList.add('hidden');
|
submenu.classList.remove('menu-visible');
|
||||||
if (arrow) arrow.style.transform = '';
|
|
||||||
|
// 重置图标动画
|
||||||
|
if (menuIcon) menuIcon.style.transform = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1457,7 +1522,13 @@ const hasActiveNavItem = activeItem || activeGroupId || activeSubItem ? true : f
|
|||||||
toggles.forEach(toggle => {
|
toggles.forEach(toggle => {
|
||||||
const parentId = toggle.getAttribute('data-mobile-menu-toggle');
|
const parentId = toggle.getAttribute('data-mobile-menu-toggle');
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
toggleSubmenu(parentId, false);
|
const submenu = document.querySelector(`.mobile-submenu[data-parent-id="${parentId}"]`);
|
||||||
|
const menuIcon = toggle ? toggle.querySelector('.mobile-menu-icon') : null;
|
||||||
|
|
||||||
|
if (submenu) {
|
||||||
|
submenu.classList.remove('menu-visible');
|
||||||
|
if (menuIcon) menuIcon.style.transform = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1486,12 +1557,6 @@ const hasActiveNavItem = activeItem || activeGroupId || activeSubItem ? true : f
|
|||||||
|
|
||||||
if (mobileMenuButton) {
|
if (mobileMenuButton) {
|
||||||
mobileMenuButton.setAttribute('aria-expanded', 'false');
|
mobileMenuButton.setAttribute('aria-expanded', 'false');
|
||||||
|
|
||||||
// 重置菜单图标
|
|
||||||
if (menuOpenIcon && menuCloseIcon) {
|
|
||||||
menuOpenIcon.classList.remove('hidden');
|
|
||||||
menuCloseIcon.classList.add('hidden');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同时关闭所有子菜单
|
// 同时关闭所有子菜单
|
||||||
@ -1519,12 +1584,6 @@ const hasActiveNavItem = activeItem || activeGroupId || activeSubItem ? true : f
|
|||||||
// 更新按钮状态
|
// 更新按钮状态
|
||||||
mobileMenuButton.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');
|
mobileMenuButton.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');
|
||||||
|
|
||||||
// 切换图标
|
|
||||||
if (menuOpenIcon && menuCloseIcon) {
|
|
||||||
menuOpenIcon.classList.toggle('hidden');
|
|
||||||
menuCloseIcon.classList.toggle('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示/隐藏菜单
|
// 显示/隐藏菜单
|
||||||
mobileMenu.classList.toggle('hidden');
|
mobileMenu.classList.toggle('hidden');
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user