Add video player

This commit is contained in:
alexencom committed 2026-10-30 22:31:58 +03:00
1 parent edc022c895
commit e4988e9d67
9 files changed
+303 -7

No files matched your search

+104 -1
View File
@@ -69,4 +69,107 @@ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
});
});
// ===== Portfolio Video Lightbox =====
(function() {
const modal = document.getElementById('videoModal');
const player = document.getElementById('videoModalPlayer');
const title = document.getElementById('videoModalTitle');
const fsBtn = document.getElementById('videoModalFullscreen');
const triggers = document.querySelectorAll('.work-video[data-video]');
if (!modal || !player || !triggers.length) return;
let lastFocus = null;
function open(src, label) {
lastFocus = document.activeElement;
title.textContent = label || '';
player.src = src;
modal.classList.add('open');
modal.setAttribute('aria-hidden', 'false');
document.body.classList.add('video-modal-open');
const p = player.play();
if (p && typeof p.catch === 'function') p.catch(() => {});
player.focus({ preventScroll: true });
}
function close() {
if (document.fullscreenElement) {
document.exitFullscreen().catch(() => {});
}
player.pause();
modal.classList.remove('open');
modal.setAttribute('aria-hidden', 'true');
document.body.classList.remove('video-modal-open');
setTimeout(() => {
if (!modal.classList.contains('open')) {
player.removeAttribute('src');
player.load();
}
}, 300);
if (lastFocus && typeof lastFocus.focus === 'function') lastFocus.focus({ preventScroll: true });
}
function toggleFullscreen() {
if (document.fullscreenElement) {
document.exitFullscreen().catch(() => {});
return;
}
if (player.requestFullscreen) {
player.requestFullscreen().catch(() => {});
} else if (player.webkitEnterFullscreen) {
player.webkitEnterFullscreen(); // iOS Safari
}
}
triggers.forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
open(btn.getAttribute('data-video'), btn.getAttribute('data-title'));
});
});
// Превью: когда карточка попадает в зону видимости, гарантируем загрузку первого кадра
const previews = document.querySelectorAll('.work-video-preview');
if ('IntersectionObserver' in window && previews.length) {
const io = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (!entry.isIntersecting) return;
const v = entry.target;
obs.unobserve(v);
if (v.readyState < 2) {
v.preload = 'auto';
v.load();
}
v.addEventListener('loadeddata', () => {
if (v.currentTime === 0) { try { v.currentTime = 0.1; } catch (err) {} }
v.preload = 'metadata';
}, { once: true });
});
}, { rootMargin: '200px 0px' });
previews.forEach(v => io.observe(v));
}
modal.querySelectorAll('[data-video-close]').forEach(el => {
el.addEventListener('click', close);
});
// Клик по тёмной области вокруг плеера тоже закрывает окно
modal.querySelectorAll('.video-modal-dialog, .video-modal-stage, .video-modal-bar').forEach(el => {
el.addEventListener('click', function(e) {
if (e.target === el) close();
});
});
if (fsBtn) fsBtn.addEventListener('click', toggleFullscreen);
player.addEventListener('dblclick', toggleFullscreen);
document.addEventListener('keydown', function(e) {
if (!modal.classList.contains('open')) return;
if (e.key === 'Escape') { close(); return; }
if (e.key === ' ' && document.activeElement === player) return; // native handling
if (e.key === 'f' || e.key === 'F') toggleFullscreen();
});
})();