feat: оживление сайта — обложки, hero-фон, статистика, анимации
- ArticleCard: реальные обложки с fallback на детерминированный градиент по id статьи - HeroBackground: 3 анимированных blob'а + dot-grid + плавный fade к контенту - Stats компонент: 4 карточки — статьи / минуты чтения / токены / просмотры - Reveal компонент: IntersectionObserver-based fade-in при скролле, respect prefers-reduced-motion - next.config: rewrites /uploads/* → engine, чтобы картинки работали с относительными путями - На странице статьи — обложка над контентом
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
'use client';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
/**
|
||||
* Lightweight reveal-on-scroll. Без зависимостей, нативный IntersectionObserver.
|
||||
* Дочерние элементы с классом `.reveal` плавно появятся.
|
||||
*/
|
||||
export default function Reveal({ children, className = '' }) {
|
||||
const ref = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
const io = new IntersectionObserver(
|
||||
entries => {
|
||||
entries.forEach(e => {
|
||||
if (e.isIntersecting) {
|
||||
e.target.classList.add('is-visible');
|
||||
io.unobserve(e.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1, rootMargin: '0px 0px -50px 0px' }
|
||||
);
|
||||
ref.current.querySelectorAll('.reveal').forEach(el => io.observe(el));
|
||||
return () => io.disconnect();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={className}>
|
||||
<style jsx global>{`
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: opacity 0.7s ease-out, transform 0.7s ease-out;
|
||||
}
|
||||
.reveal.is-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.reveal-1 { transition-delay: 0.05s; }
|
||||
.reveal-2 { transition-delay: 0.15s; }
|
||||
.reveal-3 { transition-delay: 0.25s; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.reveal { opacity: 1; transform: none; transition: none; }
|
||||
}
|
||||
`}</style>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user