Бегущая строка с плавной анимацией — текст непрерывно движется слева направо. Поддерживает паузу при наведении, настраиваемую скорость и несколько строк с разным направлением. Чистый JS без библиотек.
(function(){
const root = document.createElement('div');
document.body.appendChild(root);
const container = root;
try {
const style = document.createElement('style');
style.textContent = `
.marquee-wrap {
background: #050505;
padding: 30px 0;
font-family: monospace;
overflow: hidden;
display: flex;
flex-direction: column;
gap: 16px;
}
.marquee-row {
display: flex;
overflow: hidden;
position: relative;
}
.marquee-row::before,
.marquee-row::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 60px;
z-index: 2;
pointer-events: none;
}
.marquee-row::before {
left: 0;
background: linear-gradient(to right, #050505, transparent);
}
.marquee-row::after {
right: 0;
background: linear-gradient(to left, #050505, transparent);
}
.marquee-track {
display: flex;
gap: 0;
will-change: transform;
}
.marquee-track.paused {
animation-play-state: paused !important;
}
.marquee-item {
display: flex;
align-items: center;
gap: 12px;
padding: 0 16px;
white-space: nowrap;
font-size: 15px;
font-weight: bold;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.marquee-dot {
width: 5px;
height: 5px;
border-radius: 50%;
flex-shrink: 0;
}
.marquee-row-1 .marquee-item { color: #00ff9c; }
.marquee-row-1 .marquee-dot { background: #00ff9c; }
.marquee-row-2 .marquee-item { color: #a78bfa; }
.marquee-row-2 .marquee-dot { background: #a78bfa; }
.marquee-row-3 .marquee-item {
color: transparent;
-webkit-text-stroke: 1px rgba(255,255,255,0.25);
}
.marquee-row-3 .marquee-dot { background: rgba(255,255,255,0.2); }
@keyframes marquee-left {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@keyframes marquee-right {
from { transform: translateX(-50%); }
to { transform: translateX(0); }
}
`;
document.head.appendChild(style);
const rows = [
{
cls: 'marquee-row-1',
direction: 'left',
speed: 28,
items: ['JavaScript', 'HTML', 'CSS', 'PHP', 'React', 'Node.js', 'Canvas', 'WebGL'],
},
{
cls: 'marquee-row-2',
direction: 'right',
speed: 22,
items: ['Snippets', 'Components', 'Utilities', 'Animations', 'Effects', 'Micro Apps'],
},
{
cls: 'marquee-row-3',
direction: 'left',
speed: 35,
items: ['Copy', 'Paste', 'Build', 'Ship', 'Create', 'Deploy', 'Code', 'Design'],
},
];
const wrap = document.createElement('div');
wrap.className = 'marquee-wrap';
rows.forEach(row => {
const rowEl = document.createElement('div');
rowEl.className = `marquee-row ${row.cls}`;
const track = document.createElement('div');
track.className = 'marquee-track';
// Дублируем items для бесшовного повтора
const allItems = [...row.items, ...row.items];
allItems.forEach(text => {
const item = document.createElement('div');
item.className = 'marquee-item';
item.innerHTML = `<span class="marquee-dot"></span>${text}`;
track.appendChild(item);
});
// Анимация через JS для точного контроля
let offset = row.direction === 'right' ? -50 : 0;
const trackWidth = row.items.length * 160;
let paused = false;
let animId;
function animate() {
if (!paused) {
if (row.direction === 'left') {
offset -= row.speed / 60 / row.items.length;
if (offset <= -50) offset = 0;
} else {
offset += row.speed / 60 / row.items.length;
if (offset >= 0) offset = -50;
}
track.style.transform = `translateX(${offset}%)`;
}
animId = requestAnimationFrame(animate);
}
animate();
// Пауза при наведении
rowEl.addEventListener('mouseenter', () => { paused = true; });
rowEl.addEventListener('mouseleave', () => { paused = false; });
rowEl.appendChild(track);
wrap.appendChild(rowEl);
});
container.appendChild(wrap);
} catch(e){
console.error(e);
}
})();
👁 9