大前端(原生开发的尽头是html css js)

支持放大视频和旋转

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>
相关推荐
Arya_aa2 小时前
CSS中的列表样式
css
低保和光头哪个先来2 小时前
TinyEditor 篇2:剪贴板粘贴图片并同步上传至服务器
服务器·前端·javascript·css·vue.js
无巧不成书02182 小时前
React Native 深度解析:跨平台移动开发框架(2026实战版)
javascript·react native·react.js
青柠代码录2 小时前
【Vue3】SCSS 基础篇
前端·scss
为美好的生活献上中指2 小时前
*Java 沉淀重走长征路*之——《Java Web 应用开发完全指南:从零到企业实战(两万字深度解析)》
java·开发语言·前端·html·javaweb·js
阳火锅2 小时前
AI时代的到来,我想打造这样一款产品。
前端·javascript·vue.js
llxxyy卢2 小时前
polar-web题目
开发语言·前端·javascript
OpenTiny社区2 小时前
不仅是修复 Bug:TinyVue 3.29.0 把“无障碍信息”写进了组件的 DNA 里
前端·javascript·vue.js