|
|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <div class="video-processor">
|
|
|
+ <div class="video-processor" @click="toggleControls">
|
|
|
<!-- 封面图片 -->
|
|
|
<img
|
|
|
v-if="processedCoverUrl && !isVideoMode"
|
|
|
@@ -16,7 +16,6 @@
|
|
|
ref="videoElement"
|
|
|
:class="videoClass"
|
|
|
:poster="processedCoverUrl"
|
|
|
- controls
|
|
|
preload="metadata"
|
|
|
playsinline
|
|
|
webkit-playsinline
|
|
|
@@ -28,13 +27,99 @@
|
|
|
@loadeddata="onVideoLoadedData"
|
|
|
@error="onVideoError"
|
|
|
@canplay="onVideoCanPlay"
|
|
|
- @play="onVideoPlay"
|
|
|
+ @play="onVideoPlayEvent"
|
|
|
+ @pause="onVideoPauseEvent"
|
|
|
@timeupdate="onVideoTimeUpdate"
|
|
|
@seeking="onVideoSeeking"
|
|
|
+ @durationchange="onDurationChange"
|
|
|
+ @ended="onVideoEnded"
|
|
|
>
|
|
|
您的浏览器不支持视频播放
|
|
|
</video>
|
|
|
|
|
|
+ <!-- 自定义播放控制条 -->
|
|
|
+ <div
|
|
|
+ v-if="isVideoMode && !error"
|
|
|
+ class="custom-controls"
|
|
|
+ :class="{ 'show': showControls }"
|
|
|
+ @click.stop
|
|
|
+ >
|
|
|
+ <!-- 播放/暂停按钮(中央) -->
|
|
|
+ <div
|
|
|
+ v-if="!isPlaying"
|
|
|
+ class="play-button-center"
|
|
|
+ @click="togglePlay"
|
|
|
+ >
|
|
|
+ <svg class="w-16 h-16" fill="white" viewBox="0 0 24 24">
|
|
|
+ <path d="M8 5v14l11-7z"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 底部控制栏 -->
|
|
|
+ <div class="controls-bottom">
|
|
|
+ <!-- 进度条 -->
|
|
|
+ <div class="progress-container" @click="seekVideo">
|
|
|
+ <div class="progress-bar">
|
|
|
+ <div class="progress-played" :style="{ width: progress + '%' }"></div>
|
|
|
+ <div class="progress-buffered" :style="{ width: buffered + '%' }"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 控制按钮区域 -->
|
|
|
+ <div class="controls-buttons">
|
|
|
+ <!-- 左侧:播放/暂停 + 时间 -->
|
|
|
+ <div class="controls-left">
|
|
|
+ <button class="control-btn" @click="togglePlay">
|
|
|
+ <svg v-if="!isPlaying" class="w-6 h-6" fill="white" viewBox="0 0 24 24">
|
|
|
+ <path d="M8 5v14l11-7z"/>
|
|
|
+ </svg>
|
|
|
+ <svg v-else class="w-6 h-6" fill="white" viewBox="0 0 24 24">
|
|
|
+ <path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ <span class="time-display">{{ formatTime(currentTime) }} / {{ formatTime(duration) }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右侧:音量 + 全屏 -->
|
|
|
+ <div class="controls-right">
|
|
|
+ <!-- 音量控制 -->
|
|
|
+ <div class="volume-control">
|
|
|
+ <button class="control-btn" @click="toggleMute">
|
|
|
+ <svg v-if="!isMuted && volume > 0.5" class="w-6 h-6" fill="white" viewBox="0 0 24 24">
|
|
|
+ <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
|
|
|
+ </svg>
|
|
|
+ <svg v-else-if="!isMuted && volume > 0" class="w-6 h-6" fill="white" viewBox="0 0 24 24">
|
|
|
+ <path d="M7 9v6h4l5 5V4l-5 5H7z"/>
|
|
|
+ </svg>
|
|
|
+ <svg v-else class="w-6 h-6" fill="white" viewBox="0 0 24 24">
|
|
|
+ <path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ <input
|
|
|
+ v-if="showVolumeSlider"
|
|
|
+ type="range"
|
|
|
+ class="volume-slider"
|
|
|
+ min="0"
|
|
|
+ max="100"
|
|
|
+ :value="volume * 100"
|
|
|
+ @input="changeVolume"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 全屏按钮 -->
|
|
|
+ <button class="control-btn fullscreen-btn" @click="toggleFullscreen">
|
|
|
+ <svg v-if="!isFullscreen" class="w-6 h-6" fill="white" viewBox="0 0 24 24">
|
|
|
+ <path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>
|
|
|
+ </svg>
|
|
|
+ <svg v-else class="w-6 h-6" fill="white" viewBox="0 0 24 24">
|
|
|
+ <path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/>
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 加载状态 -->
|
|
|
<div v-if="loading" class="loading-overlay">
|
|
|
<div class="spinner"></div>
|
|
|
@@ -102,6 +187,19 @@ const maxRetries = 3;
|
|
|
const videoElement = ref<HTMLVideoElement>();
|
|
|
const hlsInstance = ref<Hls | null>(null);
|
|
|
|
|
|
+// 播放器控制状态
|
|
|
+const isPlaying = ref(false);
|
|
|
+const currentTime = ref(0);
|
|
|
+const duration = ref(0);
|
|
|
+const progress = ref(0);
|
|
|
+const buffered = ref(0);
|
|
|
+const volume = ref(1);
|
|
|
+const isMuted = ref(false);
|
|
|
+const isFullscreen = ref(false);
|
|
|
+const showControls = ref(true);
|
|
|
+const showVolumeSlider = ref(true); // 桌面端显示音量滑块,移动端通过CSS隐藏
|
|
|
+let controlsTimer: ReturnType<typeof setTimeout> | null = null;
|
|
|
+
|
|
|
// 计算属性
|
|
|
const isVideoMode = computed(() => !!props.m3u8Url);
|
|
|
const showRetryButton = computed(
|
|
|
@@ -493,6 +591,112 @@ const retry = (): void => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+// 播放器控制方法
|
|
|
+const togglePlay = (): void => {
|
|
|
+ if (!videoElement.value) return;
|
|
|
+
|
|
|
+ if (isPlaying.value) {
|
|
|
+ videoElement.value.pause();
|
|
|
+ } else {
|
|
|
+ videoElement.value.play();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const toggleMute = (): void => {
|
|
|
+ if (!videoElement.value) return;
|
|
|
+
|
|
|
+ isMuted.value = !isMuted.value;
|
|
|
+ videoElement.value.muted = isMuted.value;
|
|
|
+};
|
|
|
+
|
|
|
+const changeVolume = (event: Event): void => {
|
|
|
+ if (!videoElement.value) return;
|
|
|
+
|
|
|
+ const target = event.target as HTMLInputElement;
|
|
|
+ const newVolume = parseInt(target.value) / 100;
|
|
|
+ volume.value = newVolume;
|
|
|
+ videoElement.value.volume = newVolume;
|
|
|
+
|
|
|
+ if (newVolume > 0 && isMuted.value) {
|
|
|
+ isMuted.value = false;
|
|
|
+ videoElement.value.muted = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const toggleFullscreen = async (): Promise<void> => {
|
|
|
+ if (!videoElement.value) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (!document.fullscreenElement) {
|
|
|
+ // 进入全屏
|
|
|
+ const container = videoElement.value.parentElement;
|
|
|
+ if (container) {
|
|
|
+ // 尝试不同的全屏API
|
|
|
+ if (container.requestFullscreen) {
|
|
|
+ await container.requestFullscreen();
|
|
|
+ } else if ((container as any).webkitRequestFullscreen) {
|
|
|
+ await (container as any).webkitRequestFullscreen();
|
|
|
+ } else if ((container as any).mozRequestFullScreen) {
|
|
|
+ await (container as any).mozRequestFullScreen();
|
|
|
+ } else if ((container as any).msRequestFullscreen) {
|
|
|
+ await (container as any).msRequestFullscreen();
|
|
|
+ }
|
|
|
+ // iOS特殊处理
|
|
|
+ else if ((videoElement.value as any).webkitEnterFullscreen) {
|
|
|
+ (videoElement.value as any).webkitEnterFullscreen();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 退出全屏
|
|
|
+ if (document.exitFullscreen) {
|
|
|
+ await document.exitFullscreen();
|
|
|
+ } else if ((document as any).webkitExitFullscreen) {
|
|
|
+ await (document as any).webkitExitFullscreen();
|
|
|
+ } else if ((document as any).mozCancelFullScreen) {
|
|
|
+ await (document as any).mozCancelFullScreen();
|
|
|
+ } else if ((document as any).msExitFullscreen) {
|
|
|
+ await (document as any).msExitFullscreen();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.error("全屏切换失败:", err);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const seekVideo = (event: MouseEvent): void => {
|
|
|
+ if (!videoElement.value || !duration.value) return;
|
|
|
+
|
|
|
+ const progressBar = event.currentTarget as HTMLElement;
|
|
|
+ const rect = progressBar.getBoundingClientRect();
|
|
|
+ const pos = (event.clientX - rect.left) / rect.width;
|
|
|
+ const seekTime = pos * duration.value;
|
|
|
+
|
|
|
+ videoElement.value.currentTime = seekTime;
|
|
|
+};
|
|
|
+
|
|
|
+const formatTime = (seconds: number): string => {
|
|
|
+ if (isNaN(seconds)) return "00:00";
|
|
|
+
|
|
|
+ const mins = Math.floor(seconds / 60);
|
|
|
+ const secs = Math.floor(seconds % 60);
|
|
|
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
|
+};
|
|
|
+
|
|
|
+const toggleControls = (): void => {
|
|
|
+ showControls.value = true;
|
|
|
+
|
|
|
+ if (controlsTimer) {
|
|
|
+ clearTimeout(controlsTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果正在播放,3秒后自动隐藏控制条
|
|
|
+ if (isPlaying.value) {
|
|
|
+ controlsTimer = setTimeout(() => {
|
|
|
+ showControls.value = false;
|
|
|
+ }, 3000);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
// 事件处理
|
|
|
const handleCoverError = (event: Event): void => {
|
|
|
const img = event.target as HTMLImageElement;
|
|
|
@@ -513,11 +717,47 @@ const onVideoCanPlay = (): void => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-const onVideoPlay = (): void => {
|
|
|
+const onVideoPlayEvent = (): void => {
|
|
|
+ isPlaying.value = true;
|
|
|
emit("play");
|
|
|
+ toggleControls(); // 开始自动隐藏倒计时
|
|
|
+};
|
|
|
+
|
|
|
+const onVideoPauseEvent = (): void => {
|
|
|
+ isPlaying.value = false;
|
|
|
+ showControls.value = true;
|
|
|
+ if (controlsTimer) {
|
|
|
+ clearTimeout(controlsTimer);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const onDurationChange = (): void => {
|
|
|
+ if (videoElement.value) {
|
|
|
+ duration.value = videoElement.value.duration;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const onVideoEnded = (): void => {
|
|
|
+ isPlaying.value = false;
|
|
|
+ showControls.value = true;
|
|
|
};
|
|
|
|
|
|
const onVideoTimeUpdate = (): void => {
|
|
|
+ if (videoElement.value) {
|
|
|
+ currentTime.value = videoElement.value.currentTime;
|
|
|
+
|
|
|
+ // 更新进度
|
|
|
+ if (duration.value > 0) {
|
|
|
+ progress.value = (currentTime.value / duration.value) * 100;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新缓冲进度
|
|
|
+ if (videoElement.value.buffered.length > 0) {
|
|
|
+ const bufferedEnd = videoElement.value.buffered.end(videoElement.value.buffered.length - 1);
|
|
|
+ buffered.value = (bufferedEnd / duration.value) * 100;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
emit("timeupdate");
|
|
|
};
|
|
|
|
|
|
@@ -600,10 +840,39 @@ watch(
|
|
|
{ immediate: true }
|
|
|
);
|
|
|
|
|
|
+// 全屏状态监听
|
|
|
+const handleFullscreenChange = (): void => {
|
|
|
+ isFullscreen.value = !!(
|
|
|
+ document.fullscreenElement ||
|
|
|
+ (document as any).webkitFullscreenElement ||
|
|
|
+ (document as any).mozFullScreenElement ||
|
|
|
+ (document as any).msFullscreenElement
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+// 组件挂载时添加全屏监听
|
|
|
+onMounted(() => {
|
|
|
+ document.addEventListener('fullscreenchange', handleFullscreenChange);
|
|
|
+ document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
|
|
|
+ document.addEventListener('mozfullscreenchange', handleFullscreenChange);
|
|
|
+ document.addEventListener('MSFullscreenChange', handleFullscreenChange);
|
|
|
+});
|
|
|
+
|
|
|
// 组件卸载时清理资源
|
|
|
onUnmounted(() => {
|
|
|
destroyHls();
|
|
|
|
|
|
+ // 清理全屏监听
|
|
|
+ document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
|
|
+ document.removeEventListener('webkitfullscreenchange', handleFullscreenChange);
|
|
|
+ document.removeEventListener('mozfullscreenchange', handleFullscreenChange);
|
|
|
+ document.removeEventListener('MSFullscreenChange', handleFullscreenChange);
|
|
|
+
|
|
|
+ // 清理控制条定时器
|
|
|
+ if (controlsTimer) {
|
|
|
+ clearTimeout(controlsTimer);
|
|
|
+ }
|
|
|
+
|
|
|
// 清理 Blob URL
|
|
|
if (processedCoverUrl.value?.startsWith("blob:")) {
|
|
|
URL.revokeObjectURL(processedCoverUrl.value);
|
|
|
@@ -648,6 +917,216 @@ defineExpose({
|
|
|
height: 100%;
|
|
|
}
|
|
|
|
|
|
+/* 自定义控制条 */
|
|
|
+.custom-controls {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ pointer-events: none;
|
|
|
+ opacity: 0;
|
|
|
+ transition: opacity 0.3s ease;
|
|
|
+ z-index: 5;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-controls.show {
|
|
|
+ opacity: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-controls > * {
|
|
|
+ pointer-events: auto;
|
|
|
+}
|
|
|
+
|
|
|
+/* 中央播放按钮 */
|
|
|
+.play-button-center {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ width: 80px;
|
|
|
+ height: 80px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
+ border-radius: 50%;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
+}
|
|
|
+
|
|
|
+.play-button-center:hover {
|
|
|
+ background: rgba(0, 0, 0, 0.8);
|
|
|
+ transform: translate(-50%, -50%) scale(1.1);
|
|
|
+}
|
|
|
+
|
|
|
+/* 底部控制栏 */
|
|
|
+.controls-bottom {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.4) 70%, transparent 100%);
|
|
|
+ padding: 10px 15px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 进度条容器 */
|
|
|
+.progress-container {
|
|
|
+ width: 100%;
|
|
|
+ padding: 8px 0;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-bar {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 4px;
|
|
|
+ background: rgba(255, 255, 255, 0.3);
|
|
|
+ border-radius: 2px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-buffered {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ height: 100%;
|
|
|
+ background: rgba(255, 255, 255, 0.5);
|
|
|
+ border-radius: 2px;
|
|
|
+ transition: width 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-played {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ height: 100%;
|
|
|
+ background: #3b82f6;
|
|
|
+ border-radius: 2px;
|
|
|
+ transition: width 0.1s ease;
|
|
|
+}
|
|
|
+
|
|
|
+/* 控制按钮区域 */
|
|
|
+.controls-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.controls-left,
|
|
|
+.controls-right {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.control-btn {
|
|
|
+ background: none;
|
|
|
+ border: none;
|
|
|
+ padding: 8px;
|
|
|
+ cursor: pointer;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.control-btn:hover {
|
|
|
+ background: rgba(255, 255, 255, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.control-btn:active {
|
|
|
+ transform: scale(0.95);
|
|
|
+}
|
|
|
+
|
|
|
+/* 时间显示 */
|
|
|
+.time-display {
|
|
|
+ color: white;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ white-space: nowrap;
|
|
|
+ user-select: none;
|
|
|
+}
|
|
|
+
|
|
|
+/* 音量控制 */
|
|
|
+.volume-control {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.volume-slider {
|
|
|
+ width: 60px;
|
|
|
+ height: 4px;
|
|
|
+ appearance: none;
|
|
|
+ background: rgba(255, 255, 255, 0.3);
|
|
|
+ border-radius: 2px;
|
|
|
+ outline: none;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.volume-slider::-webkit-slider-thumb {
|
|
|
+ appearance: none;
|
|
|
+ width: 12px;
|
|
|
+ height: 12px;
|
|
|
+ background: white;
|
|
|
+ border-radius: 50%;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.volume-slider::-moz-range-thumb {
|
|
|
+ width: 12px;
|
|
|
+ height: 12px;
|
|
|
+ background: white;
|
|
|
+ border-radius: 50%;
|
|
|
+ border: none;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+/* 移动端优化 */
|
|
|
+@media screen and (max-width: 768px) {
|
|
|
+ .controls-bottom {
|
|
|
+ padding: 8px 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .play-button-center {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .time-display {
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .control-btn {
|
|
|
+ padding: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .volume-slider {
|
|
|
+ display: none; /* 移动端隐藏音量滑块 */
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* iOS全屏样式优化 */
|
|
|
+@media screen and (max-width: 768px) {
|
|
|
+ .video-processor:fullscreen .custom-controls,
|
|
|
+ .video-processor:-webkit-full-screen .custom-controls,
|
|
|
+ .video-processor:-moz-full-screen .custom-controls {
|
|
|
+ background: rgba(0, 0, 0, 0.3);
|
|
|
+ }
|
|
|
+
|
|
|
+ .video-processor:fullscreen .controls-bottom,
|
|
|
+ .video-processor:-webkit-full-screen .controls-bottom,
|
|
|
+ .video-processor:-moz-full-screen .controls-bottom {
|
|
|
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.6) 70%, transparent 100%);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.loading-overlay {
|
|
|
position: absolute;
|
|
|
top: 0;
|