Код: Выделить всё
document.addEventListener('DOMContentLoaded', () => {
String.prototype.timeFormat = function() {
let sec = parseInt(this, 10);
let h = Math.floor(sec / 3600);
let m = Math.floor((sec % 3600) / 60);
let s = sec % 60;
if (h < 10) h = '0' + h;
if (m < 10) m = '0' + m;
if (s < 10) s = '0' + s;
return h > 0 ? `${h}:${m}:${s}` : `${m}:${s}`;
};
function toggleFullScreen(elem) {
if (!document.fullscreenElement) {
elem.requestFullscreen?.() || elem.webkitRequestFullscreen?.() || elem.msRequestFullscreen?.();
} else {
document.exitFullscreen?.() || document.webkitExitFullscreen?.() || document.msExitFullscreen?.();
}
}
document.querySelectorAll('.video-container').forEach(container => {
const video = container.querySelector('video');
const poster = container.querySelector('.poster');
const spinner = container.querySelector('.loading-spinner');
const playBtn = container.querySelector('input[name="play-pause"]');
const progressBar = container.querySelector('.progress-bar');
const progress = container.querySelector('.progress');
const currentTimeEl = container.querySelector('.current-time');
const durationEl = container.querySelector('.duration');
const volumeSlider = container.querySelector('.volume-slider');
const muteToggle = container.querySelector('.mute-toggle');
const muteCheckbox = muteToggle.querySelector('input[type="checkbox"]');
const rateDisplay = container.querySelector('.rate_display');
const fullscreenBtn = container.querySelector('input[name="screen-toggler"]');
const rateOptions = container.querySelectorAll('.playback-rate ul li');
let manualPaused = false;
let isTryingToPlay = false;
function showSpinner() {
spinner.style.opacity = 1;
}
function hideSpinner() {
spinner.style.opacity = 0;
}
function updatePlayPause() {
if (video.paused) {
playBtn.value = 'Play';
playBtn.classList.add('play');
playBtn.classList.remove('pause');
} else {
playBtn.value = 'Pause';
playBtn.classList.remove('play');
playBtn.classList.add('pause');
}
}
function updateProgress() {
const dur = isFinite(video.duration) ? video.duration : 0;
const cur = isFinite(video.currentTime) ? video.currentTime : 0;
const percent = dur > 0 ? (cur / dur) * 100 : 0;
progress.style.width = percent + '%';
currentTimeEl.textContent = Math.round(cur).toString().timeFormat();
durationEl.textContent = dur > 0 ? Math.round(dur).toString().timeFormat() : '--:--';
}
async function attemptPlay() {
if (manualPaused || isTryingToPlay) return;
isTryingToPlay = true;
showSpinner();
try {
await video.play();
} catch {}
hideSpinner();
isTryingToPlay = false;
updatePlayPause();
}
playBtn.addEventListener('click', () => {
manualPaused = !video.paused;
if (!manualPaused) {
poster.style.display = 'none';
attemptPlay();
} else {
video.pause();
}
});
volumeSlider.addEventListener('input', () => {
video.volume = volumeSlider.value;
video.muted = video.volume === 0;
muteCheckbox.checked = video.muted;
muteToggle.classList.toggle('muted', video.muted);
});
muteToggle.addEventListener('click', () => {
video.muted = muteCheckbox.checked;
muteToggle.classList.toggle('muted', video.muted);
});
progressBar.addEventListener('click', e => {
const rect = progressBar.getBoundingClientRect();
const dur = isFinite(video.duration) && video.duration > 0 ? video.duration : 100;
video.currentTime = ((e.clientX - rect.left) / rect.width) * dur;
updateProgress();
attemptPlay();
});
fullscreenBtn.addEventListener('click', () => {
container.classList.toggle('fullscreen');
fullscreenBtn.classList.toggle('exit');
toggleFullScreen(document.body);
});
document.addEventListener('fullscreenchange', () => {
if (!document.fullscreenElement) {
container.classList.remove('fullscreen');
fullscreenBtn.classList.remove('exit');
}
});
rateOptions.forEach(opt => {
opt.addEventListener('click', () => {
video.playbackRate = parseFloat(opt.dataset.rate);
rateDisplay.textContent = video.playbackRate === 1 ? 'Normal' : video.playbackRate + 'x';
});
});
video.addEventListener('loadedmetadata', updateProgress);
video.addEventListener('timeupdate', updateProgress);
video.addEventListener('waiting', showSpinner);
video.addEventListener('stalled', showSpinner);
video.addEventListener('seeking', showSpinner);
video.addEventListener('playing', () => {
hideSpinner();
updatePlayPause();
});
video.addEventListener('canplay', () => {
hideSpinner();
updatePlayPause();
});
video.addEventListener('pause', updatePlayPause);
video.addEventListener('ended', updatePlayPause);
video.addEventListener('seeked', () => {
updateProgress();
if (!manualPaused) attemptPlay();
updatePlayPause();
});
});
});Код: Выделить всё
.video-container {
font-family: Arial, sans-serif;
max-width: 960px;
margin: 0 auto;
position: relative;
overflow: hidden;
aspect-ratio: 16 / 9;
}
.video-container video,
.video-container .poster {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* Video loading indicator (spinner) */
@keyframes spin {
to {
transform: rotate(360deg)
}
}
.loading-spinner {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 5;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
}
.video-container.loading .loading-spinner {
opacity: 1;
}
.spinner {
width: 48px;
height: 48px;
border: 4px solid rgba(255, 255, 255, 0.3);
border-top-color: #fff;
border-radius: 50%;
animation: spin 0.9s linear infinite;
}
.video-container:hover .controls-wrapper {
bottom: 0;
}
.video-container.fullscreen {
max-width: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1021;
}
.video-container.fullscreen video {
position: absolute;
top: 50%;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
.controls-wrapper {
position: absolute;
height: 50px;
bottom: -57px;
left: 0;
right: 0;
z-index: 10;
-webkit-transition: all 0.25s ease-in-out;
-moz-transition: all 0.25s ease-in-out;
transition: all 0.25s ease-in-out;
}
.controls-wrapper * {
cursor: pointer;
}
.controls-wrapper span {
cursor: default;
color: #fff;
}
.progress-bar {
height: 5px;
width: 100%;
background: #777;
cursor: pointer;
}
.progress {
height: 5px;
width: 0%;
max-width: 100%;
background: #c62129;
}
.video-controls {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
justify-content: space-between;
align-items: center;
height: 45px;
background: linear-gradient(to top,
rgba(0, 0, 0, 0.4) 0,
rgba(0, 0, 0, 0) 100%);
}
.video-controls>li {
font-size: 12px;
margin: 0 5px;
}
.video-controls li.fullscreen-container {
margin: 0 10px 0 auto;
}
.video-controls li input {
display: inline-block;
padding: 0 2px;
float: left;
height: 24px;
line-height: 24px;
cursor: pointer;
width: 20px;
overflow: hidden;
line-height: 999px;
margin: 0;
border: none;
background: transparent;
outline: none;
background: url("https://i.sstatic.net/Da9PZ5i4.png") no-repeat top left;
width: 20px;
height: 20px;
}
.video-controls li span {
display: inline-block;
padding: 0 2px;
float: left;
height: 24px;
line-height: 24px;
}
.video-controls li a {
display: inline-block;
padding: 0 2px;
float: left;
height: 24px;
line-height: 24px;
cursor: pointer;
width: 20px;
overflow: hidden;
line-height: 999px;
margin: 0;
border: none;
background: transparent;
outline: none;
text-decoration: none;
background: url("https://i.sstatic.net/Da9PZ5i4.png") no-repeat top left;
width: 20px;
height: 20px;
}
.video-controls li input[type="checkbox"] {
opacity: 0;
}
.video-controls li input.volume-slider {
width: 100px;
background: #c62129;
height: 5px;
}
.video-controls li input.play {
background-position: 0 0;
}
.video-controls li input.pause {
background-position: 0 -30px;
}
.video-controls li .previous {
background-position: 0 -60px;
}
.video-controls li .next {
background-position: 0 -90px;
}
.video-controls li input.toggle-fullscreen {
background-position: 0 -180px;
}
.video-controls li input.toggle-fullscreen.exit {
background-position: 0 -210px;
}
.video-controls input[type="range"] {
-webkit-appearance: none;
height: 4px;
background: #c62129;
opacity: 0.8;
padding: 0;
margin: 0;
outline: none;
overflow: visible;
}
.video-controls input[type="range"]:focus {
-webkit-appearance: none;
height: 4px;
background: #c62129;
opacity: 0.8;
padding: 0;
margin: 0;
outline: none;
overflow: visible;
}
.video-controls input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 10px;
width: 10px;
border-radius: 50%;
background: #fff;
cursor: pointer;
margin-top: -1px;
}
.video-controls input[type="range"]::-moz-range-thumb {
-webkit-appearance: none;
height: 10px;
width: 10px;
border-radius: 50%;
background: #fff;
cursor: pointer;
margin-top: -2px;
}
.video-controls li.mute-toggle {
background: url("https://i.sstatic.net/Da9PZ5i4.png") no-repeat top left;
width: 20px;
height: 20px;
}
.video-controls li.unmuted {
background-position: 0 -120px;
}
.video-controls li.muted {
background-position: 0 -150px;
}
.playback-rate {
position: relative;
}
.playback-rate span {
width: 55px;
text-align: center;
color: #1c232c;
padding: 5px;
border-radius: 2px;
background: rgba(255, 255, 255, 0.7);
cursor: pointer;
}
.playback-rate .piker {
cursor: pointer;
transition: opacity 1s ease-in-out;
display: none;
position: absolute;
opacity: 0;
bottom: 0;
padding-bottom: 30px;
width: 55px;
}
.playback-rate:hover .piker {
display: block;
opacity: 1;
text-align: center;
}
.playback-rate ul {
padding: 0;
margin: 0;
overflow: hidden;
border-radius: 2px;
background: rgba(0, 0, 0, 0.75);
box-shadow: 0 1px 1px 0px rgba(0, 0, 0, 0.12);
}
.playback-rate ul li {
color: #fff;
text-align: right;
margin: 0;
display: block;
height: 20px;
line-height: 20px;
padding: 0 5px;
}
.playback-rate ul li:hover {
background: rgba(255, 255, 255, 0.1);
}Код: Выделить всё
[img]https://fastly.picsum.photos/id/866/1200/800.jpg?hmac=cMZ0zQRwnCFQskIqM7bmp90xVm9-k6lHUAFEJZp3mw4[/img]
class="poster"
/>
[list]
[*]
[*]
[url=https://larablog.com/show/enhancing-female-pleasure-techniques-for-deeper-intimacy]
Previous
[/url]
[*]
[*]
[*]
[*]
/
[*]
Normal
0.5x
[*]0.75x
[*]Normal
[*]1.125x
[*]1.5x
[*]2x
[/list]
[*]
Проблема, с которой я столкнулся, заключается в следующем: когда видео очень большое (100 МБ или более), оно блокируется, и попытка разблокировать его путем поиска уже буферизованной точки на временной шкале не приводит к его разблокировке.
Что вызывает эту ошибку?
Каков надежный способ сделать это возможным? пользователю разблокировать видео, вернувшись обратно на временную шкалу?
Подробнее здесь: https://stackoverflow.com/questions/798 ... -using-the
Мобильная версия