Улучшение коÐда мобильного меню
This commit is contained in:
parent
4883a52b88
commit
d5286ab980
9 changed files with 800 additions and 580 deletions
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
export interface Props {
|
||||
links?: Array<{ name: string; url: string }>;
|
||||
class?: string;
|
||||
links?: Array<{ name: string; url: string }>;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const defaultLinks = [
|
||||
{ name: 'Главная', url: '/' },
|
||||
{ name: 'Услуги', url: '/services' },
|
||||
{ name: 'Кейсы', url: '/cases' },
|
||||
{ name: 'Блог', url: '/blog' },
|
||||
|
|
@ -12,53 +13,229 @@ const defaultLinks = [
|
|||
{ name: 'Контакты', url: '/contacts' },
|
||||
];
|
||||
|
||||
const {
|
||||
links = defaultLinks,
|
||||
class: className = '',
|
||||
const {
|
||||
links = defaultLinks,
|
||||
class: className = '',
|
||||
}: Props = Astro.props;
|
||||
|
||||
// Определяем текущую страницу
|
||||
const currentPath = Astro.url.pathname;
|
||||
const isHomePage = currentPath === '/' || currentPath === '';
|
||||
|
||||
// Фильтруем ссылки: убираем "Главная" если мы на главной странице
|
||||
const filteredLinks = isHomePage
|
||||
? links.filter(link => link.url !== '/')
|
||||
: links;
|
||||
---
|
||||
|
||||
<nav class={className}>
|
||||
<ul class="nav-list">
|
||||
{links.map((link) => (
|
||||
<li>
|
||||
<a href={link.url} class="nav-link">{link.name}</a>
|
||||
</li>
|
||||
))}
|
||||
{filteredLinks.map((link, index) => {
|
||||
const isActive = currentPath === link.url ||
|
||||
(link.url !== '/' && currentPath.startsWith(link.url));
|
||||
return (
|
||||
<li class="nav-item" style={{ '--item-index': index } as any}>
|
||||
<a
|
||||
href={link.url}
|
||||
class={`nav-link ${isActive ? 'active' : ''}`}
|
||||
data-text={link.name}
|
||||
>
|
||||
<span class="nav-link-text">{link.name}</span>
|
||||
<span class="nav-indicator"></span>
|
||||
</a>
|
||||
</li>
|
||||
)})}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.nav-list {
|
||||
display: flex;
|
||||
gap: 2.5rem;
|
||||
gap: 0.5rem;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
position: relative;
|
||||
perspective: 500px;
|
||||
/* Убираем анимацию появления */
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: #535e6c;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.6rem 1.2rem;
|
||||
/* Цвет текста как в логотипе (logo-name) */
|
||||
color: #1e3050;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
transition: color 0.2s ease, background-position 0.3s ease;
|
||||
background-image: linear-gradient(to right, #1e3050 0%, #1e3050 100%);
|
||||
background-size: 0% 2px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left bottom;
|
||||
padding-bottom: 2px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
letter-spacing: 0.3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Текст с анимацией */
|
||||
.nav-link-text {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
transition: transform 0.2s cubic-bezier(0.34, 1.2, 0.64, 1);
|
||||
}
|
||||
|
||||
/* Индикатор под пунктом */
|
||||
.nav-indicator {
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, #eac26e, #d4af37, #eac26e);
|
||||
border-radius: 3px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform: translateX(-50%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Hover эффекты */
|
||||
.nav-link:hover {
|
||||
color: #1e3050;
|
||||
background-size: 100% 2px;
|
||||
/* При наведении цвет становится насыщеннее */
|
||||
color: #0a1a2e;
|
||||
background: rgba(30, 48, 80, 0.05);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Адаптив: скрываем меню на мобильных */
|
||||
.nav-link:hover .nav-link-text {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.nav-item:hover .nav-indicator {
|
||||
width: 70%;
|
||||
opacity: 1;
|
||||
animation: indicatorPulse 0.6s ease-out;
|
||||
}
|
||||
|
||||
/* Активное состояние - улучшенная видимость */
|
||||
.nav-link.active {
|
||||
/* Используем темно-синий цвет вместо золотого для лучшей читаемости */
|
||||
color: #1e3050;
|
||||
background: linear-gradient(135deg, rgba(234, 194, 110, 0.25), rgba(234, 194, 110, 0.12));
|
||||
position: relative;
|
||||
font-weight: 700;
|
||||
/* Добавляем небольшую тень для выделения */
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.nav-link.active .nav-indicator {
|
||||
width: 80%;
|
||||
opacity: 1;
|
||||
background: linear-gradient(90deg, #eac26e, #f0d68a, #eac26e);
|
||||
height: 3px;
|
||||
bottom: -4px;
|
||||
}
|
||||
|
||||
/* Дополнительный эффект для активного пункта - полужирный */
|
||||
.nav-link.active .nav-link-text {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Ripple эффект при клике */
|
||||
.nav-link::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(234, 194, 110, 0.3), transparent);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.6s, height 0.6s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-link:active::before {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Анимация индикатора */
|
||||
@keyframes indicatorPulse {
|
||||
0% {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
width: 85%;
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
width: 70%;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Дополнительный эффект свечения при наведении */
|
||||
.nav-link::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(234, 194, 110, 0.1), transparent);
|
||||
transition: left 0.5s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-link:hover::after {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
/* Адаптив */
|
||||
@media (max-width: 992px) {
|
||||
.nav-list {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Добавляем дополнительные эффекты для активной ссылки
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Находим все активные ссылки
|
||||
const activeLinks = document.querySelectorAll('.nav-link.active');
|
||||
|
||||
activeLinks.forEach(link => {
|
||||
// Добавляем дополнительный класс для стилизации
|
||||
link.classList.add('active-animated');
|
||||
|
||||
// Создаем эффект пульсации для активного индикатора
|
||||
const indicator = link.querySelector('.nav-indicator');
|
||||
if (indicator) {
|
||||
(indicator as HTMLElement).style.animation = 'indicatorPulse 1s ease-out infinite';
|
||||
}
|
||||
});
|
||||
|
||||
// Добавляем поддержку touch-устройств
|
||||
const navLinks = document.querySelectorAll('.nav-link');
|
||||
|
||||
navLinks.forEach(link => {
|
||||
link.addEventListener('touchstart', function(this: HTMLElement) {
|
||||
// Убираем эффект наведения на touch-устройствах
|
||||
this.classList.add('touch-active');
|
||||
|
||||
// Убираем класс через таймаут
|
||||
setTimeout(() => {
|
||||
this.classList.remove('touch-active');
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue