极简PDF预览,单页垂直滚动加载,目前上线后没有遇到白屏问题
本次用到 pdf.min.js pdf.worker.js 1.1.469 版本 使用高版本 web-view 一些方法不支持。
并且使用cover-view 标签 按钮可以悬浮在web-view 之上 必须真机调试才有效果
监听滚动事件,快速来回滚动 页码数会错乱 暂时没有好的方法就去掉了
H5:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PDF</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
background-color: #f5f5f5;
overflow: hidden;
}
#pdf-container {
position: relative;
width: 100vw;
height: 95vh;
overflow-y: auto;
/* 显示垂直滚动条 */
background-color: white;
}
.spacer {
width: 100%;
height: 20px;
/* 空白行的高度 */
background-color: #f5f5f5;
/* 空白行的背景颜色 */
}
.pdf-page {
width: 100%;
display: block;
}
#page-info {
position: fixed;
top: 12px;
right: 12px;
background: rgba(0, 0, 0, 0.6);
color: white;
padding: 6px 12px;
border-radius: 16px;
font-size: 14px;
z-index: 1000;
user-select: none;
}
</style>
</head>
<body>
<div id="pdf-container">
<!-- <div id="page-info">第 0 页 / 共 0 页</div> -->
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/1.1.469/pdf.min.js"></script>
<script>
let pdfDoc = null;
let currentPage = 1;
let totalPages = 0;
let scale = 1.5;
let pageNum = 1;
let isLoading = false; // 标记是否正在加载页面,防止重复加载
PDFJS.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/1.1.469/pdf.worker.js';
const pdfContainer = document.getElementById('pdf-container');
const pageInfo = document.getElementById('page-info');
function getUrlParam(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); // 构造一个含有目标参数的正则表达式对象
var param = decodeURIComponent(window.location.search);
var r = param.substr(1).match(reg); // 匹配目标参数
//window.console.log(r)
if (r != null) return r[2];
return null; // 返回参数值
};
//标题
let name = getUrlParam('n');
document.title = decodeURIComponent(name);
// 加载 PDF
async function loadPDF(url) {
try {
pdfDoc = await PDFJS.getDocument(url).promise;
totalPages = pdfDoc.numPages;
renderPage(currentPage);
} catch (error) {
console.error('加载 PDF 失败:', error);
}
}
// 渲染指定页面
async function renderPage(pageNum) {
if (!pdfDoc || isLoading) return;
isLoading = true; // 标记正在加载
const page = await pdfDoc.getPage(pageNum);
const viewport = page.getViewport(1);
// 创建新的 canvas 元素
const canvas = document.createElement('canvas');
canvas.className = 'pdf-page page-' + pageNum;
canvas.width = viewport.width;
canvas.height = viewport.height;
const ctx = canvas.getContext('2d');
const renderContext = {
canvasContext: ctx,
viewport: viewport
};
await page.render(renderContext).promise;
// 将新 canvas 插入到容器中
pdfContainer.appendChild(canvas);
// 如果不是最后一页,添加一个空白行
if (pageNum < totalPages) {
const spacer = document.createElement('div');
spacer.className = 'spacer';
pdfContainer.appendChild(spacer);
}
// 更新页码信息
// pageInfo.textContent = `第 ${pageNum} 页 / 共 ${totalPages} 页`;
// 如果是第一页,滚动到顶部
if (pageNum === 1) {
pdfContainer.scrollTop = 0;
}
isLoading = false; // 加载完成,取消标记
}
监听滑动事件
/* 触摸滑动事件(移动端) */
let startY = 0;
window.addEventListener('touchstart', e => { startY = e.touches[0].clientY });
window.addEventListener('touchend', e => {
const dy = startY - e.changedTouches[0].clientY;
if (Math.abs(dy) > 60) go(dy > 0 ? 1 : -1);
});
PC 端滚轮事件
/* 滚轮/触摸上下滑动翻页 */
let busy = false;
const scrollDelay = 3000; // 防抖时间 ms
let lastCall = 0;
function go(delta) { // delta = 1 下一页;-1 上一页
const now = new Date().getTime();
const target = pageNum + delta;
var info = document.getElementsByClassName('pdf-page page-' + target);
if (info.length > 0) {
return;
}
if (now - lastCall < scrollDelay ||target < 1 || target > totalPages || busy) return;
lastCall = now;
pageNum = target;
busy = true;
nextPage(pageNum).finally(() => busy = false);
}
/* 滚轮事件 */
window.addEventListener('wheel', e => {
go(e.deltaY > 0 ? 1 : -1);
}, { passive: true });
async function nextPage(pageNum) {
await renderPage(pageNum);
}
// 初始化加载 PDF
loadPDF(getUrlParam('u'));
</script>
</body>
</html>
H5 部署在业务域名下 ,web-view 调用
小程序 :
.material-operation {
position: fixed;
bottom: 0;
z-index: 2;
.info-btn {
height: 120rpx;
display: flex;
align-items: center;
image,cover-image {
width: 36rpx;
height: 36rpx;
margin-right: 2rpx;
}
}
}
<web-view :src="info.viewUrl">
<cover-view class="material-operation">
<cover-view class="info-btn" @click="fnCollect" style="width: 130rpx;"/>
</cover-view>
</web-view>