html
复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>高仿B站视频播放器(带竖屏+自由旋转+无限放大+弹幕适配)</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft Yahei", sans-serif;
}
body {
background-color: #000;
color: #fff;
overflow-x: hidden;
}
/* 播放器容器 */
.bilibili-player {
position: relative;
width: 100%;
max-width: 1200px;
margin: 0 auto;
background-color: #000;
}
/* 视频容器 - 新增竖屏模式样式 */
.video-container {
position: relative;
width: 100%;
aspect-ratio: 16/13;
overflow: hidden;
/* 竖屏模式切换过渡 */
transition: all 0.3s ease;
}
/* 竖屏模式 - 核心样式 */
.video-container.vertical-mode {
aspect-ratio: 9/16;
max-width: 500px;
margin: 0 auto;
}
/* 全屏状态下的竖屏模式 */
.video-container:fullscreen.vertical-mode,
.video-container:-webkit-full-screen.vertical-mode,
.video-container:-ms-fullscreen.vertical-mode {
width: 100vw !important;
height: 100vh !important;
max-width: none !important;
display: flex;
justify-content: center;
align-items: center;
}
/* 旋转缩放核心容器 */
.video-wrap {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
cursor: grab;
z-index: 1; /* 视频层级低于弹幕 */
}
.video-wrap:active {
cursor: grabbing;
}
/* 视频元素 - 适配竖屏模式 */
#video-player {
width: 100%;
height: 100%;
object-fit: contain;
background-color: #000;
/* 竖屏视频全屏时保持比例 */
max-width: 100%;
max-height: 100%;
/* 旋转缩放基础样式 */
transition: transform 0.1s ease-out;
transform-origin: center center;
will-change: transform;
}
/* 全屏竖屏模式下的视频 */
.video-container:fullscreen.vertical-mode #video-player,
.video-container:-webkit-full-screen.vertical-mode #video-player,
.video-container:-ms-fullscreen.vertical-mode #video-player {
aspect-ratio: 9/16;
}
/* 全屏横屏模式下的视频 */
.video-container:fullscreen:not(.vertical-mode) #video-player,
.video-container:-webkit-full-screen:not(.vertical-mode) #video-player,
.video-container:-ms-fullscreen:not(.vertical-mode) #video-player {
aspect-ratio: 16/9;
max-width: 100vw;
max-height: 100vh;
}
/* 弹幕容器 - 核心适配:层级高于视频,同步变换 */
.danmaku-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%; /* 改为100%高度适配旋转缩放 */
pointer-events: none;
overflow: hidden;
z-index: 5; /* 确保弹幕在视频上方 */
transform-origin: center center;
will-change: transform;
}
/* 竖屏模式下的弹幕容器 */
.video-container.vertical-mode .danmaku-container {
aspect-ratio: 9/16;
max-width: 500px;
margin: 0 auto;
}
/* 全屏竖屏模式下的弹幕容器 */
.video-container:fullscreen.vertical-mode .danmaku-container,
.video-container:-webkit-full-screen.vertical-mode .danmaku-container,
.video-container:-ms-fullscreen.vertical-mode .danmaku-container {
aspect-ratio: 9/16;
width: 100%;
height: 100%;
max-width: 100%;
}
/* 单个弹幕样式 - 修复旋转后位置计算 */
.danmaku-item {
position: absolute;
color: #fff;
font-size: 18px;
white-space: nowrap;
text-shadow: 0 0 2px #000;
z-index: 10;
will-change: transform;
animation-timing-function: linear;
animation-iteration-count: 1;
animation-fill-mode: forwards;
transform-origin: center center;
}
/* 竖屏模式下的弹幕字体适配 */
.video-container.vertical-mode .danmaku-item {
font-size: 16px;
max-width: 80%;
}
/* 视频标题 - 覆盖在视频上方 */
.video-title {
position: absolute;
top: 10px;
left: 10px;
z-index: 15;
padding: 5px 10px;
font-size: 18px;
font-weight: bold;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 4px;
max-width: calc(100% - 220px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: opacity 0.3s ease;
}
/* 竖屏模式下的标题适配 */
.video-container.vertical-mode .video-title {
max-width: calc(100% - 180px);
font-size: 16px;
}
/* 全屏竖屏模式下的标题 */
.video-container:fullscreen.vertical-mode .video-title {
max-width: calc(100% - 100px);
font-size: 18px;
top: 20px;
left: 20px;
}
/* 标题隐藏样式 */
.video-title.hidden {
opacity: 0;
pointer-events: none;
}
/* 控制器样式 - 重构布局 */
.video-controls {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background: linear-gradient(transparent, rgba(0,0,0,0.8));
padding: 10px 20px 15px;
transition: all 0.3s ease;
z-index: 20;
}
/* 竖屏模式下的控制器适配 */
.video-container.vertical-mode .video-controls {
max-width: 500px;
margin: 0 auto;
}
/* 全屏竖屏模式下的控制器 */
.video-container:fullscreen.vertical-mode .video-controls {
max-width: 100%;
width: 100%;
padding: 15px 20px 20px;
}
/* 进度条容器 */
.progress-container {
width: 100%;
height: 4px;
background-color: rgba(255,255,255,0.3);
border-radius: 2px;
margin-bottom: 10px;
cursor: pointer;
position: relative;
}
/* 进度条 */
.progress-bar {
height: 100%;
background-color: #fb7299;
border-radius: 2px;
width: 0%;
position: relative;
}
/* 进度条滑块 */
.progress-handle {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
border: none;
background: url('http://192.168.0.101:8080/backgrounds/HIA.png') center center no-repeat;
background-size: contain;
opacity: 1;
transition: opacity 0.2s ease;
cursor: grab;
z-index: 1;
}
.progress-handle:active {
cursor: grabbing;
}
.progress-container:hover .progress-handle {
opacity: 1;
}
/* 控制按钮区域 */
.controls-wrapper {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.controls-row-1 {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.left-controls {
display: flex;
align-items: center;
gap: 15px;
}
.play-control-group {
display: flex;
align-items: center;
gap: 10px;
}
/* 播放控制按钮 */
.play-btn, .rewind-btn, .forward-btn, .mute-btn, .danmaku-toggle-btn, .title-toggle-btn, .vertical-toggle-btn, .rotate-btn, .reset-btn {
width: 30px;
height: 30px;
background: none;
border: none;
color: #fff;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
/* 旋转缩放控制组 */
.rotate-scale-controls {
display: flex;
align-items: center;
gap: 10px;
}
/* 按钮高亮样式 */
.vertical-toggle-btn.active, .rotate-btn.active, .reset-btn.active {
color: #fb7299;
transform: scale(1.1);
}
.time-display {
font-size: 14px;
color: #fff;
margin: 0 10px;
}
.right-controls-row1 {
display: flex;
align-items: center;
gap: 15px;
}
.controls-row-2 {
display: flex;
width: 100%;
gap: 10px;
}
.danmaku-input-container {
flex: 1;
width: 100%;
position: relative;
}
#danmaku-input {
width: 100%;
height: 36px;
background-color: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.3);
border-radius: 18px;
padding: 0 15px;
color: #fff;
outline: none;
font-size: 14px;
}
#send-danmaku {
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%);
background-color: #fb7299;
border: none;
color: #fff;
padding: 4px 12px;
border-radius: 12px;
cursor: pointer;
font-size: 12px;
white-space: nowrap;
}
.fullscreen-btn {
background: none;
border: none;
color: #fff;
font-size: 18px;
cursor: pointer;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
/* 视频选集 */
.chapters-container {
position: absolute;
top: 10px;
left: 0;
z-index: 999;
max-width: 200px;
height: calc(100% - 80px);
transform: translateX(-100%);
transition: transform 0.3s ease-out;
background-color: rgba(0,0,0,0.95);
border-radius: 0 8px 8px 0;
overflow: hidden;
box-shadow: 2px 0 10px rgba(0,0,0,0.5);
}
/* 竖屏模式下的选集容器适配 */
.video-container.vertical-mode .chapters-container {
max-width: 180px;
}
/* 全屏竖屏模式下的选集容器 */
.video-container:fullscreen.vertical-mode .chapters-container {
max-width: 250px;
height: calc(100% - 100px);
}
.chapters-container.show {
transform: translateX(0);
}
.chapters-toggle-btn {
position: absolute;
right: -40px;
top: 50%;
transform: translateY(-50%);
background-color: rgba(0,0,0,0.8);
color: #fff;
border: 1px solid rgba(255,255,255,0.3);
border-radius: 0 4px 4px 0;
padding: 8px 8px;
cursor: pointer;
font-size: 14px;
z-index: 1000;
}
/* 全屏竖屏模式下的选集按钮 */
.video-container:fullscreen.vertical-mode .chapters-toggle-btn {
padding: 10px 10px;
font-size: 16px;
right: -50px;
}
.chapters-list {
width: 100%;
height: 100%;
overflow-y: auto;
padding-top: 10px;
scrollbar-width: thin;
scrollbar-color: #fb7299 transparent;
}
.chapters-list::-webkit-scrollbar {
width: 4px;
}
.chapters-list::-webkit-scrollbar-thumb {
background-color: #fb7299;
border-radius: 2px;
}
.chapter-item {
padding: 8px 10px;
cursor: pointer;
border-bottom: 1px solid rgba(255,255,255,0.1);
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 竖屏模式下的选集项 */
.video-container.vertical-mode .chapter-item {
padding: 7px 9px;
font-size: 13px;
}
/* 全屏竖屏模式下的选集项 */
.video-container:fullscreen.vertical-mode .chapter-item {
padding: 10px 15px;
font-size: 16px;
}
.chapter-item:last-child {
border-bottom: none;
}
.chapter-item.active {
background-color: #fb7299;
color: #fff;
}
.chapter-item:hover {
background-color: rgba(255,255,255,0.1);
}
/* 手机端适配 */
@media (max-width: 768px) {
.play-control-group {
gap: 5px;
}
.play-btn, .rewind-btn, .forward-btn, .mute-btn, .danmaku-toggle-btn, .title-toggle-btn, .vertical-toggle-btn, .rotate-btn, .reset-btn {
width: 25px;
height: 25px;
font-size: 16px;
}
.video-title {
font-size: 16px;
max-width: calc(100% - 180px);
}
.chapters-container {
max-width: 180px;
}
.time-display {
font-size: 12px;
}
.progress-handle {
width: 16px;
height: 16px;
}
.danmaku-item {
font-size: 14px;
}
}
@media (max-width: 480px) {
.video-controls {
padding: 8px 15px 10px;
}
.time-display {
font-size: 11px;
margin: 0 5px;
}
.chapters-container {
max-width: 150px;
}
.chapter-item {
padding: 6px 8px;
font-size: 12px;
}
.video-title {
font-size: 14px;
max-width: calc(100% - 150px);
}
#danmaku-input {
height: 32px;
font-size: 13px;
}
#send-danmaku {
padding: 2px 8px;
font-size: 11px;
}
.progress-handle {
width: 14px;
height: 14px;
}
.danmaku-item {
font-size: 12px;
}
}
/* 控件隐藏状态 */
.video-controls.hidden {
opacity: 0;
pointer-events: none;
}
.danmaku-container.hidden {
display: none;
}
.swipe-tip {
display: none;
}
.swipe-area {
position: absolute;
top: 0;
left: 0;
width: 40%;
height: 100%;
z-index: 16;
}
</style>
</head>
<body>
<div class="bilibili-player">
<div class="video-container" id="video-container">
<!-- 弹幕容器移到video-wrap上方,确保层级正确 -->
<div class="danmaku-container" id="danmaku-container"></div>
<!-- 视频旋转缩放容器 -->
<div class="video-wrap" id="video-wrap">
<video id="video-player" preload="metadata">
<source src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4" type="video/mp4">
您的浏览器不支持HTML5视频播放
</video>
</div>
<div class="video-title" id="video-title">高仿B站视频播放器 - 测试视频</div>
<div class="swipe-tip">右滑打开选集</div>
<div class="swipe-area" id="swipe-area"></div>
<div class="chapters-container" id="chapters-container">
<button class="chapters-toggle-btn" id="chapters-toggle-btn">
选集
</button>
<div class="chapters-list" id="chapters-list">
<div class="chapter-item active" data-src="http://192.168.0.101:8080/backgrounds/横.mp4">测试视频1 - 这是一个很长的视频标题用来测试文字截断功能</div>
<div class="chapter-item" data-src="http://192.168.0.101:8080/backgrounds/竖.mp4">测试视频2 - 横屏测试视频超长标题展示省略号效果</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频3 - 竖屏测试视频</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频4 - 测试测试测试测试测试测试测试</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频5</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频6</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频7</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频8</div>
</div>
</div>
<div class="video-controls" id="video-controls">
<div class="progress-container" id="progress-container">
<div class="progress-bar" id="progress-bar">
<div class="progress-handle" id="progress-handle"></div>
</div>
</div>
<div class="controls-wrapper">
<div class="controls-row-1">
<div class="left-controls">
<div class="play-control-group">
<button class="rewind-btn" id="rewind-btn">⏪</button>
<button class="play-btn" id="play-btn">▶️</button>
<button class="forward-btn" id="forward-btn">⏩</button>
<button class="mute-btn" id="mute-btn">🔊</button>
<button class="danmaku-toggle-btn" id="danmaku-toggle-btn">💬</button>
<button class="title-toggle-btn" id="title-toggle-btn">📝</button>
<button class="vertical-toggle-btn" id="vertical-toggle-btn">📱</button>
<!-- 旋转缩放控制组 -->
<div class="rotate-scale-controls">
<button class="rotate-btn" id="rotate-btn">🔄</button>
<button class="reset-btn" id="reset-btn">🔧</button>
</div>
</div>
<div class="time-display" id="time-display">00:00 / 00:00</div>
</div>
<div class="right-controls-row1">
<button class="fullscreen-btn" id="fullscreen-btn">⛶</button>
</div>
</div>
<div class="controls-row-2">
<div class="danmaku-input-container">
<input type="text" id="danmaku-input" placeholder="发个友善的弹幕吧~" maxlength="50">
<button id="send-danmaku">发送</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// 核心变量
const video = document.getElementById('video-player');
const playBtn = document.getElementById('play-btn');
const rewindBtn = document.getElementById('rewind-btn');
const forwardBtn = document.getElementById('forward-btn');
const muteBtn = document.getElementById('mute-btn');
const danmakuToggleBtn = document.getElementById('danmaku-toggle-btn');
const progressBar = document.getElementById('progress-bar');
const progressContainer = document.getElementById('progress-container');
const progressHandle = document.getElementById('progress-handle');
const timeDisplay = document.getElementById('time-display');
const danmakuInput = document.getElementById('danmaku-input');
const sendDanmakuBtn = document.getElementById('send-danmaku');
const danmakuContainer = document.getElementById('danmaku-container');
const fullscreenBtn = document.getElementById('fullscreen-btn');
const chapterItems = document.querySelectorAll('.chapter-item');
const chaptersToggleBtn = document.getElementById('chapters-toggle-btn');
const chaptersContainer = document.getElementById('chapters-container');
const chaptersList = document.getElementById('chapters-list');
const videoControls = document.getElementById('video-controls');
const videoContainer = document.getElementById('video-container');
const videoTitle = document.getElementById('video-title');
const titleToggleBtn = document.getElementById('title-toggle-btn');
const swipeArea = document.getElementById('swipe-area');
// 竖屏切换按钮和状态变量
const verticalToggleBtn = document.getElementById('vertical-toggle-btn');
let isVerticalMode = false; // 标记是否为竖屏模式
// 旋转缩放核心变量
const videoWrap = document.getElementById('video-wrap');
const rotateBtn = document.getElementById('rotate-btn');
const resetBtn = document.getElementById('reset-btn');
let rotateDegree = 0; // 任意旋转角度
let scaleRatio = 1; // 缩放比例(1=原始大小,>1放大,<1缩小)
let isDraggingVideo = false; // 是否拖拽视频
let dragStartX = 0; // 拖拽起始X
let dragStartY = 0; // 拖拽起始Y
let videoOffsetX = 0; // 视频偏移X
let videoOffsetY = 0; // 视频偏移Y
// 弹幕相关变量
let danmakuPool = [];
let historyDanmakus = [];
let isVideoPlaying = false;
let isMuted = false;
let isDanmakuVisible = true;
let isControlsVisible = true;
let isTitleVisible = true;
let danmakuSpeed = 8;
let animationFrameId = null;
let controlsTimer = null;
let touchStartX = 0;
let touchEndX = 0;
let touchStartY = 0;
let isChaptersShow = false;
let isDragging = false;
// 模拟历史弹幕数据
function loadHistoryDanmakus() {
historyDanmakus = [
{ text: '前排出售瓜子汽水', time: 2 },
{ text: 'UP主辛苦了!', time: 5 },
{ text: '这个视频太赞了', time: 8 },
{ text: '666666', time: 10 },
{ text: '打卡打卡', time: 15 },
{ text: '为什么没有字幕?', time: 20 },
{ text: '好听!', time: 25 },
{ text: '求BGM', time: 30 },
{ text: '来了来了', time: 35 },
{ text: '一键三连!', time: 40 }
];
}
// 初始化
function initPlayer() {
loadHistoryDanmakus();
// 加载视频时长
video.addEventListener('loadedmetadata', () => {
updateTimeDisplay();
checkHistoryDanmakus();
});
// 播放/暂停和标题显隐
playBtn.addEventListener('click', togglePlayPause);
video.addEventListener('click', (e) => {
toggleTitleVisible();
if (!e.target.closest('.video-controls') && !e.target.closest('.chapters-container')) {
togglePlayPause();
}
});
// 快退5秒
rewindBtn.addEventListener('click', () => {
video.currentTime = Math.max(0, video.currentTime - 5);
updateProgress();
checkHistoryDanmakus();
});
// 快进5秒
forwardBtn.addEventListener('click', () => {
video.currentTime = Math.min(video.duration, video.currentTime + 5);
updateProgress();
checkHistoryDanmakus();
});
// 静音切换
muteBtn.addEventListener('click', toggleMute);
// 弹幕显隐切换
danmakuToggleBtn.addEventListener('click', toggleDanmakuVisible);
// 标题显隐切换
titleToggleBtn.addEventListener('click', toggleTitleVisible);
// 竖屏模式切换
verticalToggleBtn.addEventListener('click', toggleVerticalMode);
// 旋转缩放事件绑定
rotateBtn.addEventListener('click', () => incrementRotate(15)); // 点击旋转15°
resetBtn.addEventListener('click', resetVideoTransform); // 重置按钮
resetBtn.addEventListener('click', () => resetBtn.classList.toggle('active'));
bindDragEvents(); // 绑定拖拽/缩放事件
// 进度条点击跳转
progressContainer.addEventListener('click', (e) => {
seekVideo(e);
checkHistoryDanmakus();
});
// 滑块鼠标按下开始拖动
progressHandle.addEventListener('mousedown', (e) => {
e.preventDefault();
isDragging = true;
document.addEventListener('mousemove', handleDrag);
document.addEventListener('mouseup', () => {
isDragging = false;
document.removeEventListener('mousemove', handleDrag);
});
});
// 移动端触摸拖动支持
progressHandle.addEventListener('touchstart', (e) => {
e.preventDefault();
isDragging = true;
document.addEventListener('touchmove', handleTouchDrag);
document.addEventListener('touchend', () => {
isDragging = false;
document.removeEventListener('touchmove', handleTouchDrag);
});
});
// 视频进度更新
video.addEventListener('timeupdate', () => {
if (!isDragging) {
updateProgress();
}
checkHistoryDanmakus();
});
// 视频结束
video.addEventListener('ended', handleVideoEnd);
// 发送弹幕
sendDanmakuBtn.addEventListener('click', sendDanmaku);
danmakuInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendDanmaku();
});
// 全屏切换
fullscreenBtn.addEventListener('click', toggleFullscreen);
// 视频选集
chapterItems.forEach(item => {
item.addEventListener('click', switchVideoChapter);
});
// 选集按钮点击
chaptersToggleBtn.addEventListener('click', function(e) {
e.stopPropagation();
toggleChaptersList();
});
// 播放状态监听
video.addEventListener('play', () => {
isVideoPlaying = true;
startDanmakuAnimation();
playBtn.textContent = '⏸️';
startControlsTimer();
});
video.addEventListener('pause', () => {
isVideoPlaying = false;
stopDanmakuAnimation();
playBtn.textContent = '▶️';
clearControlsTimer();
});
// 控件显隐
videoContainer.addEventListener('mousemove', showControls);
videoContainer.addEventListener('touchmove', showControls);
// 控件显隐切换
videoContainer.addEventListener('click', (e) => {
if (!e.target.closest('.video-controls') && !e.target.closest('.chapters-container')) {
toggleControlsVisible();
}
});
// 全屏状态监听
document.addEventListener('fullscreenchange', () => {
updateFullscreenBtn();
adjustVerticalModeLayout();
updateVideoTransform(); // 保留旋转缩放状态
});
document.addEventListener('webkitfullscreenchange', () => {
updateFullscreenBtn();
adjustVerticalModeLayout();
updateVideoTransform(); // 保留旋转缩放状态
});
document.addEventListener('msfullscreenchange', () => {
updateFullscreenBtn();
adjustVerticalModeLayout();
updateVideoTransform(); // 保留旋转缩放状态
});
// 滑动事件处理
let isTouching = false;
videoContainer.addEventListener('touchstart', (e) => {
const rect = videoContainer.getBoundingClientRect();
const touchX = e.changedTouches[0].clientX - rect.left;
if (touchX < rect.width * 0.4) {
isTouching = true;
touchStartX = e.changedTouches[0].screenX;
touchStartY = e.changedTouches[0].screenY;
}
showControls();
});
videoContainer.addEventListener('touchmove', (e) => {
if (isTouching) {
touchEndX = e.changedTouches[0].screenX;
}
});
videoContainer.addEventListener('touchend', (e) => {
if (isTouching) {
isTouching = false;
const touchEndY = e.changedTouches[0].screenY;
const yDiff = Math.abs(touchEndY - touchStartY);
if (yDiff < 30) {
const swipeDistance = touchEndX - touchStartX;
if (swipeDistance > 30) {
showChaptersList();
} else if (swipeDistance < -30) {
hideChaptersList();
}
}
}
});
// PC端测试:鼠标拖拽
let isChapterDragging = false;
let startX = 0;
videoContainer.addEventListener('mousedown', (e) => {
const rect = videoContainer.getBoundingClientRect();
const clickX = e.clientX - rect.left;
if (clickX < rect.width * 0.4) {
isChapterDragging = true;
startX = e.clientX;
}
});
videoContainer.addEventListener('mousemove', (e) => {
if (isChapterDragging) {
// 实时显示滑动效果
}
});
videoContainer.addEventListener('mouseup', (e) => {
if (isChapterDragging) {
isChapterDragging = false;
const endX = e.clientX;
const diffX = endX - startX;
if (diffX > 30) {
showChaptersList();
} else if (diffX < -30) {
hideChaptersList();
}
}
});
// 点击空白区域关闭选集
document.addEventListener('click', (e) => {
if (isChaptersShow &&
!e.target.closest('.chapters-container') &&
e.target !== chaptersToggleBtn) {
hideChaptersList();
}
});
// 阻止选集容器内点击冒泡
chaptersContainer.addEventListener('click', (e) => {
e.stopPropagation();
});
console.log('播放器初始化完成,竖屏切换+自由旋转+无限放大+弹幕适配功能已启用');
}
// ========== 竖屏模式切换 ==========
function toggleVerticalMode() {
isVerticalMode = !isVerticalMode;
// 更新按钮样式
if (isVerticalMode) {
verticalToggleBtn.classList.add('active');
videoContainer.classList.add('vertical-mode');
} else {
verticalToggleBtn.classList.remove('active');
videoContainer.classList.remove('vertical-mode');
}
// 调整布局
adjustVerticalModeLayout();
showControls();
console.log(`已切换为${isVerticalMode ? '竖屏' : '横屏'}模式`);
}
// 适配竖屏模式布局
function adjustVerticalModeLayout() {
const isFullscreen = !!document.fullscreenElement;
if (isVerticalMode && isFullscreen) {
videoContainer.style.display = 'flex';
videoContainer.style.justifyContent = 'center';
videoContainer.style.alignItems = 'center';
} else if (isVerticalMode && !isFullscreen) {
videoContainer.style.display = 'block';
videoContainer.style.maxWidth = '500px';
videoContainer.style.margin = '0 auto';
} else {
videoContainer.style.display = 'block';
videoContainer.style.maxWidth = '1200px';
videoContainer.style.margin = '0 auto';
}
// 保留旋转缩放状态
updateVideoTransform();
}
// ========== 自由旋转+无限缩放核心功能 ==========
// 1. 更新视频变换(旋转+缩放+平移)
function updateVideoTransform() {
video.style.transform = `rotate(${rotateDegree}deg) scale(${scaleRatio}) translate(${videoOffsetX}px, ${videoOffsetY}px)`;
// 同步更新弹幕容器(让弹幕跟随视频旋转缩放)
danmakuContainer.style.transform = `rotate(${rotateDegree}deg) scale(${scaleRatio}) translate(${videoOffsetX}px, ${videoOffsetY}px)`;
danmakuContainer.style.transformOrigin = 'center center';
}
// 2. 增量旋转(每次点击旋转15°)
function incrementRotate(step = 15) {
rotateDegree = (rotateDegree + step) % 360;
rotateBtn.textContent = `🔄${Math.round(rotateDegree)}°`;
rotateBtn.classList.toggle('active', rotateDegree !== 0);
updateVideoTransform();
showControls();
console.log(`视频旋转至${rotateDegree}°`);
}
// 3. 滚轮缩放(无限放大/缩小)
function handleVideoWheel(e) {
e.preventDefault();
// 滚轮增量:向上=放大,向下=缩小(0.1为每次缩放步长)
const delta = e.deltaY > 0 ? -0.1 : 0.1;
// 限制最小缩放(避免缩太小),不限制最大缩放(实现无限放大)
scaleRatio = Math.max(0.5, scaleRatio + delta);
updateVideoTransform();
showControls();
console.log(`视频缩放至${(scaleRatio * 100).toFixed(0)}%`);
}
// 4. 拖拽旋转(鼠标/触屏拖拽控制任意角度)
function handleDragRotate(e) {
if (!isDraggingVideo) return;
// 获取鼠标/触屏位置
const clientX = e.type.includes('touch') ? e.changedTouches[0].clientX : e.clientX;
const clientY = e.type.includes('touch') ? e.changedTouches[0].clientY : e.clientY;
// 计算拖拽增量(每移动10px旋转1°,可调整灵敏度)
const deltaX = clientX - dragStartX;
const deltaY = clientY - dragStartY;
const angleDelta = (deltaX + deltaY) / 10;
rotateDegree += angleDelta;
rotateBtn.textContent = `🔄${Math.round(rotateDegree)}°`;
rotateBtn.classList.toggle('active', rotateDegree !== 0);
// 更新起始位置
dragStartX = clientX;
dragStartY = clientY;
updateVideoTransform();
}
// 5. 拖拽平移(放大后调整视频位置)
function handleDragTranslate(e) {
if (!isDraggingVideo || scaleRatio <= 1) return; // 仅放大后可平移
const clientX = e.type.includes('touch') ? e.changedTouches[0].clientX : e.clientX;
const clientY = e.type.includes('touch') ? e.changedTouches[0].clientY : e.clientY;
// 计算偏移量
videoOffsetX += clientX - dragStartX;
videoOffsetY += clientY - dragStartY;
// 更新起始位置
dragStartX = clientX;
dragStartY = clientY;
updateVideoTransform();
}
// 6. 重置所有变换(恢复默认)
function resetVideoTransform() {
rotateDegree = 0;
scaleRatio = 1;
videoOffsetX = 0;
videoOffsetY = 0;
rotateBtn.textContent = '🔄';
rotateBtn.classList.remove('active');
resetBtn.classList.remove('active');
updateVideoTransform();
showControls();
console.log('视频已重置为默认状态');
}
// 7. 绑定拖拽事件(旋转+平移)
function bindDragEvents() {
// 鼠标开始拖拽
videoWrap.addEventListener('mousedown', (e) => {
if (e.button !== 0) return; // 仅左键
isDraggingVideo = true;
dragStartX = e.clientX;
dragStartY = e.clientY;
videoWrap.style.cursor = 'grabbing';
showControls();
});
// 触屏开始拖拽
videoWrap.addEventListener('touchstart', (e) => {
isDraggingVideo = true;
dragStartX = e.changedTouches[0].clientX;
dragStartY = e.changedTouches[0].clientY;
showControls();
});
// 鼠标拖拽移动
document.addEventListener('mousemove', (e) => {
if (isDraggingVideo) {
// 按住Shift键=旋转,否则=平移(和B站逻辑一致)
if (e.shiftKey) {
handleDragRotate(e);
} else {
handleDragTranslate(e);
}
}
});
// 触屏拖拽移动
document.addEventListener('touchmove', (e) => {
if (isDraggingVideo) {
handleDragTranslate(e); // 触屏默认平移,旋转用按钮
}
});
// 结束拖拽
document.addEventListener('mouseup', () => {
isDraggingVideo = false;
videoWrap.style.cursor = 'grab';
});
document.addEventListener('touchend', () => {
isDraggingVideo = false;
});
// 滚轮缩放
videoWrap.addEventListener('wheel', handleVideoWheel);
// 触屏双指缩放(移动端)
let touchDistance = 0;
videoWrap.addEventListener('touchmove', (e) => {
if (e.touches.length === 2) {
e.preventDefault();
// 计算双指距离
const newDistance = Math.hypot(
e.touches[0].clientX - e.touches[1].clientX,
e.touches[0].clientY - e.touches[1].clientY
);
if (touchDistance > 0) {
// 双指张开=放大,闭合=缩小
const delta = (newDistance - touchDistance) / 100;
scaleRatio = Math.max(0.5, scaleRatio + delta);
updateVideoTransform();
}
touchDistance = newDistance;
}
});
videoWrap.addEventListener('touchend', () => {
touchDistance = 0;
});
}
// 进度条拖动处理函数
function handleDrag(e) {
if (!isDragging) return;
const rect = progressContainer.getBoundingClientRect();
let position = (e.clientX - rect.left) / rect.width;
position = Math.max(0, Math.min(1, position));
video.currentTime = position * video.duration;
progressBar.style.width = `${position * 100}%`;
updateTimeDisplay();
}
function handleTouchDrag(e) {
if (!isDragging) return;
const rect = progressContainer.getBoundingClientRect();
const touch = e.changedTouches[0];
let position = (touch.clientX - rect.left) / rect.width;
position = Math.max(0, Math.min(1, position));
video.currentTime = position * video.duration;
progressBar.style.width = `${position * 100}%`;
updateTimeDisplay();
}
// 显示选集列表
function showChaptersList() {
console.log('显示选集列表');
if (!isChaptersShow) {
chaptersContainer.classList.add('show');
isChaptersShow = true;
}
}
// 隐藏选集列表
function hideChaptersList() {
console.log('隐藏选集列表');
if (isChaptersShow) {
chaptersContainer.classList.remove('show');
isChaptersShow = false;
}
}
// 切换选集列表
function toggleChaptersList() {
console.log('切换选集列表状态,当前:', isChaptersShow);
if (isChaptersShow) {
hideChaptersList();
} else {
showChaptersList();
}
showControls();
}
// 播放/暂停切换
function togglePlayPause() {
if (video.paused) {
video.play();
} else {
video.pause();
}
showControls();
}
// 静音切换
function toggleMute() {
isMuted = !isMuted;
video.muted = isMuted;
muteBtn.textContent = isMuted ? '🔇' : '🔊';
showControls();
}
// 弹幕显隐切换
function toggleDanmakuVisible() {
isDanmakuVisible = !isDanmakuVisible;
if (isDanmakuVisible) {
danmakuContainer.classList.remove('hidden');
danmakuToggleBtn.textContent = '💬';
if (isVideoPlaying) startDanmakuAnimation();
} else {
danmakuContainer.classList.add('hidden');
danmakuToggleBtn.textContent = '🚫';
stopDanmakuAnimation();
}
showControls();
}
// 标题显隐切换
function toggleTitleVisible() {
isTitleVisible = !isTitleVisible;
if (isTitleVisible) {
videoTitle.classList.remove('hidden');
titleToggleBtn.textContent = '📝';
} else {
videoTitle.classList.add('hidden');
titleToggleBtn.textContent = '🙈';
}
showControls();
}
// 控件显隐切换
function toggleControlsVisible() {
isControlsVisible = !isControlsVisible;
if (isControlsVisible) {
showControls();
} else {
hideControls();
}
}
// 显示控件
function showControls() {
clearControlsTimer();
isControlsVisible = true;
videoControls.classList.remove('hidden');
if (isVideoPlaying) {
startControlsTimer();
}
}
// 隐藏控件
function hideControls() {
isControlsVisible = false;
videoControls.classList.add('hidden');
}
// 启动控件自动隐藏计时器
function startControlsTimer() {
clearControlsTimer();
controlsTimer = setTimeout(() => {
hideControls();
}, 3000);
}
// 清除控件计时器
function clearControlsTimer() {
if (controlsTimer) {
clearTimeout(controlsTimer);
controlsTimer = null;
}
}
// 检查并显示历史弹幕
function checkHistoryDanmakus() {
if (!isDanmakuVisible) return;
const currentTime = video.currentTime;
const newDanmakus = historyDanmakus.filter(danmaku =>
danmaku.time >= currentTime - 1 && danmaku.time <= currentTime &&
!danmakuPool.some(item => item.text === danmaku.text && item.startTime === danmaku.time)
);
newDanmakus.forEach(danmaku => {
const element = createDanmakuElement(danmaku.text);
danmakuPool.push({
element: element,
startTime: danmaku.time,
text: danmaku.text,
position: 'normal',
speed: danmakuSpeed
});
danmakuContainer.appendChild(element);
if (isVideoPlaying) {
updateDanmakuPosition();
}
});
}
// 视频进度跳转
function seekVideo(e) {
const rect = progressContainer.getBoundingClientRect();
const clickPosition = (e.clientX - rect.left) / rect.width;
video.currentTime = clickPosition * video.duration;
updateProgress();
showControls();
}
// 更新进度条
function updateProgress() {
const progress = (video.currentTime / video.duration) * 100;
progressBar.style.width = `${progress}%`;
updateTimeDisplay();
}
// 更新时间显示
function updateTimeDisplay() {
const currentTime = formatTime(video.currentTime);
const duration = formatTime(video.duration);
timeDisplay.textContent = `${currentTime} / ${duration}`;
}
// 格式化时间
function formatTime(seconds) {
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')}`;
}
// 视频结束处理
function handleVideoEnd() {
playBtn.textContent = '▶️';
isVideoPlaying = false;
stopDanmakuAnimation();
video.currentTime = 0;
updateProgress();
clearControlsTimer();
}
// 发送弹幕 - 修复显示问题
function sendDanmaku() {
const text = danmakuInput.value.trim();
if (!text) return;
const danmaku = createDanmakuElement(text);
const newDanmaku = {
element: danmaku,
startTime: video.currentTime,
text: text,
position: 'normal',
speed: danmakuSpeed
};
danmakuPool.push(newDanmaku);
historyDanmakus.push({
text: text,
time: video.currentTime
});
// 强制刷新DOM后添加弹幕
setTimeout(() => {
danmakuContainer.appendChild(danmaku);
if (isVideoPlaying && isDanmakuVisible) {
updateDanmakuPosition();
}
}, 0);
danmakuInput.value = '';
showControls();
}
// 创建弹幕元素 - 修复位置计算
function createDanmakuElement(text) {
const danmaku = document.createElement('div');
danmaku.className = 'danmaku-item';
danmaku.textContent = text;
// 修复旋转缩放后弹幕位置
const containerRect = danmakuContainer.getBoundingClientRect();
const top = Math.floor(Math.random() * 80) + 5;
danmaku.style.top = `${top}%`;
danmaku.style.right = `-${text.length * 18}px`; // 根据文字长度计算初始位置
danmaku.style.transform = 'translateZ(0)'; // 硬件加速
return danmaku;
}
// 开始弹幕动画
function startDanmakuAnimation() {
if (!animationFrameId && isVideoPlaying && isDanmakuVisible) {
animationFrameId = requestAnimationFrame(updateDanmakuPosition);
}
}
// 停止弹幕动画
function stopDanmakuAnimation() {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
animationFrameId = null;
}
}
// 更新弹幕位置 - 适配旋转缩放
function updateDanmakuPosition() {
if (!isVideoPlaying || !isDanmakuVisible) return;
// 获取原始容器尺寸(不包含变换)
const containerWidth = danmakuContainer.offsetWidth / scaleRatio;
const containerHeight = danmakuContainer.offsetHeight / scaleRatio;
for (let i = danmakuPool.length - 1; i >= 0; i--) {
const danmaku = danmakuPool[i];
const element = danmaku.element;
const elapsedTime = video.currentTime - danmaku.startTime;
const progress = elapsedTime / danmakuSpeed;
const xPosition = containerWidth * (1 - progress);
if (progress >= 1) {
element.remove();
danmakuPool.splice(i, 1);
} else {
// 适配旋转缩放后的弹幕位置
element.style.transform = `translateX(${xPosition - containerWidth}px)`;
}
}
animationFrameId = requestAnimationFrame(updateDanmakuPosition);
}
// 切换全屏
function toggleFullscreen() {
const container = document.querySelector('.video-container');
if (!document.fullscreenElement) {
if (container.requestFullscreen) {
container.requestFullscreen();
} else if (container.webkitRequestFullscreen) {
container.webkitRequestFullscreen();
} else if (container.msRequestFullscreen) {
container.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
showControls();
}
// 更新全屏按钮显示
function updateFullscreenBtn() {
if (document.fullscreenElement) {
fullscreenBtn.textContent = '🔙';
} else {
fullscreenBtn.textContent = '⛶';
}
}
// 切换视频选集
function switchVideoChapter(e) {
chapterItems.forEach(item => item.classList.remove('active'));
e.target.classList.add('active');
const videoSrc = e.target.dataset.src;
const chapterTitle = e.target.textContent;
videoTitle.textContent = `高仿B站视频播放器 - ${chapterTitle}`;
video.pause();
clearAllDanmakus();
video.src = videoSrc;
video.load();
playBtn.textContent = '▶️';
progressBar.style.width = '0%';
timeDisplay.textContent = '00:00 / 00:00';
hideChaptersList();
showControls();
// 自动播放
video.addEventListener('loadeddata', function playNewVideo() {
video.play();
video.removeEventListener('loadeddata', playNewVideo);
});
}
// 清空所有弹幕
function clearAllDanmakus() {
danmakuPool = [];
danmakuContainer.innerHTML = '';
stopDanmakuAnimation();
}
// 初始化播放器
window.addEventListener('DOMContentLoaded', initPlayer);
</script>
</body>
</html>
html
复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>高仿B站视频播放器(带竖屏切换)</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft Yahei", sans-serif;
}
body {
background-color: #000;
color: #fff;
overflow-x: hidden;
}
/* 播放器容器 */
.bilibili-player {
position: relative;
width: 100%;
max-width: 1200px;
margin: 0 auto;
background-color: #000;
}
/* 视频容器 - 新增竖屏模式样式 */
.video-container {
position: relative;
width: 100%;
aspect-ratio: 16/13;
overflow: hidden;
/* 竖屏模式切换过渡 */
transition: all 0.3s ease;
}
/* 竖屏模式 - 核心样式 */
.video-container.vertical-mode {
aspect-ratio: 9/16;
max-width: 500px;
margin: 0 auto;
}
/* 全屏状态下的竖屏模式 */
.video-container:fullscreen.vertical-mode,
.video-container:-webkit-full-screen.vertical-mode,
.video-container:-ms-fullscreen.vertical-mode {
width: 100vw !important;
height: 100vh !important;
max-width: none !important;
display: flex;
justify-content: center;
align-items: center;
}
/* 视频元素 - 适配竖屏模式 */
#video-player {
width: 100%;
height: 100%;
object-fit: contain;
background-color: #000;
/* 竖屏视频全屏时保持比例 */
max-width: 100%;
max-height: 100%;
}
/* 全屏竖屏模式下的视频 */
.video-container:fullscreen.vertical-mode #video-player,
.video-container:-webkit-full-screen.vertical-mode #video-player,
.video-container:-ms-fullscreen.vertical-mode #video-player {
aspect-ratio: 9/16;
}
/* 全屏横屏模式下的视频 */
.video-container:fullscreen:not(.vertical-mode) #video-player,
.video-container:-webkit-full-screen:not(.vertical-mode) #video-player,
.video-container:-ms-fullscreen:not(.vertical-mode) #video-player {
aspect-ratio: 16/9;
max-width: 100vw;
max-height: 100vh;
}
/* 弹幕容器 - 适配竖屏模式 */
.danmaku-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 85%;
pointer-events: none;
overflow: hidden;
/* 跟随视频容器比例变化 */
transition: all 0.3s ease;
}
/* 竖屏模式下的弹幕容器 */
.video-container.vertical-mode .danmaku-container {
aspect-ratio: 9/16;
max-width: 500px;
margin: 0 auto;
}
/* 全屏竖屏模式下的弹幕容器 */
.video-container:fullscreen.vertical-mode .danmaku-container,
.video-container:-webkit-full-screen.vertical-mode .danmaku-container,
.video-container:-ms-fullscreen.vertical-mode .danmaku-container {
aspect-ratio: 9/16;
width: 100%;
height: 100%;
max-width: 100%;
}
/* 单个弹幕样式 */
.danmaku-item {
position: absolute;
color: #fff;
font-size: 18px;
white-space: nowrap;
text-shadow: 0 0 2px #000;
z-index: 10;
will-change: transform;
animation-timing-function: linear;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}
/* 竖屏模式下的弹幕字体适配 */
.video-container.vertical-mode .danmaku-item {
font-size: 16px;
max-width: 80%;
}
/* 视频标题 - 覆盖在视频上方 */
.video-title {
position: absolute;
top: 10px;
left: 10px;
z-index: 15;
padding: 5px 10px;
font-size: 18px;
font-weight: bold;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 4px;
max-width: calc(100% - 220px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: opacity 0.3s ease;
}
/* 竖屏模式下的标题适配 */
.video-container.vertical-mode .video-title {
max-width: calc(100% - 180px);
font-size: 16px;
}
/* 全屏竖屏模式下的标题 */
.video-container:fullscreen.vertical-mode .video-title {
max-width: calc(100% - 100px);
font-size: 18px;
top: 20px;
left: 20px;
}
/* 标题隐藏样式 */
.video-title.hidden {
opacity: 0;
pointer-events: none;
}
/* 控制器样式 - 重构布局 */
.video-controls {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background: linear-gradient(transparent, rgba(0,0,0,0.8));
padding: 10px 20px 15px;
transition: all 0.3s ease;
z-index: 20;
}
/* 竖屏模式下的控制器适配 */
.video-container.vertical-mode .video-controls {
max-width: 500px;
margin: 0 auto;
}
/* 全屏竖屏模式下的控制器 */
.video-container:fullscreen.vertical-mode .video-controls {
max-width: 100%;
width: 100%;
padding: 15px 20px 20px;
}
/* 进度条容器 */
.progress-container {
width: 100%;
height: 4px;
background-color: rgba(255,255,255,0.3);
border-radius: 2px;
margin-bottom: 10px;
cursor: pointer;
position: relative;
}
/* 进度条 */
.progress-bar {
height: 100%;
background-color: #fb7299;
border-radius: 2px;
width: 0%;
position: relative;
}
/* 进度条滑块 */
.progress-handle {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
border: none;
background: url('http://192.168.0.101:8080/backgrounds/HIA.png') center center no-repeat;
background-size: contain;
opacity: 1;
transition: opacity 0.2s ease;
cursor: grab;
z-index: 1;
}
.progress-handle:active {
cursor: grabbing;
}
.progress-container:hover .progress-handle {
opacity: 1;
}
/* 控制按钮区域 */
.controls-wrapper {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.controls-row-1 {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.left-controls {
display: flex;
align-items: center;
gap: 15px;
}
.play-control-group {
display: flex;
align-items: center;
gap: 10px;
}
/* 播放控制按钮 */
.play-btn, .rewind-btn, .forward-btn, .mute-btn, .danmaku-toggle-btn, .title-toggle-btn, .vertical-toggle-btn {
width: 30px;
height: 30px;
background: none;
border: none;
color: #fff;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
/* 竖屏按钮高亮样式 */
.vertical-toggle-btn.active {
color: #fb7299;
transform: scale(1.1);
}
.time-display {
font-size: 14px;
color: #fff;
margin: 0 10px;
}
.right-controls-row1 {
display: flex;
align-items: center;
gap: 15px;
}
.controls-row-2 {
display: flex;
width: 100%;
gap: 10px;
}
.danmaku-input-container {
flex: 1;
width: 100%;
position: relative;
}
#danmaku-input {
width: 100%;
height: 36px;
background-color: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.3);
border-radius: 18px;
padding: 0 15px;
color: #fff;
outline: none;
font-size: 14px;
}
#send-danmaku {
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%);
background-color: #fb7299;
border: none;
color: #fff;
padding: 4px 12px;
border-radius: 12px;
cursor: pointer;
font-size: 12px;
white-space: nowrap;
}
.fullscreen-btn {
background: none;
border: none;
color: #fff;
font-size: 18px;
cursor: pointer;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
/* 视频选集 */
.chapters-container {
position: absolute;
top: 10px;
left: 0;
z-index: 999;
max-width: 200px;
height: calc(100% - 80px);
transform: translateX(-100%);
transition: transform 0.3s ease-out;
background-color: rgba(0,0,0,0.95);
border-radius: 0 8px 8px 0;
overflow: hidden;
box-shadow: 2px 0 10px rgba(0,0,0,0.5);
}
/* 竖屏模式下的选集容器适配 */
.video-container.vertical-mode .chapters-container {
max-width: 180px;
}
/* 全屏竖屏模式下的选集容器 */
.video-container:fullscreen.vertical-mode .chapters-container {
max-width: 250px;
height: calc(100% - 100px);
}
.chapters-container.show {
transform: translateX(0);
}
.chapters-toggle-btn {
position: absolute;
right: -40px;
top: 50%;
transform: translateY(-50%);
background-color: rgba(0,0,0,0.8);
color: #fff;
border: 1px solid rgba(255,255,255,0.3);
border-radius: 0 4px 4px 0;
padding: 8px 8px;
cursor: pointer;
font-size: 14px;
z-index: 1000;
}
/* 全屏竖屏模式下的选集按钮 */
.video-container:fullscreen.vertical-mode .chapters-toggle-btn {
padding: 10px 10px;
font-size: 16px;
right: -50px;
}
.chapters-list {
width: 100%;
height: 100%;
overflow-y: auto;
padding-top: 10px;
scrollbar-width: thin;
scrollbar-color: #fb7299 transparent;
}
.chapters-list::-webkit-scrollbar {
width: 4px;
}
.chapters-list::-webkit-scrollbar-thumb {
background-color: #fb7299;
border-radius: 2px;
}
.chapter-item {
padding: 8px 10px;
cursor: pointer;
border-bottom: 1px solid rgba(255,255,255,0.1);
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 竖屏模式下的选集项 */
.video-container.vertical-mode .chapter-item {
padding: 7px 9px;
font-size: 13px;
}
/* 全屏竖屏模式下的选集项 */
.video-container:fullscreen.vertical-mode .chapter-item {
padding: 10px 15px;
font-size: 16px;
}
.chapter-item:last-child {
border-bottom: none;
}
.chapter-item.active {
background-color: #fb7299;
color: #fff;
}
.chapter-item:hover {
background-color: rgba(255,255,255,0.1);
}
/* 手机端适配 */
@media (max-width: 768px) {
.play-control-group {
gap: 5px;
}
.play-btn, .rewind-btn, .forward-btn, .mute-btn, .danmaku-toggle-btn, .title-toggle-btn, .vertical-toggle-btn {
width: 25px;
height: 25px;
font-size: 16px;
}
.video-title {
font-size: 16px;
max-width: calc(100% - 180px);
}
.chapters-container {
max-width: 180px;
}
.time-display {
font-size: 12px;
}
.progress-handle {
width: 16px;
height: 16px;
}
}
@media (max-width: 480px) {
.video-controls {
padding: 8px 15px 10px;
}
.time-display {
font-size: 11px;
margin: 0 5px;
}
.chapters-container {
max-width: 150px;
}
.chapter-item {
padding: 6px 8px;
font-size: 12px;
}
.video-title {
font-size: 14px;
max-width: calc(100% - 150px);
}
#danmaku-input {
height: 32px;
font-size: 13px;
}
#send-danmaku {
padding: 2px 8px;
font-size: 11px;
}
.progress-handle {
width: 14px;
height: 14px;
}
}
/* 控件隐藏状态 */
.video-controls.hidden {
opacity: 0;
pointer-events: none;
}
.danmaku-container.hidden {
display: none;
}
.swipe-tip {
display: none;
}
.swipe-area {
position: absolute;
top: 0;
left: 0;
width: 40%;
height: 100%;
z-index: 16;
}
</style>
</head>
<body>
<div class="bilibili-player">
<div class="video-container" id="video-container">
<div class="danmaku-container" id="danmaku-container"></div>
<video id="video-player" preload="metadata">
<source src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4" type="video/mp4">
您的浏览器不支持HTML5视频播放
</video>
<div class="video-title" id="video-title">高仿B站视频播放器 - 测试视频</div>
<div class="swipe-tip">右滑打开选集</div>
<div class="swipe-area" id="swipe-area"></div>
<div class="chapters-container" id="chapters-container">
<button class="chapters-toggle-btn" id="chapters-toggle-btn">
选集
</button>
<div class="chapters-list" id="chapters-list">
<div class="chapter-item active" data-src="http://192.168.0.101:8080/backgrounds/横.mp4">测试视频1 - 这是一个很长的视频标题用来测试文字截断功能</div>
<div class="chapter-item" data-src="http://192.168.0.101:8080/backgrounds/竖.mp4">测试视频2 - 横屏测试视频超长标题展示省略号效果</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频3 - 竖屏测试视频</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频4 - 测试测试测试测试测试测试测试</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频5</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频6</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频7</div>
<div class="chapter-item" data-src="https://vod.pipi.cn/fec9203cvodtransbj1251246104/aa5308fc5285890804986750388/v.f42906.mp4">测试视频8</div>
</div>
</div>
<div class="video-controls" id="video-controls">
<div class="progress-container" id="progress-container">
<div class="progress-bar" id="progress-bar">
<div class="progress-handle" id="progress-handle"></div>
</div>
</div>
<div class="controls-wrapper">
<div class="controls-row-1">
<div class="left-controls">
<div class="play-control-group">
<button class="rewind-btn" id="rewind-btn">⏪</button>
<button class="play-btn" id="play-btn">▶️</button>
<button class="forward-btn" id="forward-btn">⏩</button>
<button class="mute-btn" id="mute-btn">🔊</button>
<button class="danmaku-toggle-btn" id="danmaku-toggle-btn">💬</button>
<button class="title-toggle-btn" id="title-toggle-btn">📝</button>
<!-- 新增:竖屏切换按钮 -->
<button class="vertical-toggle-btn" id="vertical-toggle-btn">📱</button>
</div>
<div class="time-display" id="time-display">00:00 / 00:00</div>
</div>
<div class="right-controls-row1">
<button class="fullscreen-btn" id="fullscreen-btn">⛶</button>
</div>
</div>
<div class="controls-row-2">
<div class="danmaku-input-container">
<input type="text" id="danmaku-input" placeholder="发个友善的弹幕吧~" maxlength="50">
<button id="send-danmaku">发送</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// 核心变量
const video = document.getElementById('video-player');
const playBtn = document.getElementById('play-btn');
const rewindBtn = document.getElementById('rewind-btn');
const forwardBtn = document.getElementById('forward-btn');
const muteBtn = document.getElementById('mute-btn');
const danmakuToggleBtn = document.getElementById('danmaku-toggle-btn');
const progressBar = document.getElementById('progress-bar');
const progressContainer = document.getElementById('progress-container');
const progressHandle = document.getElementById('progress-handle');
const timeDisplay = document.getElementById('time-display');
const danmakuInput = document.getElementById('danmaku-input');
const sendDanmakuBtn = document.getElementById('send-danmaku');
const danmakuContainer = document.getElementById('danmaku-container');
const fullscreenBtn = document.getElementById('fullscreen-btn');
const chapterItems = document.querySelectorAll('.chapter-item');
const chaptersToggleBtn = document.getElementById('chapters-toggle-btn');
const chaptersContainer = document.getElementById('chapters-container');
const chaptersList = document.getElementById('chapters-list');
const videoControls = document.getElementById('video-controls');
const videoContainer = document.getElementById('video-container');
const videoTitle = document.getElementById('video-title');
const titleToggleBtn = document.getElementById('title-toggle-btn');
const swipeArea = document.getElementById('swipe-area');
// 新增:竖屏切换按钮和状态变量
const verticalToggleBtn = document.getElementById('vertical-toggle-btn');
let isVerticalMode = false; // 标记是否为竖屏模式
// 弹幕相关变量
let danmakuPool = [];
let historyDanmakus = [];
let isVideoPlaying = false;
let isMuted = false;
let isDanmakuVisible = true;
let isControlsVisible = true;
let isTitleVisible = true;
let danmakuSpeed = 8;
let animationFrameId = null;
let controlsTimer = null;
let touchStartX = 0;
let touchEndX = 0;
let touchStartY = 0;
let isChaptersShow = false;
let isDragging = false;
// 模拟历史弹幕数据
function loadHistoryDanmakus() {
historyDanmakus = [
{ text: '前排出售瓜子汽水', time: 2 },
{ text: 'UP主辛苦了!', time: 5 },
{ text: '这个视频太赞了', time: 8 },
{ text: '666666', time: 10 },
{ text: '打卡打卡', time: 15 },
{ text: '为什么没有字幕?', time: 20 },
{ text: '好听!', time: 25 },
{ text: '求BGM', time: 30 },
{ text: '来了来了', time: 35 },
{ text: '一键三连!', time: 40 }
];
}
// 初始化
function initPlayer() {
loadHistoryDanmakus();
// 加载视频时长
video.addEventListener('loadedmetadata', () => {
updateTimeDisplay();
checkHistoryDanmakus();
});
// 播放/暂停和标题显隐
playBtn.addEventListener('click', togglePlayPause);
video.addEventListener('click', (e) => {
toggleTitleVisible();
if (!e.target.closest('.video-controls') && !e.target.closest('.chapters-container')) {
togglePlayPause();
}
});
// 快退5秒
rewindBtn.addEventListener('click', () => {
video.currentTime = Math.max(0, video.currentTime - 5);
updateProgress();
checkHistoryDanmakus();
});
// 快进5秒
forwardBtn.addEventListener('click', () => {
video.currentTime = Math.min(video.duration, video.currentTime + 5);
updateProgress();
checkHistoryDanmakus();
});
// 静音切换
muteBtn.addEventListener('click', toggleMute);
// 弹幕显隐切换
danmakuToggleBtn.addEventListener('click', toggleDanmakuVisible);
// 标题显隐切换
titleToggleBtn.addEventListener('click', toggleTitleVisible);
// 新增:竖屏模式切换
verticalToggleBtn.addEventListener('click', toggleVerticalMode);
// 进度条点击跳转
progressContainer.addEventListener('click', (e) => {
seekVideo(e);
checkHistoryDanmakus();
});
// 滑块鼠标按下开始拖动
progressHandle.addEventListener('mousedown', (e) => {
e.preventDefault();
isDragging = true;
document.addEventListener('mousemove', handleDrag);
document.addEventListener('mouseup', () => {
isDragging = false;
document.removeEventListener('mousemove', handleDrag);
});
});
// 移动端触摸拖动支持
progressHandle.addEventListener('touchstart', (e) => {
e.preventDefault();
isDragging = true;
document.addEventListener('touchmove', handleTouchDrag);
document.addEventListener('touchend', () => {
isDragging = false;
document.removeEventListener('touchmove', handleTouchDrag);
});
});
// 视频进度更新
video.addEventListener('timeupdate', () => {
if (!isDragging) {
updateProgress();
}
checkHistoryDanmakus();
});
// 视频结束
video.addEventListener('ended', handleVideoEnd);
// 发送弹幕
sendDanmakuBtn.addEventListener('click', sendDanmaku);
danmakuInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendDanmaku();
});
// 全屏切换
fullscreenBtn.addEventListener('click', toggleFullscreen);
// 视频选集
chapterItems.forEach(item => {
item.addEventListener('click', switchVideoChapter);
});
// 选集按钮点击
chaptersToggleBtn.addEventListener('click', function(e) {
e.stopPropagation();
toggleChaptersList();
});
// 播放状态监听
video.addEventListener('play', () => {
isVideoPlaying = true;
startDanmakuAnimation();
playBtn.textContent = '⏸️';
startControlsTimer();
});
video.addEventListener('pause', () => {
isVideoPlaying = false;
stopDanmakuAnimation();
playBtn.textContent = '▶️';
clearControlsTimer();
});
// 控件显隐
videoContainer.addEventListener('mousemove', showControls);
videoContainer.addEventListener('touchmove', showControls);
// 控件显隐切换
videoContainer.addEventListener('click', (e) => {
if (!e.target.closest('.video-controls') && !e.target.closest('.chapters-container')) {
toggleControlsVisible();
}
});
// 全屏状态监听
document.addEventListener('fullscreenchange', () => {
updateFullscreenBtn();
// 全屏变化时重新适配竖屏模式
adjustVerticalModeLayout();
});
document.addEventListener('webkitfullscreenchange', () => {
updateFullscreenBtn();
adjustVerticalModeLayout();
});
document.addEventListener('msfullscreenchange', () => {
updateFullscreenBtn();
adjustVerticalModeLayout();
});
// 滑动事件处理
let isTouching = false;
videoContainer.addEventListener('touchstart', (e) => {
const rect = videoContainer.getBoundingClientRect();
const touchX = e.changedTouches[0].clientX - rect.left;
if (touchX < rect.width * 0.4) {
isTouching = true;
touchStartX = e.changedTouches[0].screenX;
touchStartY = e.changedTouches[0].screenY;
}
showControls();
});
videoContainer.addEventListener('touchmove', (e) => {
if (isTouching) {
touchEndX = e.changedTouches[0].screenX;
}
});
videoContainer.addEventListener('touchend', (e) => {
if (isTouching) {
isTouching = false;
const touchEndY = e.changedTouches[0].screenY;
const yDiff = Math.abs(touchEndY - touchStartY);
if (yDiff < 30) {
const swipeDistance = touchEndX - touchStartX;
if (swipeDistance > 30) {
showChaptersList();
} else if (swipeDistance < -30) {
hideChaptersList();
}
}
}
});
// PC端测试:鼠标拖拽
let isChapterDragging = false;
let startX = 0;
videoContainer.addEventListener('mousedown', (e) => {
const rect = videoContainer.getBoundingClientRect();
const clickX = e.clientX - rect.left;
if (clickX < rect.width * 0.4) {
isChapterDragging = true;
startX = e.clientX;
}
});
videoContainer.addEventListener('mousemove', (e) => {
if (isChapterDragging) {
// 实时显示滑动效果
}
});
videoContainer.addEventListener('mouseup', (e) => {
if (isChapterDragging) {
isChapterDragging = false;
const endX = e.clientX;
const diffX = endX - startX;
if (diffX > 30) {
showChaptersList();
} else if (diffX < -30) {
hideChaptersList();
}
}
});
// 点击空白区域关闭选集
document.addEventListener('click', (e) => {
if (isChaptersShow &&
!e.target.closest('.chapters-container') &&
e.target !== chaptersToggleBtn) {
hideChaptersList();
}
});
// 阻止选集容器内点击冒泡
chaptersContainer.addEventListener('click', (e) => {
e.stopPropagation();
});
console.log('播放器初始化完成,竖屏切换功能已启用');
}
// ========== 新增核心功能:竖屏模式切换 ==========
function toggleVerticalMode() {
isVerticalMode = !isVerticalMode;
// 更新按钮样式
if (isVerticalMode) {
verticalToggleBtn.classList.add('active');
videoContainer.classList.add('vertical-mode');
} else {
verticalToggleBtn.classList.remove('active');
videoContainer.classList.remove('vertical-mode');
}
// 调整布局
adjustVerticalModeLayout();
showControls();
console.log(`已切换为${isVerticalMode ? '竖屏' : '横屏'}模式`);
}
// 适配竖屏模式布局
function adjustVerticalModeLayout() {
const isFullscreen = !!document.fullscreenElement;
if (isVerticalMode && isFullscreen) {
// 全屏竖屏模式:居中显示,保持9:16比例
videoContainer.style.display = 'flex';
videoContainer.style.justifyContent = 'center';
videoContainer.style.alignItems = 'center';
} else if (isVerticalMode && !isFullscreen) {
// 非全屏竖屏模式:限制宽度,垂直布局
videoContainer.style.display = 'block';
videoContainer.style.maxWidth = '500px';
videoContainer.style.margin = '0 auto';
} else {
// 横屏模式:恢复默认
videoContainer.style.display = 'block';
videoContainer.style.maxWidth = '1200px';
videoContainer.style.margin = '0 auto';
}
}
// 进度条拖动处理函数
function handleDrag(e) {
if (!isDragging) return;
const rect = progressContainer.getBoundingClientRect();
let position = (e.clientX - rect.left) / rect.width;
position = Math.max(0, Math.min(1, position));
video.currentTime = position * video.duration;
progressBar.style.width = `${position * 100}%`;
updateTimeDisplay();
}
function handleTouchDrag(e) {
if (!isDragging) return;
const rect = progressContainer.getBoundingClientRect();
const touch = e.changedTouches[0];
let position = (touch.clientX - rect.left) / rect.width;
position = Math.max(0, Math.min(1, position));
video.currentTime = position * video.duration;
progressBar.style.width = `${position * 100}%`;
updateTimeDisplay();
}
// 显示选集列表
function showChaptersList() {
console.log('显示选集列表');
if (!isChaptersShow) {
chaptersContainer.classList.add('show');
isChaptersShow = true;
}
}
// 隐藏选集列表
function hideChaptersList() {
console.log('隐藏选集列表');
if (isChaptersShow) {
chaptersContainer.classList.remove('show');
isChaptersShow = false;
}
}
// 切换选集列表
function toggleChaptersList() {
console.log('切换选集列表状态,当前:', isChaptersShow);
if (isChaptersShow) {
hideChaptersList();
} else {
showChaptersList();
}
showControls();
}
// 播放/暂停切换
function togglePlayPause() {
if (video.paused) {
video.play();
} else {
video.pause();
}
showControls();
}
// 静音切换
function toggleMute() {
isMuted = !isMuted;
video.muted = isMuted;
muteBtn.textContent = isMuted ? '🔇' : '🔊';
showControls();
}
// 弹幕显隐切换
function toggleDanmakuVisible() {
isDanmakuVisible = !isDanmakuVisible;
if (isDanmakuVisible) {
danmakuContainer.classList.remove('hidden');
danmakuToggleBtn.textContent = '💬';
if (isVideoPlaying) startDanmakuAnimation();
} else {
danmakuContainer.classList.add('hidden');
danmakuToggleBtn.textContent = '🚫';
stopDanmakuAnimation();
}
showControls();
}
// 标题显隐切换
function toggleTitleVisible() {
isTitleVisible = !isTitleVisible;
if (isTitleVisible) {
videoTitle.classList.remove('hidden');
titleToggleBtn.textContent = '📝';
} else {
videoTitle.classList.add('hidden');
titleToggleBtn.textContent = '🙈';
}
showControls();
}
// 控件显隐切换
function toggleControlsVisible() {
isControlsVisible = !isControlsVisible;
if (isControlsVisible) {
showControls();
} else {
hideControls();
}
}
// 显示控件
function showControls() {
clearControlsTimer();
isControlsVisible = true;
videoControls.classList.remove('hidden');
if (isVideoPlaying) {
startControlsTimer();
}
}
// 隐藏控件
function hideControls() {
isControlsVisible = false;
videoControls.classList.add('hidden');
}
// 启动控件自动隐藏计时器
function startControlsTimer() {
clearControlsTimer();
controlsTimer = setTimeout(() => {
hideControls();
}, 3000);
}
// 清除控件计时器
function clearControlsTimer() {
if (controlsTimer) {
clearTimeout(controlsTimer);
controlsTimer = null;
}
}
// 检查并显示历史弹幕
function checkHistoryDanmakus() {
if (!isDanmakuVisible) return;
const currentTime = video.currentTime;
const newDanmakus = historyDanmakus.filter(danmaku =>
danmaku.time >= currentTime - 1 && danmaku.time <= currentTime &&
!danmakuPool.some(item => item.text === danmaku.text && item.startTime === danmaku.time)
);
newDanmakus.forEach(danmaku => {
const element = createDanmakuElement(danmaku.text);
danmakuPool.push({
element: element,
startTime: danmaku.time,
text: danmaku.text,
position: 'normal',
speed: danmakuSpeed
});
danmakuContainer.appendChild(element);
if (isVideoPlaying) {
updateDanmakuPosition();
}
});
}
// 视频进度跳转
function seekVideo(e) {
const rect = progressContainer.getBoundingClientRect();
const clickPosition = (e.clientX - rect.left) / rect.width;
video.currentTime = clickPosition * video.duration;
updateProgress();
showControls();
}
// 更新进度条
function updateProgress() {
const progress = (video.currentTime / video.duration) * 100;
progressBar.style.width = `${progress}%`;
updateTimeDisplay();
}
// 更新时间显示
function updateTimeDisplay() {
const currentTime = formatTime(video.currentTime);
const duration = formatTime(video.duration);
timeDisplay.textContent = `${currentTime} / ${duration}`;
}
// 格式化时间
function formatTime(seconds) {
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')}`;
}
// 视频结束处理
function handleVideoEnd() {
playBtn.textContent = '▶️';
isVideoPlaying = false;
stopDanmakuAnimation();
video.currentTime = 0;
updateProgress();
clearControlsTimer();
}
// 发送弹幕
function sendDanmaku() {
const text = danmakuInput.value.trim();
if (!text) return;
const danmaku = createDanmakuElement(text);
const newDanmaku = {
element: danmaku,
startTime: video.currentTime,
text: text,
position: 'normal',
speed: danmakuSpeed
};
danmakuPool.push(newDanmaku);
historyDanmakus.push({
text: text,
time: video.currentTime
});
danmakuContainer.appendChild(danmaku);
danmakuInput.value = '';
if (isVideoPlaying && isDanmakuVisible) {
updateDanmakuPosition();
}
showControls();
}
// 创建弹幕元素
function createDanmakuElement(text) {
const danmaku = document.createElement('div');
danmaku.className = 'danmaku-item';
danmaku.textContent = text;
const top = Math.floor(Math.random() * 70) + 5;
danmaku.style.top = `${top}%`;
danmaku.style.right = `-${danmaku.offsetWidth || 100}px`;
return danmaku;
}
// 开始弹幕动画
function startDanmakuAnimation() {
if (!animationFrameId && isVideoPlaying && isDanmakuVisible) {
animationFrameId = requestAnimationFrame(updateDanmakuPosition);
}
}
// 停止弹幕动画
function stopDanmakuAnimation() {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
animationFrameId = null;
}
}
// 更新弹幕位置
function updateDanmakuPosition() {
if (!isVideoPlaying || !isDanmakuVisible) return;
const containerWidth = danmakuContainer.offsetWidth;
for (let i = danmakuPool.length - 1; i >= 0; i--) {
const danmaku = danmakuPool[i];
const element = danmaku.element;
const elapsedTime = video.currentTime - danmaku.startTime;
const progress = elapsedTime / danmakuSpeed;
const xPosition = containerWidth * (1 - progress);
if (progress >= 1) {
element.remove();
danmakuPool.splice(i, 1);
} else {
element.style.transform = `translateX(${xPosition - containerWidth}px)`;
}
}
animationFrameId = requestAnimationFrame(updateDanmakuPosition);
}
// 切换全屏
function toggleFullscreen() {
const container = document.querySelector('.video-container');
if (!document.fullscreenElement) {
if (container.requestFullscreen) {
container.requestFullscreen();
} else if (container.webkitRequestFullscreen) {
container.webkitRequestFullscreen();
} else if (container.msRequestFullscreen) {
container.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
showControls();
}
// 更新全屏按钮显示
function updateFullscreenBtn() {
if (document.fullscreenElement) {
fullscreenBtn.textContent = '🔙';
} else {
fullscreenBtn.textContent = '⛶';
}
}
// 切换视频选集
function switchVideoChapter(e) {
chapterItems.forEach(item => item.classList.remove('active'));
e.target.classList.add('active');
const videoSrc = e.target.dataset.src;
const chapterTitle = e.target.textContent;
videoTitle.textContent = `高仿B站视频播放器 - ${chapterTitle}`;
video.pause();
clearAllDanmakus();
video.src = videoSrc;
video.load();
playBtn.textContent = '▶️';
progressBar.style.width = '0%';
timeDisplay.textContent = '00:00 / 00:00';
hideChaptersList();
showControls();
// 自动播放
video.addEventListener('loadeddata', function playNewVideo() {
video.play();
video.removeEventListener('loadeddata', playNewVideo);
});
}
// 清空所有弹幕
function clearAllDanmakus() {
danmakuPool = [];
danmakuContainer.innerHTML = '';
stopDanmakuAnimation();
}
// 初始化播放器
window.addEventListener('DOMContentLoaded', initPlayer);
</script>
</body>
</html>