241 lines
No EOL
7.2 KiB
Text
241 lines
No EOL
7.2 KiB
Text
---
|
||
export interface Props {
|
||
links?: Array<{ name: string; url: string }>;
|
||
class?: string;
|
||
}
|
||
|
||
const defaultLinks = [
|
||
{ name: 'Главная', url: '/' },
|
||
{ name: 'Услуги', url: '/services' },
|
||
{ name: 'Кейсы', url: '/cases' },
|
||
{ name: 'Блог', url: '/blog' },
|
||
{ name: 'Отзывы', url: '/reviews' },
|
||
{ name: 'Контакты', url: '/contacts' },
|
||
];
|
||
|
||
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">
|
||
{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: 0.5rem;
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
position: relative;
|
||
}
|
||
|
||
.nav-item {
|
||
position: relative;
|
||
perspective: 500px;
|
||
/* Убираем анимацию появления */
|
||
}
|
||
|
||
.nav-link {
|
||
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: 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: #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> |