当前位置: 首页 > news >正文

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

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

要求:

  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 });// 创建容器divconst 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

像这样传递参数就行。 

相关文章:

  • 软考复习——知识点软件开发
  • 深入理解Java包装类:自动装箱拆箱与缓存池机制
  • Linux操作系统--进程的创建和终止
  • 缓存 --- Redis的三种高可用模式
  • 重构之去除多余的if-else
  • Kubernetes相关的名词解释Dashboard界面(6)
  • 年化26.9%的稳健策略|polars重构因子计算引擎(python策略下载)
  • 03【变量观】`let`, `mut` 与 Shadowing:理解 Rust 的变量绑定哲学
  • c++STL——list的使用和模拟实现
  • go环境安装mac
  • 02【初体验】安装、配置与 Hello Cargo:踏出 Rust 开发第一步
  • Three.js + React 实战系列-3D 个人主页 :完成 Navbar 导航栏组件
  • Mac-VScode-C++环境配置
  • Git拉分支技巧:从零开始创建并推送分支
  • 深入理解 CICD 与 Jenkins 流水线:从原理到实践
  • 基于Docker+k8s集群的web应用部署与监控
  • 【esp32 点亮led】-解决不能闪烁问题
  • 深入理解Linux中的线程控制:多线程编程的实战技巧
  • 常用算法解析:从基础排序到图论应用
  • 51单片机的原理图和PCB绘制
  • 淄博张店区国资公司挂牌转让所持“假国企”股权,转让底价为1元
  • 澳门世界杯“中日对决”,蒯曼击败伊藤美诚晋级女单决赛
  • 云南省交通发展投资有限责任公司原党委书记、董事长陈以东接受审查调查
  • 中国船协发布关于美对华造船业实施限制措施的严正声明
  • 上海地铁18号线二期长轨贯通,预计今年年底开通初期运营
  • 九部门:将符合条件的家政从业人员纳入公租房等保障范围