代码 智能视频墙

2025-02-22 11:21:29 +0800 CST views 263

该文本描述了一个智能视频墙的HTML和JavaScript实现。用户可以创建视频容器,支持拖拽和调整大小,并且能够保存布局到本地存储。使用了EasyPlayerPro播放器来播放视频流,提供了多种调整手柄以便用户自定义视频容器的大小和位置。代码中还处理了窗口大小变化的情况,确保视频容器不会超出视口。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能视频墙</title>
    <script src="/js/EasyPlayer-pro.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background: #000;
            overflow: hidden;
            height: 100vh;
        }

        .video-container {
            position: absolute;
            cursor: move;
            overflow: hidden;
            border: 2px solid transparent;
            transition: border-color 0.2s;
        }

        .video-container.selected {
            border-color: #2196F3;
        }

        .resize-handle {
            position: absolute;
            width: 12px;
            height: 12px;
            background: #2196F3;
            z-index: 2;
            opacity: 0;
            transition: opacity 0.2s;
        }

        .selected .resize-handle {
            opacity: 1;
        }

        .resize-nw { left: -6px;  top: -6px;  cursor: nw-resize; }
        .resize-ne { right: -6px; top: -6px;  cursor: ne-resize; }
        .resize-sw { left: -6px;  bottom: -6px; cursor: sw-resize; }
        .resize-se { right: -6px; bottom: -6px; cursor: se-resize; }
        .resize-n  { left: 50%;   top: -6px;  cursor: n-resize; }
        .resize-s  { left: 50%;   bottom: -6px; cursor: s-resize; }
        .resize-w  { left: -6px;  top: 50%;   cursor: w-resize; }
        .resize-e  { right: -6px; top: 50%;   cursor: e-resize; }

        .drag-ghost {
            opacity: 0.8;
            transform: scale(1.02);
        }
    </style>
</head>
<body>
<script>
    const videoSources = [
        "ws://183.223.76.216:28080/live/PerwsB0LcQY_01.flv?expired=20250124101429",
        "ws://183.223.76.216:28080/live/P7ULxnT47x_01.flv?expired=20250124101527",
        "ws://183.223.76.216:28080/live/PhfaWb6Ux0c_01.flv?expired=20250124101020",
        "ws://183.223.76.216:28080/live/PubmOrMJoHg_01.flv?expired=20250124101154",
        "ws://183.223.76.216:28080/live/PmKfOwWkdZD_01.flv?expired=20250124101609"
    ];

    const STORAGE_KEY = 'videoWallLayout';
    let selectedContainer = null;
    let isResizing = false;
    let isDragging = false;
    let dragStartX, dragStartY, initialLeft, initialTop;

    function createVideoContainer(url, index) {
        const container = document.createElement('div');
        container.className = 'video-container';
        
        // 添加调整手柄
        const directions = ['nw', 'ne', 'sw', 'se', 'n', 's', 'w', 'e'];
        directions.forEach(dir => {
            const handle = document.createElement('div');
            handle.className = `resize-handle resize-${dir}`;
            handle.dataset.direction = dir;
            container.appendChild(handle);
        });

        // 初始化播放器
        new EasyPlayerPro({
            isLive: true,
            isMute: true,
            container: container,
            videoBuffer: 0.2,
            useMSE: true,
            showBandwidth: false,
            operateBtns: { 
                fullscreen: false,
                screenshot: false,
                play: false,
                audio: false,
                quality: false
            }
        }).play(url);

        // 加载布局
        const savedLayout = loadLayout();
        if(savedLayout && savedLayout[index]) {
            Object.assign(container.style, {
                width: savedLayout[index].width,
                height: savedLayout[index].height,
                left: savedLayout[index].left,
                top: savedLayout[index].top
            });
        } else {
            container.style.width = '400px';
            container.style.height = '225px';
            container.style.left = `${Math.random() * (window.innerWidth - 400)}px`;
            container.style.top = `${Math.random() * (window.innerHeight - 225)}px`;
        }

        // 事件监听
        container.addEventListener('mousedown', handleMouseDown);
        container.addEventListener('mouseup', handleMouseUp);
        
        document.body.appendChild(container);
        return container;
    }

    function handleMouseDown(e) {
        if (e.target.classList.contains('resize-handle')) {
            startResize(e);
            return;
        }

        const container = e.currentTarget;
        selectContainer(container);
        
        // 立即开始拖拽
        startDrag(e);
    }

    function handleMouseUp() {
        if (isDragging) endDrag();
    }

    function selectContainer(container) {
        if (selectedContainer) selectedContainer.classList.remove('selected');
        selectedContainer = container;
        container.classList.add('selected');
    }

    function startResize(e) {
        isResizing = true;
        const container = e.target.parentElement;
        const direction = e.target.dataset.direction;
        const rect = container.getBoundingClientRect();
        
        const startX = e.clientX;
        const startY = e.clientY;
        const startWidth = rect.width;
        const startHeight = rect.height;
        const startLeft = rect.left;
        const startTop = rect.top;

        function moveHandler(e) {
            if (!isResizing) return;
            
            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;
            
            switch(direction) {
                case 'e':
                    container.style.width = startWidth + deltaX + 'px';
                    break;
                case 's':
                    container.style.height = startHeight + deltaY + 'px';
                    break;
                case 'se':
                    container.style.width = startWidth + deltaX + 'px';
                    container.style.height = startHeight + deltaY + 'px';
                    break;
                case 'n':
                    container.style.height = startHeight - deltaY + 'px';
                    container.style.top = startTop + deltaY + 'px';
                    break;
                case 'w':
                    container.style.width = startWidth - deltaX + 'px';
                    container.style.left = startLeft + deltaX + 'px';
                    break;
                case 'ne':
                    container.style.width = startWidth + deltaX + 'px';
                    container.style.height = startHeight - deltaY + 'px';
                    container.style.top = startTop + deltaY + 'px';
                    break;
                case 'nw':
                    container.style.width = startWidth - deltaX + 'px';
                    container.style.height = startHeight - deltaY + 'px';
                    container.style.left = startLeft + deltaX + 'px';
                    container.style.top = startTop + deltaY + 'px';
                    break;
                case 'sw':
                    container.style.width = startWidth - deltaX + 'px';
                    container.style.height = startHeight + deltaY + 'px';
                    container.style.left = startLeft + deltaX + 'px';
                    break;
            }
        }

        function upHandler() {
            isResizing = false;
            saveLayout();
            document.removeEventListener('mousemove', moveHandler);
            document.removeEventListener('mouseup', upHandler);
        }

        document.addEventListener('mousemove', moveHandler);
        document.addEventListener('mouseup', upHandler);
    }

    function startDrag(e) {
        isDragging = true;
        const container = e.currentTarget;
        container.classList.add('drag-ghost');
        
        dragStartX = e.clientX;
        dragStartY = e.clientY;
        initialLeft = parseFloat(container.style.left);
        initialTop = parseFloat(container.style.top);

        function moveHandler(e) {
            if (!isDragging) return;
            
            const deltaX = e.clientX - dragStartX;
            const deltaY = e.clientY - dragStartY;
            
            container.style.left = Math.max(0, Math.min(
                initialLeft + deltaX,
                window.innerWidth - container.offsetWidth
            )) + 'px';
            
            container.style.top = Math.max(0, Math.min(
                initialTop + deltaY,
                window.innerHeight - container.offsetHeight
            )) + 'px';
        }

        function upHandler() {
            isDragging = false;
            container.classList.remove('drag-ghost');
            saveLayout();
            document.removeEventListener('mousemove', moveHandler);
            document.removeEventListener('mouseup', upHandler);
        }

        document.addEventListener('mousemove', moveHandler);
        document.addEventListener('mouseup', upHandler);
    }

    function saveLayout() {
        const layout = Array.from(document.querySelectorAll('.video-container')).map(container => ({
            width: container.style.width,
            height: container.style.height,
            left: container.style.left,
            top: container.style.top
        }));
        localStorage.setItem(STORAGE_KEY, JSON.stringify(layout));
    }

    function loadLayout() {
        try {
            return JSON.parse(localStorage.getItem(STORAGE_KEY));
        } catch {
            return null;
        }
    }

    window.addEventListener('load', () => {
        videoSources.forEach((url, index) => createVideoContainer(url, index));
        
        // 点击空白取消选择
        document.addEventListener('click', (e) => {
            if (!e.target.closest('.video-container')) {
                if (selectedContainer) {
                    selectedContainer.classList.remove('selected');
                    selectedContainer = null;
                }
            }
        });

        // 窗口大小变化处理
        window.addEventListener('resize', () => {
            document.querySelectorAll('.video-container').forEach(container => {
                container.style.left = Math.min(
                    parseFloat(container.style.left),
                    window.innerWidth - container.offsetWidth
                ) + 'px';
                
                container.style.top = Math.min(
                    parseFloat(container.style.top),
                    window.innerHeight - container.offsetHeight
                ) + 'px';
            });
            saveLayout();
        });
    });
</script>
</body>
</html>
复制全文 生成海报 前端开发 视频播放 用户交互 Web技术

推荐文章

使用 node-ssh 实现自动化部署
2024-11-18 20:06:21 +0800 CST
PHP 代码功能与使用说明
2024-11-18 23:08:44 +0800 CST
解决python “No module named pip”
2024-11-18 11:49:18 +0800 CST
JavaScript设计模式:装饰器模式
2024-11-19 06:05:51 +0800 CST
mysql删除重复数据
2024-11-19 03:19:52 +0800 CST
CSS 中的 `scrollbar-width` 属性
2024-11-19 01:32:55 +0800 CST
Vue 中如何处理父子组件通信?
2024-11-17 04:35:13 +0800 CST
Vue中如何处理异步更新DOM?
2024-11-18 22:38:53 +0800 CST
Go 开发中的热加载指南
2024-11-18 23:01:27 +0800 CST
npm速度过慢的解决办法
2024-11-19 10:10:39 +0800 CST
JavaScript设计模式:观察者模式
2024-11-19 05:37:50 +0800 CST
nuxt.js服务端渲染框架
2024-11-17 18:20:42 +0800 CST
Go配置镜像源代理
2024-11-19 09:10:35 +0800 CST
三种高效获取图标资源的平台
2024-11-18 18:18:19 +0800 CST
Git 常用命令详解
2024-11-18 16:57:24 +0800 CST
Plyr.js 播放器介绍
2024-11-18 12:39:35 +0800 CST
js迭代器
2024-11-19 07:49:47 +0800 CST
程序员茄子在线接单