H5多功能面板

H5多功能面板

(function () {
    if (document.getElementById("custom-float-panel")) return;

    let isCollapsed = false;

    function createFloatPanel() {
        const panel = document.createElement("div");
        panel.id = "custom-float-panel";
        panel.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 99999;
            background: #fff;
            padding: 20px;
            border: 1px solid #e5e7eb;
            border-radius: 12px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
            min-width: 350px;
            max-height: 80vh; /* 限制最大高度为视口80% */
            overflow: hidden; /* 隐藏溢出,由内容区滚动 */
            font-family: -apple-system, BlinkMacSystemFont, sans-serif;
            transition: all 0.3s ease;
        `;

        const panelHeader = document.createElement("div");
        panelHeader.style.cssText = `
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 16px;
            position: sticky;
            top: 0;
            background: #fff;
            z-index: 1;
            padding-bottom: 8px;
        `;

        const title = document.createElement("h3");
        title.textContent = "多功能面板";
        title.style.cssText = `
            margin: 0;
            font-size: 18px;
            color: #1f2937;
            font-weight: 600;
            white-space: nowrap;
        `;

        const toggleBtn = document.createElement("button");
        toggleBtn.id = "panel-toggle-btn";
        toggleBtn.textContent = "−";
        toggleBtn.style.cssText = `
            width: 24px;
            height: 24px;
            border: none;
            border-radius: 4px;
            background: #f3f4f6;
            color: #4b5563;
            cursor: pointer;
            font-size: 16px;
            display: flex;
            align-items: center;
            justify-content: center;
            flex-shrink: 0;
            box-shadow: 0 1px 2px rgba(0,0,0,0.05);
            transition: background 0.2s;
        `;
        toggleBtn.onmouseover = () => toggleBtn.style.background = "#e5e7eb";
        toggleBtn.onmouseout = () => toggleBtn.style.background = "#f3f4f6";

        panelHeader.appendChild(title);
        panelHeader.appendChild(toggleBtn);

        // ========== 核心修改:内容区增加滚动 ==========
        const panelContent = document.createElement("div");
        panelContent.id = "panel-content";
        panelContent.style.cssText = `
            overflow-y: auto; /* 垂直滚动 */
            max-height: calc(80vh - 80px); /* 减去头部高度,适配最大高度 */
            padding-right: 4px; /* 给滚动条预留空间 */
            scrollbar-width: thin; /* 精简滚动条(Firefox) */
            scrollbar-color: #d1d5db #f9fafb; /* 滚动条样式 */
        `;
        // 自定义滚动条样式(Chrome/Safari)
        panelContent.style.cssText += `
            &::-webkit-scrollbar {
                width: 6px;
            }
            &::-webkit-scrollbar-track {
                background: #f9fafb;
                border-radius: 3px;
            }
            &::-webkit-scrollbar-thumb {
                background: #d1d5db;
                border-radius: 3px;
            }
            &::-webkit-scrollbar-thumb:hover {
                background: #9ca3af;
            }
        `;

        // 分割线
        const divider = document.createElement("hr");
        divider.style.cssText = `
            border: none;
            border-top: 1px solid #e5e7eb;
            margin: 14px 0;
        `;

        // ========== 通用按钮样式(复用) ==========
        const btnCommonStyle = `
            padding: 8px 16px;
            border: 1px solid transparent;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
            transition: all 0.2s ease;
        `;

        // ========== 定位模块 ==========
        const geoSection = document.createElement("div");
        geoSection.style.marginBottom = "20px";

        const geoTitle = document.createElement("div");
        geoTitle.textContent = "地理位置";
        geoTitle.style.cssText = `
            font-weight: 600;
            margin-bottom: 10px;
            color: #4b5563;
            font-size: 14px;
        `;

        const geoBtn = document.createElement("button");
        geoBtn.textContent = "获取位置";
        geoBtn.style.cssText = `${btnCommonStyle}
            background: #3b82f6;
            color: white;
            border-color: #2563eb;
        `;
        geoBtn.onmouseover = () => {
            geoBtn.style.background = "#2563eb";
            geoBtn.style.boxShadow = "0 4px 8px rgba(59, 130, 246, 0.2)";
        };
        geoBtn.onmouseout = () => {
            geoBtn.style.background = "#3b82f6";
            geoBtn.style.boxShadow = "0 2px 4px rgba(0,0,0,0.05)";
        };

        const geoResult = document.createElement("div");
        geoResult.style.cssText = `
            margin-top: 8px;
            font-size: 12px;
            color: #6b7280;
            min-height: 20px;
        `;
        geoResult.textContent = "未获取";
        geoSection.appendChild(geoTitle);
        geoSection.appendChild(geoBtn);
        geoSection.appendChild(geoResult);

        // ========== 文件选择模块 ==========
        const fileSection = document.createElement("div");
        fileSection.style.marginBottom = "20px";

        const fileTitle = document.createElement("div");
        fileTitle.textContent = "多文件选择";
        fileTitle.style.cssText = `
            font-weight: 600;
            margin-bottom: 10px;
            color: #4b5563;
            font-size: 14px;
        `;

        const fileBtn = document.createElement("button");
        fileBtn.textContent = "选择文件";
        fileBtn.style.cssText = `${btnCommonStyle}
            background: #10b981;
            color: white;
            border-color: #059669;
        `;
        fileBtn.onmouseover = () => {
            fileBtn.style.background = "#059669";
            fileBtn.style.boxShadow = "0 4px 8px rgba(16, 185, 129, 0.2)";
        };
        fileBtn.onmouseout = () => {
            fileBtn.style.background = "#10b981";
            fileBtn.style.boxShadow = "0 2px 4px rgba(0,0,0,0.05)";
        };

        const fileInput = document.createElement("input");
        fileInput.type = "file";
        fileInput.multiple = true;
        fileInput.style.display = "none";

        const fileInfo = document.createElement("div");
        fileInfo.style.cssText = `
            margin-top: 8px;
            font-size: 12px;
            color: #6b7280;
            min-height: 20px;
        `;
        fileInfo.textContent = "未选择";
        fileSection.appendChild(fileTitle);
        fileSection.appendChild(fileBtn);
        fileSection.appendChild(fileInput);
        fileSection.appendChild(fileInfo);

        // ========== 录音模块 ==========
        const audioSection = document.createElement("div");
        audioSection.style.marginBottom = "20px";

        const audioTitle = document.createElement("div");
        audioTitle.textContent = "H5录音";
        audioTitle.style.cssText = `
            font-weight: 600;
            margin-bottom: 10px;
            color: #4b5563;
            font-size: 14px;
        `;

        const audioBtnWrap = document.createElement("div");
        audioBtnWrap.style.display = "flex";
        audioBtnWrap.style.gap = "8px";

        const startAudio = document.createElement("button");
        startAudio.textContent = "开始录音";
        startAudio.style.cssText = `${btnCommonStyle}
            background: #f59e0b;
            color: white;
            border-color: #d97706;
        `;
        startAudio.onmouseover = () => {
            startAudio.style.background = "#d97706";
            startAudio.style.boxShadow = "0 4px 8px rgba(245, 158, 11, 0.2)";
        };
        startAudio.onmouseout = () => {
            startAudio.style.background = "#f59e0b";
            startAudio.style.boxShadow = "0 2px 4px rgba(0,0,0,0.05)";
        };

        const stopAudio = document.createElement("button");
        stopAudio.textContent = "停止录音";
        stopAudio.style.cssText = `${btnCommonStyle}
            background: #ef4444;
            color: white;
            border-color: #dc2626;
            opacity: 0.6;
            pointer-events: none;
        `;
        stopAudio.onmouseover = () => {
            if (!stopAudio.disabled) {
                stopAudio.style.background = "#dc2626";
                stopAudio.style.boxShadow = "0 4px 8px rgba(239, 68, 68, 0.2)";
            }
        };
        stopAudio.onmouseout = () => {
            if (!stopAudio.disabled) {
                stopAudio.style.background = "#ef4444";
                stopAudio.style.boxShadow = "0 2px 4px rgba(0,0,0,0.05)";
            }
        };

        const audioResult = document.createElement("div");
        audioResult.style.cssText = `
            margin-top: 8px;
            font-size: 12px;
            color: #6b7280;
            min-height: 20px;
        `;
        audioResult.textContent = "未录音";
        audioBtnWrap.appendChild(startAudio);
        audioBtnWrap.appendChild(stopAudio);
        audioSection.appendChild(audioTitle);
        audioSection.appendChild(audioBtnWrap);
        audioSection.appendChild(audioResult);

        // ========== 录像模块(优化显示) ==========
        const videoSection = document.createElement("div");
        videoSection.style.marginBottom = "20px"; // 增加底部间距

        const videoTitle = document.createElement("div");
        videoTitle.textContent = "H5录像";
        videoTitle.style.cssText = `
            font-weight: 600;
            margin-bottom: 10px;
            color: #4b5563;
            font-size: 14px;
        `;

        const videoBtnWrap = document.createElement("div");
        videoBtnWrap.style.display = "flex";
        videoBtnWrap.style.gap = "8px";

        const startVideo = document.createElement("button");
        startVideo.textContent = "开始录像";
        startVideo.style.cssText = `${btnCommonStyle}
            background: #8b5cf6;
            color: white;
            border-color: #7c3aed;
        `;
        startVideo.onmouseover = () => {
            startVideo.style.background = "#7c3aed";
            startVideo.style.boxShadow = "0 4px 8px rgba(139, 92, 246, 0.2)";
        };
        startVideo.onmouseout = () => {
            startVideo.style.background = "#8b5cf6";
            startVideo.style.boxShadow = "0 2px 4px rgba(0,0,0,0.05)";
        };

        const stopVideo = document.createElement("button");
        stopVideo.textContent = "停止录像";
        stopVideo.style.cssText = `${btnCommonStyle}
            background: #ef4444;
            color: white;
            border-color: #dc2626;
            opacity: 0.6;
            pointer-events: none;
        `;
        stopVideo.onmouseover = () => {
            if (!stopVideo.disabled) {
                stopVideo.style.background = "#dc2626";
                stopVideo.style.boxShadow = "0 4px 8px rgba(239, 68, 68, 0.2)";
            }
        };
        stopVideo.onmouseout = () => {
            if (!stopVideo.disabled) {
                stopVideo.style.background = "#ef4444";
                stopVideo.style.boxShadow = "0 2px 4px rgba(0,0,0,0.05)";
            }
        };

        // ========== 核心优化:录像预览区域 ==========
        const videoPreview = document.createElement("div");
        videoPreview.style.cssText = `
            margin-top: 8px;
            font-size: 12px;
            color: #6b7280;
            min-height: 20px;
            width: 100%; /* 宽度100%适配面板 */
        `;
        // 视频元素专属样式
        videoPreview.innerHTML = `
            <style>
                #video-preview-container video {
                    max-width: 100% !important;
                    height: auto !important;
                    border-radius: 6px;
                    margin-top: 8px;
                }
            </style>
            <div id="video-preview-container">未录像</div>
        `;

        videoBtnWrap.appendChild(startVideo);
        videoBtnWrap.appendChild(stopVideo);
        videoSection.appendChild(videoTitle);
        videoSection.appendChild(videoBtnWrap);
        videoSection.appendChild(videoPreview);

        // 组装内容
        panelContent.appendChild(geoSection);
        panelContent.appendChild(fileSection);
        panelContent.appendChild(audioSection);
        panelContent.appendChild(videoSection);

        panel.appendChild(panelHeader);
        panel.appendChild(divider);
        panel.appendChild(panelContent);
        document.body.appendChild(panel);

        return {
            panel, toggleBtn, panelContent, title,
            geoBtn, geoResult,
            fileBtn, fileInput, fileInfo,
            startAudio, stopAudio, audioResult,
            startVideo, stopVideo, 
            videoPreview: document.getElementById("video-preview-container")
        };
    }

    // 折叠/展开:正方形 + 横排缩写
    function initToggle(dom) {
        dom.toggleBtn.onclick = () => {
            isCollapsed = !isCollapsed;
            if (isCollapsed) {
                // 折叠状态:60×60 正方形
                dom.panel.style.minWidth = "60px";
                dom.panel.style.width = "60px";
                dom.panel.style.height = "60px";
                dom.panel.style.padding = "8px";
                dom.panel.style.borderRadius = "12px";
                dom.panelContent.style.display = "none";
                dom.toggleBtn.textContent = "+";
                // 标题缩写 + 缩小字号
                dom.title.textContent = "多功能";
                dom.title.style.fontSize = "12px";
                dom.title.style.maxWidth = "32px";
                dom.title.style.overflow = "hidden";
                dom.title.style.textOverflow = "ellipsis";
            } else {
                // 展开状态:恢复原样式
                dom.panel.style.minWidth = "350px";
                dom.panel.style.width = "";
                dom.panel.style.height = "";
                dom.panel.style.padding = "20px";
                dom.panelContent.style.display = "block";
                dom.toggleBtn.textContent = "−";
                dom.title.textContent = "多功能面板";
                dom.title.style.fontSize = "18px";
                dom.title.style.maxWidth = "none";
                dom.title.style.overflow = "visible";
            }
        };
    }

    // 定位功能
    function initGeo(dom) {
        dom.geoBtn.onclick = () => {
            if (!navigator.geolocation) {
                dom.geoResult.textContent = "不支持定位";
                return;
            }
            dom.geoResult.textContent = "获取中...";
            navigator.geolocation.getCurrentPosition(pos => {
                const lat = pos.coords.latitude.toFixed(6);
                const lng = pos.coords.longitude.toFixed(6);
                dom.geoResult.textContent = `纬度:${lat},经度:${lng}`;
            }, err => {
                dom.geoResult.textContent = "获取失败:" + err.message;
            });
        };
    }

    // 文件选择功能
    function initFile(dom) {
        dom.fileBtn.onclick = () => dom.fileInput.click();
        dom.fileInput.onchange = () => {
            const files = Array.from(dom.fileInput.files);
            if (files.length === 0) {
                dom.fileInfo.textContent = "未选择";
                return;
            }
            dom.fileInfo.innerHTML = `已选 ${files.length} 个文件`;
            dom.fileInput.value = "";
        };
    }

    // 录音功能
    let audioRecorder;
    function initAudio(dom) {
        dom.startAudio.onclick = async () => {
            try {
                const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
                audioRecorder = new MediaRecorder(stream);
                const chunks = [];
                
                audioRecorder.ondataavailable = e => chunks.push(e.data);
                audioRecorder.onstop = () => {
                    const blob = new Blob(chunks, { type: "audio/webm" });
                    const url = URL.createObjectURL(blob);
                    dom.audioResult.innerHTML = `
                        录音完成<br>
                        <audio controls src="${url}" style="width:100%;margin-top:8px;">
                    `;
                    // 恢复按钮状态
                    dom.startAudio.style.opacity = "1";
                    dom.startAudio.style.pointerEvents = "auto";
                    dom.stopAudio.style.opacity = "0.6";
                    dom.stopAudio.style.pointerEvents = "none";
                    // 停止音轨
                    stream.getTracks().forEach(t => t.stop());
                };

                audioRecorder.start();
                dom.audioResult.textContent = "录音中...";
                // 禁用开始按钮,启用停止按钮
                dom.startAudio.style.opacity = "0.6";
                dom.startAudio.style.pointerEvents = "none";
                dom.stopAudio.style.opacity = "1";
                dom.stopAudio.style.pointerEvents = "auto";
            } catch (err) {
                dom.audioResult.textContent = "录音失败:" + err.message;
            }
        };

        dom.stopAudio.onclick = () => {
            audioRecorder?.stop();
        };
    }

    // 录像功能(优化显示)
    let videoRecorder;
    function initVideo(dom) {
        dom.startVideo.onclick = async () => {
            try {
                const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
                videoRecorder = new MediaRecorder(stream);
                const chunks = [];
                
                videoRecorder.ondataavailable = e => chunks.push(e.data);
                videoRecorder.onstop = () => {
                    const blob = new Blob(chunks, { type: "video/webm" });
                    const url = URL.createObjectURL(blob);
                    // 优化视频显示:宽度100%,高度自适应
                    dom.videoPreview.innerHTML = `
                        录像完成(大小:${Math.round(blob.size/1024)}KB)<br>
                        <video controls src="${url}" style="width:100%;height:auto;">
                    `;
                    // 恢复按钮状态
                    dom.startVideo.style.opacity = "1";
                    dom.startVideo.style.pointerEvents = "auto";
                    dom.stopVideo.style.opacity = "0.6";
                    dom.stopVideo.style.pointerEvents = "none";
                    // 停止音视频轨
                    stream.getTracks().forEach(t => t.stop());
                    
                    // 滚动到录像区域(可选,提升体验)
                    dom.videoPreview.scrollIntoView({ behavior: "smooth", block: "center" });
                };

                videoRecorder.start();
                dom.videoPreview.textContent = "录像中...";
                // 禁用开始按钮,启用停止按钮
                dom.startVideo.style.opacity = "0.6";
                dom.startVideo.style.pointerEvents = "none";
                dom.stopVideo.style.opacity = "1";
                dom.stopVideo.style.pointerEvents = "auto";
            } catch (err) {
                dom.videoPreview.textContent = "录像失败:" + err.message;
            }
        };

        dom.stopVideo.onclick = () => {
            videoRecorder?.stop();
        };
    }

    // 启动所有功能
    const dom = createFloatPanel();
    initToggle(dom);
    initGeo(dom);
    initFile(dom);
    initAudio(dom);
    initVideo(dom);
})();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容