微信小程序中使用h5页面预览图片、视频、pdf文件

发布于:2025-04-21 ⋅ 阅读:(19) ⋅ 点赞:(0)

遇到了这么一个需求,需要在微信小程序中点击文件,进行文件预览。

要求:

  1. 图片:长图需要宽度100%高度自适应;横图的话宽度100%,高度居中显示
  2. 视频:视频不管横向还是竖向都居中显示,有全屏播放按钮
  3. pdf:pdf需要宽度100%,高度自适应。可以预览多页pdf文件。

概要:

使用原生html、js、css写的,通过url吧要预览的文件的url传递过去。

图片使用<img>进行预览,自己写样式

视频使用的是video.js进行的预览,我发现这个可以兼容安卓和苹果手机,而且预览速度很快。

pdf使用的是pdf.min.js进行的预览。--就这玩意不好整

代码:

<!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>文件预览工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            background-color: #f5f5f5;
            color: #333;
            height: 100vh;
        }
        .modal-content {
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: column;
        }
        #pdf-container {
            width: 100%;
            flex-grow: 1;
        }
        #video-container {
            width: 100%;
            margin: auto;
            flex-grow: 1;
            display: flex;
            align-items: center;
        }
        #image-container {
            width: 100%;
            min-height: 100vh;
            flex-grow: 1;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #image-container img {
            width: 100%;
        }
        .loading {
          width: 100%;
          height: 100vh;
          background: rgba(0, 0, 0, 0.5);
          display: flex;
          justify-content: center;
          align-items: center;
          color: white;
          
        }

        /* 如果你想要一个旋转动画 */
        .loading::after {
          content: "";
          width: 30px;
          height: 30px;
          border: 3px solid rgba(255, 255, 255, 0.3);
          border-radius: 50%;
          border-top-color: white;
          animation: spin 1s linear infinite;
        }

        @keyframes spin {
          to { transform: rotate(360deg); }
        }
        .error {
            color: white;
            text-align: center;
            margin-top: 50%;
            padding: 20px;
        }
        .video-js .vjs-big-play-button{
          top: 50% !important;
          left: 50% !important;
          transform: translate(-50%, -50%);
        }
        /* PDF 控制栏样式 */
        .pdf-controls {
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0, 0, 0, 0.7);
            padding: 10px 15px;
            border-radius: 20px;
            display: flex;
            align-items: center;
            color: white;
            z-index: 100;
        }
        
        .pdf-controls button {
            background: none;
            border: none;
            color: white;
            font-size: 16px;
            padding: 5px 10px;
            cursor: pointer;
        }
        
        .pdf-controls span {
            margin: 0 10px;
        }
        
        .pdf-page {
            margin-bottom: 20px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            width: 100% !important;
            height: auto !important;
        }
        
        .pdf-page canvas {
            width: 100% !important;
            height: auto !important;
            display: block;
        }
    </style>
    <!-- Video.js 视频播放器 -->
    <link href="https://vjs.zencdn.net/7.20.3/video-js.css" rel="stylesheet">
     <script src="https://vjs.zencdn.net/7.20.3/video.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"></script>
     <script>
      // 设置PDF.js worker路径
      pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.worker.min.js';
   </script>
</head>
<body>
    <!-- 预览模态框 -->
    <div id="preview-modal" class="modal">
        <div class="modal-content">
            <div id="image-container" style="display: none;">
                <img id="preview-image" src="" class="img-box">
            </div>
            <div id="video-container" style="display: none;">
                <video id="preview-video" class="video-js" controls playsinline style="width: 100%;height: 100vh;"></video>
            </div>
            <div id="pdf-container" style="display: none;">
            </div>
            <div id="loading" class="loading">
            </div>
            <div id="error" class="error" style="display: none;"></div>
            <!-- PDF 控制栏 -->
            <div id="pdf-controls" class="pdf-controls" style="display: none;">
              <button id="prev-page">上一页</button>
              <span id="page-num">1 / 1</span>
              <button id="next-page">下一页</button>
          </div>
        </div>
    </div>
    
    <script>
        // 获取URL参数
        function getQueryParam(name) {
            const urlParams = new URLSearchParams(window.location.search);
            return urlParams.get(name);
        }
        
        // 获取文件类型
        function getFileType(url) {
            if (!url) return null;
            
            // 提取文件扩展名
            const extension = url.split('.').pop().toLowerCase().split('?')[0];
            
            // 图片类型
            const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
            if (imageTypes.includes(extension)) return 'image';
            
            // 视频类型
            const videoTypes = ['mp4', 'webm', 'ogg', 'mov'];
            if (videoTypes.includes(extension)) return 'video';
            
            // PDF类型
            if (extension === 'pdf') return 'pdf';
            
            return null;
        }
        
        // 预览文件函数
        function previewFile(url) {
            const modal = document.getElementById('preview-modal');
            const loading = document.getElementById('loading');
            const errorDiv = document.getElementById('error');
            const imageContainer = document.getElementById('image-container');
            const videoContainer = document.getElementById('video-container');
            const pdfContainer = document.getElementById('pdf-container');
            
            // 显示加载中和模态框
            loading.style.display = 'flex';
            errorDiv.style.display = 'none';
            imageContainer.style.display = 'none';
            videoContainer.style.display = 'none';
            pdfContainer.style.display = 'none';
            modal.style.display = 'block';
            console.log('加载开始');
            
            
            // 获取文件类型
            const fileType = getFileType(url);
            
            if (!fileType) {
                loading.style.display = 'none';
                errorDiv.style.display = 'block';
                errorDiv.textContent = '不支持的文件类型或URL格式不正确';
                return;
            }
            
            // 根据文件类型处理
            if (fileType === 'image') {
                const img = document.getElementById('preview-image');
                img.alt = '图片预览';
                img.onload = function() {
                  loading.style.display = 'none';
                  imageContainer.style.display = 'flex';
                };
                img.onerror = function() {
                    showError('图片加载失败');
                };
                img.src = url;
            } 
            else if (fileType === 'video') {
                loading.style.display = 'none';
                videoContainer.style.display = 'flex';
                
                // 初始化视频播放器
                const player = videojs('preview-video', {
                    controls: true,
                    autoplay: true,
                    preload: 'auto',
                    playsinline: true,
                    poster: url + '?vframe/png/offset/1',
                    sources: [{
                        src: url,
                        type: getVideoMimeType(url)
                    }]
                });
                
                player.on('error', function() {
                    showError('视频加载失败');
                });
                
                // 存储播放器实例以便关闭时销毁
                modal.dataset.player = player;
            } 
            else if (fileType === 'pdf') {
              pdfContainer.style.display = 'block';
              const previewContainer = document.getElementById('pdf-container');
              // 存储PDF相关变量
              let pdfDoc = null;
                let currentPage = 1;
                let pageRendering = false;
                let pageNumPending = null;
                const scale = 1.0;
                
                // 获取DOM元素
                const prevPageBtn = document.getElementById('prev-page');
                const nextPageBtn = document.getElementById('next-page');
                const pageNumSpan = document.getElementById('page-num');
                
                // 渲染PDF页面
                function renderPage(num) {
                    pageRendering = true;
                    
                    // 使用promise获取页面
                    pdfDoc.getPage(num).then(function(page) {
                        const viewport = page.getViewport({ scale: scale });
                        
                        // 创建容器div
                        const pageDiv = document.createElement('div');
                        pageDiv.className = 'pdf-page';
                        pageDiv.id = `page-${num}`;
                        
                        // 移除旧的页面元素
                        const oldPage = document.getElementById(`page-${num}`);
                        if (oldPage) oldPage.remove();
                        
                        // 创建canvas元素
                        const canvas = document.createElement('canvas');
                        const context = canvas.getContext('2d');
                        
                        // 设置canvas尺寸
                        canvas.height = viewport.height;
                        canvas.width = viewport.width;
                        
                        // 将canvas添加到页面容器
                        pageDiv.appendChild(canvas);
                        
                        // 将页面容器添加到PDF容器
                        pdfContainer.appendChild(pageDiv);
                        
                        // 渲染PDF页面到canvas上
                        const renderContext = {
                            canvasContext: context,
                            viewport: viewport
                        };
                        
                        const renderTask = page.render(renderContext);
                        
                        // 渲染完成
                        renderTask.promise.then(function() {
                            pageRendering = false;
                            if (pageNumPending !== null) {
                                // 有新页面要渲染
                                renderPage(pageNumPending);
                                pageNumPending = null;
                            }
                            
                            // 更新页面显示
                            pageNumSpan.textContent = `${currentPage} / ${pdfDoc.numPages}`;
                            
                            // 加载完成
                            if (currentPage === 1) {
                                loading.style.display = 'none';
                            }
                        });
                    });
                }
                
                // 跳转到指定页面
                function gotoPage(num) {
                    if (pageRendering) {
                        pageNumPending = num;
                    } else if (num !== currentPage && num > 0 && num <= pdfDoc.numPages) {
                        currentPage = num;
                        renderPage(currentPage);
                        
                        // 滚动到页面顶部
                        const pageElement = document.getElementById(`page-${currentPage}`);
                        if (pageElement) {
                            pageElement.scrollIntoView();
                        }
                    }
                }
                
                // 上一页
                prevPageBtn.onclick = function() {
                    if (currentPage <= 1) return;
                    gotoPage(currentPage - 1);
                };
                
                // 下一页
                nextPageBtn.onclick = function() {
                    if (currentPage >= pdfDoc.numPages) return;
                    gotoPage(currentPage + 1);
                };
                
                // 异步加载PDF文档
                pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
                    pdfDoc = pdfDoc_;
                    
                    // 更新页面总数显示
                    pageNumSpan.textContent = `1 / ${pdfDoc.numPages}`;
                    
                    // 初始渲染第一页
                    renderPage(1);
                    
                    // 预加载后续页面
                    for (let i = 2; i <= Math.min(999999, pdfDoc.numPages); i++) {
                        renderPage(i);
                    }
                }).catch(function(error) {
                    console.error('Error loading PDF:', error);
                    showError('PDF加载失败');
                });
            }
        }
        
        // 获取视频MIME类型
        function getVideoMimeType(url) {
            const extension = url.split('.').pop().toLowerCase().split('?')[0];
            switch(extension) {
                case 'mp4': return 'video/mp4';
                case 'webm': return 'video/webm';
                case 'ogg': return 'video/ogg';
                default: return 'video/mp4';
            }
        }
        
        // 显示错误信息
        function showError(message) {
            document.getElementById('loading').style.display = 'none';
            const errorDiv = document.getElementById('error');
            errorDiv.style.display = 'block';
            errorDiv.textContent = message;
        }
        
        // 关闭模态框
        function closeModal() {
            const modal = document.getElementById('preview-modal');
            modal.style.display = 'none';
            
            // 如果有视频播放器实例,则销毁它
            if (modal.dataset.player) {
                modal.dataset.player.dispose();
                delete modal.dataset.player;
            }
            
            // 如果有图片查看器实例,则销毁它
            if (modal.dataset.viewer) {
                modal.dataset.viewer.destroy();
                delete modal.dataset.viewer;
            }
        }
        
        // 点击模态框背景关闭
        document.getElementById('preview-modal').addEventListener('click', function(e) {
            if (e.target === this) {
                closeModal();
            }
        });
        
        // 页面加载完成后自动处理
        document.addEventListener('DOMContentLoaded', function() {
            const fileUrl = getQueryParam('url');
            if (fileUrl) {
                previewFile(fileUrl);
            } else {
                showError('未提供文件URL参数');
                document.getElementById('preview-modal').style.display = 'block';
                document.getElementById('loading').style.display = 'none';
            }
        });
    </script>
</body>
</html>

使用:

http://192.168.3.189:5500/preview.html?url=https://kt.sh-t.com.cn/sqb/c4cee917056c24b0030864d661d92c2f.pdf

像这样传递参数就行。 


网站公告

今日签到

点亮在社区的每一天
去签到