Виртуальный скролл — рендерит только видимые элементы списка независимо от его размера. 10 000 элементов в DOM не тормозят — в реальности отрисовывается только ~20. Незаменим для больших таблиц, лент и списков.
Запустить
Смотреть код
👁 8
let root = container;
const style = document.createElement('style');
style.textContent = '.vs-wrap{font-family:monospace;background:#0d1117;border-radius:12px;overflow:hidden;border:1px solid #30363d}'
+ '.vs-header{padding:12px 16px;border-bottom:1px solid #30363d;display:flex;justify-content:space-between;align-items:center;background:#161b22}'
+ '.vs-title{color:#00ff9c;font-size:13px;font-weight:bold}'
+ '.vs-counter{font-size:11px;color:#8b949e}'
+ '.vs-viewport{height:320px;overflow-y:auto;position:relative;scrollbar-width:thin;scrollbar-color:#30363d #0d1117}'
+ '.vs-viewport::-webkit-scrollbar{width:6px}'
+ '.vs-viewport::-webkit-scrollbar-track{background:#0d1117}'
+ '.vs-viewport::-webkit-scrollbar-thumb{background:#30363d;border-radius:3px}'
+ '.vs-spacer{position:absolute;top:0;left:0;width:1px;pointer-events:none}'
+ '.vs-content{position:absolute;left:0;right:0}'
+ '.vs-item{display:flex;align-items:center;gap:12px;padding:0 16px;border-bottom:1px solid #21262d;cursor:pointer;transition:background 0.1s}'
+ '.vs-item:hover{background:#161b22}'
+ '.vs-item-index{font-size:11px;color:#484f58;width:40px;flex-shrink:0;text-align:right}'
+ '.vs-item-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}'
+ '.vs-item-text{flex:1;font-size:13px;color:#e6edf3}'
+ '.vs-item-badge{font-size:10px;padding:2px 8px;border-radius:20px;flex-shrink:0}'
+ '.vs-item-views{font-size:11px;color:#484f58}'
+ '.vs-footer{padding:10px 16px;border-top:1px solid #30363d;font-size:11px;color:#484f58;background:#161b22;display:flex;justify-content:space-between}';
document.head.appendChild(style);
var TOTAL = 10000;
var ITEM_H = 44;
var BUFFER = 3;
var categories = ['JS', 'HTML', 'CSS', 'PHP', 'App'];
var colors = ['#00ff9c', '#a78bfa', '#22d3ee', '#fb923c', '#f472b6'];
var names = [
'debounce', 'throttle', 'smoothScroll', 'copyText', 'formatDate',
'randomColor', 'shuffleArray', 'typewriter', 'rippleEffect', 'glitchText',
'Card Component', 'Nav Bar', 'Modal', 'Toast', 'Skeleton',
'Progress Bar', 'Avatar Group', 'Pricing Card', 'Stats Card', 'Profile Card'
];
var data = [];
for (var d = 0; d < TOTAL; d++) {
data.push({
id: d + 1,
name: names[d % names.length] + (d >= names.length ? ' v' + (Math.floor(d / names.length) + 1) : ''),
category: categories[d % categories.length],
color: colors[d % colors.length],
views: Math.floor(Math.random() * 9999)
});
}
var wrap = document.createElement('div');
wrap.className = 'vs-wrap';
var header = document.createElement('div');
header.className = 'vs-header';
var vsTitle = document.createElement('span');
vsTitle.className = 'vs-title';
vsTitle.textContent = '// Virtual Scroll';
var vsCounter = document.createElement('span');
vsCounter.className = 'vs-counter';
vsCounter.textContent = TOTAL.toLocaleString() + ' elements';
header.appendChild(vsTitle);
header.appendChild(vsCounter);
var viewport = document.createElement('div');
viewport.className = 'vs-viewport';
var spacer = document.createElement('div');
spacer.className = 'vs-spacer';
spacer.style.height = (TOTAL * ITEM_H) + 'px';
var content = document.createElement('div');
content.className = 'vs-content';
viewport.appendChild(spacer);
viewport.appendChild(content);
var footer = document.createElement('div');
footer.className = 'vs-footer';
var rangeEl = document.createElement('span');
rangeEl.textContent = 'Elements 1-20';
var domEl = document.createElement('span');
domEl.textContent = 'In DOM: 0 of ' + TOTAL.toLocaleString();
footer.appendChild(rangeEl);
footer.appendChild(domEl);
wrap.appendChild(header);
wrap.appendChild(viewport);
wrap.appendChild(footer);
container.appendChild(wrap);
function render() {
var scrollTop = viewport.scrollTop;
var viewH = viewport.clientHeight;
var startIdx = Math.max(0, Math.floor(scrollTop / ITEM_H) - BUFFER);
var endIdx = Math.min(TOTAL - 1, Math.ceil((scrollTop + viewH) / ITEM_H) + BUFFER);
content.style.top = (startIdx * ITEM_H) + 'px';
content.innerHTML = '';
for (var i = startIdx; i <= endIdx; i++) {
var item = data[i];
var el = document.createElement('div');
el.className = 'vs-item';
el.style.height = ITEM_H + 'px';
var idxEl = document.createElement('span');
idxEl.className = 'vs-item-index';
idxEl.textContent = '#' + item.id;
var dot = document.createElement('span');
dot.className = 'vs-item-dot';
dot.style.background = item.color;
var text = document.createElement('span');
text.className = 'vs-item-text';
text.textContent = item.name;
var badge = document.createElement('span');
badge.className = 'vs-item-badge';
badge.style.background = item.color + '22';
badge.style.color = item.color;
badge.textContent = item.category;
var views = document.createElement('span');
views.className = 'vs-item-views';
views.textContent = 'views: ' + item.views;
el.appendChild(idxEl);
el.appendChild(dot);
el.appendChild(text);
el.appendChild(badge);
el.appendChild(views);
content.appendChild(el);
}
rangeEl.textContent = 'Elements ' + (startIdx + 1) + '-' + Math.min(endIdx + 1, TOTAL);
domEl.textContent = 'In DOM: ' + (endIdx - startIdx + 1) + ' of ' + TOTAL.toLocaleString();
}
viewport.addEventListener('scroll', render);
render();
Анимированный шумовой фон на Canvas — органический живой шум который плавно меняется. Подходит для hero-секций и фонов. Настраиваемые цвета и скорость. Чистый JS без библиотек.
Запустить
Смотреть код
👁 6
let root = container;
const canvas = document.createElement('canvas');
canvas.style.cssText = 'width:100%;height:280px;border-radius:10px;display:block;';
container.appendChild(canvas);
const ctx = canvas.getContext('2d');
function resize() {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
resize();
window.addEventListener('resize', resize);
// Простой псевдо-шум без библиотек
function noise(x, y, t) {
const n = Math.sin(x * 0.08 + t) * Math.cos(y * 0.06 + t * 0.7)
+ Math.sin((x + y) * 0.05 + t * 1.3)
+ Math.cos(x * 0.03 - y * 0.07 + t * 0.5);
return (n + 3) / 6; // нормализуем 0..1
}
// Цветовые темы
const themes = [
{ name: 'Green', a: [0,255,156], b: [0,80,40] },
{ name: 'Purple', a: [167,139,250],b: [40,20,80] },
{ name: 'Cyan', a: [34,211,238], b: [0,50,70] },
{ name: 'Fire', a: [255,100,0], b: [80,10,0] },
];
let themeIdx = 0;
// UI — кнопки тем
const ui = document.createElement('div');
ui.style.cssText = 'display:flex;gap:8px;margin-bottom:10px;font-family:monospace;flex-wrap:wrap;';
themes.forEach((th, i) => {
const btn = document.createElement('button');
btn.textContent = th.name;
btn.style.cssText = `
padding:6px 14px;
border-radius:20px;
border:1px solid rgba(255,255,255,0.2);
background:${i===0?'rgba(255,255,255,0.15)':'transparent'};
color:rgba(255,255,255,0.7);
font-family:monospace;
font-size:12px;
cursor:pointer;
transition:0.2s;
`;
btn.addEventListener('click', () => {
themeIdx = i;
ui.querySelectorAll('button').forEach((b,j) => {
b.style.background = j===i ? 'rgba(255,255,255,0.15)' : 'transparent';
b.style.color = j===i ? '#fff' : 'rgba(255,255,255,0.7)';
});
});
ui.appendChild(btn);
});
container.insertBefore(ui, canvas);
// Рисуем шум через пиксели
let t = 0;
const SCALE = 4; // размер пикселя — больше = грубее/быстрее
function draw() {
const W = canvas.width;
const H = canvas.height;
if (!W || !H) { requestAnimationFrame(draw); return; }
const cols = Math.ceil(W / SCALE);
const rows = Math.ceil(H / SCALE);
const theme = themes[themeIdx];
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const n = noise(col, row, t);
const r = Math.round(theme.b[0] + (theme.a[0] - theme.b[0]) * n);
const g = Math.round(theme.b[1] + (theme.a[1] - theme.b[1]) * n);
const b = Math.round(theme.b[2] + (theme.a[2] - theme.b[2]) * n);
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.fillRect(col * SCALE, row * SCALE, SCALE, SCALE);
}
}
// Виньетка
const vignette = ctx.createRadialGradient(W/2, H/2, H*0.2, W/2, H/2, H*0.9);
vignette.addColorStop(0, 'rgba(0,0,0,0)');
vignette.addColorStop(1, 'rgba(0,0,0,0.65)');
ctx.fillStyle = vignette;
ctx.fillRect(0, 0, W, H);
t += 0.018;
requestAnimationFrame(draw);
}
draw();
Переключатель тёмной и светлой темы с плавной анимацией. Автоматически определяет системную тему при первом посещении. Чистый JS без библиотек.
Запустить
Смотреть код
👁 8
let root = container;
const host = document.createElement('div');
container.appendChild(host);
const shadow = host.attachShadow({ mode: 'open' });
const style = document.createElement('style');
style.textContent = `
:host { display: block; font-family: monospace; }
.demo {
padding: 24px;
border-radius: 14px;
transition: background 0.4s, color 0.4s, border-color 0.4s;
}
.demo[data-theme="dark"] {
background: #0d1117;
color: #e6edf3;
border: 1px solid #30363d;
}
.demo[data-theme="light"] {
background: #ffffff;
color: #1f2328;
border: 1px solid #d0d7de;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.logo { font-size: 15px; font-weight: bold; letter-spacing: 0.05em; transition: color 0.4s; }
.demo[data-theme="dark"] .logo { color: #58a6ff; }
.demo[data-theme="light"] .logo { color: #0969da; }
.toggle {
position: relative;
width: 56px;
height: 28px;
border-radius: 50px;
cursor: pointer;
border: none;
transition: background 0.4s;
flex-shrink: 0;
}
.demo[data-theme="dark"] .toggle { background: #388bfd44; }
.demo[data-theme="light"] .toggle { background: #f0c000; }
.thumb {
position: absolute;
top: 3px;
width: 22px;
height: 22px;
border-radius: 50%;
transition: transform 0.4s cubic-bezier(0.23,1,0.32,1), background 0.4s;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: bold;
}
.demo[data-theme="dark"] .thumb { transform: translateX(3px); background: #388bfd; color: #fff; }
.demo[data-theme="light"] .thumb { transform: translateX(31px); background: #fff; color: #c8a000; }
.cards {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-bottom: 16px;
}
.card {
padding: 14px;
border-radius: 10px;
transition: background 0.4s, border-color 0.4s;
}
.demo[data-theme="dark"] .card { background: #161b22; border: 1px solid #30363d; }
.demo[data-theme="light"] .card { background: #f6f8fa; border: 1px solid #d0d7de; }
.card-title { font-size: 12px; font-weight: bold; margin-bottom: 4px; transition: color 0.4s; }
.demo[data-theme="dark"] .card-title { color: #f0f6fc; }
.demo[data-theme="light"] .card-title { color: #1f2328; }
.card-sub { font-size: 11px; transition: color 0.4s; }
.demo[data-theme="dark"] .card-sub { color: #8b949e; }
.demo[data-theme="light"] .card-sub { color: #656d76; }
.badge {
display: inline-block;
margin-top: 8px;
padding: 2px 8px;
border-radius: 20px;
font-size: 10px;
font-weight: bold;
transition: background 0.4s, color 0.4s;
}
.demo[data-theme="dark"] .badge { background: #388bfd22; color: #58a6ff; }
.demo[data-theme="light"] .badge { background: #0969da15; color: #0969da; }
.code {
padding: 12px;
border-radius: 8px;
font-size: 12px;
line-height: 1.6;
transition: background 0.4s, color 0.4s, border-color 0.4s;
}
.demo[data-theme="dark"] .code { background: #010409; color: #79c0ff; border: 1px solid #30363d; }
.demo[data-theme="light"] .code { background: #f6f8fa; color: #0550ae; border: 1px solid #d0d7de; }
.label {
text-align: center;
font-size: 11px;
margin-top: 14px;
letter-spacing: 0.08em;
text-transform: uppercase;
transition: color 0.4s;
}
.demo[data-theme="dark"] .label { color: #8b949e; }
.demo[data-theme="light"] .label { color: #656d76; }
`;
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
let isDark = prefersDark;
const demo = document.createElement('div');
demo.className = 'demo';
demo.setAttribute('data-theme', isDark ? 'dark' : 'light');
const header = document.createElement('div');
header.className = 'header';
const logo = document.createElement('div');
logo.className = 'logo';
logo.textContent = '> SnippetHub_';
const toggle = document.createElement('button');
toggle.className = 'toggle';
const thumb = document.createElement('div');
thumb.className = 'thumb';
thumb.textContent = isDark ? 'D' : 'L';
toggle.appendChild(thumb);
header.appendChild(logo);
header.appendChild(toggle);
const cards = document.createElement('div');
cards.className = 'cards';
cards.innerHTML = `
<div class="card">
<div class="card-title">Typewriter Effect</div>
<div class="card-sub">Эффект печатающей машинки</div>
<span class="badge">JS</span>
</div>
<div class="card">
<div class="card-title">Card Component</div>
<div class="card-sub">Универсальная карточка</div>
<span class="badge">HTML+CSS</span>
</div>
`;
const code = document.createElement('div');
code.className = 'code';
code.innerHTML = 'const theme = localStorage.getItem(\'theme\');<br>root.setAttribute(\'data-theme\', theme);';
const label = document.createElement('div');
label.className = 'label';
label.textContent = isDark ? 'Dark Mode' : 'Light Mode';
demo.appendChild(header);
demo.appendChild(cards);
demo.appendChild(code);
demo.appendChild(label);
shadow.appendChild(style);
shadow.appendChild(demo);
toggle.addEventListener('click', () => {
isDark = !isDark;
demo.setAttribute('data-theme', isDark ? 'dark' : 'light');
thumb.textContent = isDark ? 'D' : 'L';
label.textContent = isDark ? 'Dark Mode' : 'Light Mode';
});
Бегущая строка с плавной анимацией — текст непрерывно движется слева направо. Поддерживает паузу при наведении, настраиваемую скорость и несколько строк с разным направлением. Чистый JS без библиотек.
Запустить
Смотреть код
👁 9
let root = container;
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);
Кнопка притягивается к курсору при наведении — текст и сама кнопка смещаются в сторону мыши создавая эффект магнита. При уходе курсора плавно возвращается на место. Чистый JS без библиотек.
Запустить
Смотреть код
👁 6
let root = container;
const style = document.createElement('style');
style.textContent = `
.magnetic-wrap {
background: #050505;
padding: 60px 20px;
display: flex;
gap: 30px;
justify-content: center;
align-items: center;
flex-wrap: wrap;
font-family: monospace;
min-height: 200px;
}
.magnetic-btn {
position: relative;
padding: 16px 36px;
border-radius: 50px;
border: none;
cursor: pointer;
font-family: monospace;
font-size: 14px;
font-weight: bold;
letter-spacing: 0.08em;
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1),
box-shadow 0.3s ease;
overflow: hidden;
}
.magnetic-btn-primary {
background: #00ff9c;
color: #000;
box-shadow: 0 4px 20px rgba(0,255,156,0.3);
}
.magnetic-btn-primary:hover {
box-shadow: 0 8px 30px rgba(0,255,156,0.5);
}
.magnetic-btn-outline {
background: transparent;
color: #00ff9c;
border: 1.5px solid #00ff9c;
box-shadow: 0 4px 20px rgba(0,255,156,0.1);
}
.magnetic-btn-outline:hover {
box-shadow: 0 8px 30px rgba(0,255,156,0.3);
}
.magnetic-btn-dark {
background: #111827;
color: #fff;
border: 1px solid rgba(255,255,255,0.1);
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
}
.magnetic-btn-dark:hover {
box-shadow: 0 8px 30px rgba(255,255,255,0.15);
}
.magnetic-btn-text {
position: relative;
display: inline-block;
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
pointer-events: none;
}
`;
document.head.appendChild(style);
const wrap = document.createElement('div');
wrap.className = 'magnetic-wrap';
const buttons = [
{ label: 'Подписаться', cls: 'magnetic-btn-primary' },
{ label: 'Подробнее', cls: 'magnetic-btn-outline' },
{ label: 'Сохранить', cls: 'magnetic-btn-dark' },
];
buttons.forEach(b => {
const btn = document.createElement('button');
btn.className = `magnetic-btn ${b.cls}`;
const span = document.createElement('span');
span.className = 'magnetic-btn-text';
span.textContent = b.label;
btn.appendChild(span);
// Магнетизм
const STRENGTH = 0.38;
const TEXT_STRENGTH = 0.55;
btn.addEventListener('mousemove', e => {
const rect = btn.getBoundingClientRect();
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
const dx = e.clientX - cx;
const dy = e.clientY - cy;
btn.style.transform = `translate(${dx * STRENGTH}px, ${dy * STRENGTH}px)`;
span.style.transform = `translate(${dx * TEXT_STRENGTH}px, ${dy * TEXT_STRENGTH}px)`;
});
btn.addEventListener('mouseleave', () => {
btn.style.transform = 'translate(0, 0)';
span.style.transform = 'translate(0, 0)';
});
wrap.appendChild(btn);
});
container.appendChild(wrap);
Загрузка...