目录
1、安装并使用 pdfjs-dist、pubsub-js,初始化工作
6、点击左侧略缩图列表中的某略缩页,中间的展示区滚动到该页的位置
0、简单效果展示
1、安装并使用 pdfjs-dist、pubsub-js,初始化工作
安装pdfjs-dist:
npm i pdfjs-dist
引入并配置工作者源文件:
import * as PDF from 'pdfjs-dist'
import workerSrc from 'pdfjs-dist/build/pdf.worker.entry'
PDF.GlobalWorkerOptions.workerSrc = workerSrc;
安装 pubsub-js(消息订阅与发布):
npm i pubsub-js
引入到项中:
import pubsub from 'pubsub-js'
定义一个用于包裹绘制 pdf 的 canvas 列表的容器:
// 承装所有 canvas 的容器的类名
const allCanvasParentNodeClassName = 'canvas-parent-container';
2、使用 pdfjs-dist 绘制给定PDF的首页或整体
封装函数 displayPDF 实现该功能。
该函数接收六个参数:
(1)给定 pdf 的地址;
(2)承载要展示pdf区域的父元素(dom);
(3)是否展示完整个pdf(false 就是只展示 pdf 的首页);
(4)指定 canvas 的类名(PDF的每一页都会在一个canvas画布上绘制,为这一组画布确定一个共同的类名);
(5)自定义pdf的缩放倍数;
(6)pdf渲染完成后的一个回调函数。
/*
绘制整个pdf,一个canvas绘制一页
传入 pdf 的地址、父容器标签元素、是否展示全部(false 即展示首页) 、指定canvas类名, 自定义缩放大小、自定义内联样式
*/
function displayPDF(pdfURL, parentNode, isAllPages, canvasClassName, customScale, callback){
// 使用 PDF 加载指定的 .pdf 文档
const loadingTask = PDF.getDocument(pdfURL);
loadingTask.promise.then(async pdf=>{
// pdf._pdfInfo.numPages 为 pdf 的总页数,isAllPage 为boolean值,表示是否展示全部
const pageNumber = isAllPages ? pdf._pdfInfo.numPages : 1;
/*
当前显示设备的物理像素分辨率与CSS像素分辨率之比,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素
<canvas>可能在视网膜屏幕上显得太模糊。 使用window.devicePixelRatio确定应添加多少额外的像素密度以使图像更清晰。
*/
const outputScale = window.devicePixelRatio || 1;
const allCanvasParentNode = document.createElement('div');
allCanvasParentNode.className = allCanvasParentNodeClassName;
allCanvasParentNode.style = `
width: 100%;
height: 100%;
overflow: auto;
padding: 0;
margin: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
`
// 将 canvas 列表的容器添加到给定的容器中
parentNode.appendChild(allCanvasParentNode);
// 根据 isAllPages 确定渲染多少页
mainAreaAsyncRender(pdf, pageNumber, 1, customScale, canvasClassName, outputScale, allCanvasParentNode, callback);
})
}
其中 mainAreaAsyncRender 函数为解决 pdf 页面异步渲染的一个方案。即 使 PDF 的每一页在渲染完成后,都可以直接呈现到页面上,不必等待 PDF 的所有页面都渲染完成再一次过呈现到页面上(这种一般直接使用 for 循环来完成),避免用户过多的等待。
该函数接收以下几个参数:
(1)使用 pdfjs-dist 的 getDocument 加载后进行期约构造成功后返回的 pdf 对象;
(2)当前 pdf 的总页数;
(3)当前所绘制的是第几页;
(4)自定义的缩放比例;
(5)自定义的 canvas 类名;
(6)当前显示设备的物理像素分辨率与CSS像素分辨率之比;
(7)承装所有 canvas 的容器(dom元素);
(8)所有页面渲染完成后执行的回调函数(这里设置回调函数有一个参数,为该pdf的总页数,用于展示pdf的总页数);
// 主要展示区 异步渲染解决方案,使之一上来就有页面
async function mainAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName, outputScale, allCanvasParentNode, callback){
// 获取 pdf 指定页
let page = await pdf.getPage(i);
// 设置 canvas 整体缩放的倍数
let scale = customScale || 1.4;
let viewport = page.getViewport({ scale });
// 创建 canvas 共同的容器
let canvas = document.createElement('canvas');
canvas.className = canvasClassName || '';
let context = canvas.getContext("2d");
// 设置 canvas 和内容 大小
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
canvas.style.margin = '10px 0';
let transform = (outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null);
// 将指定页渲染到 canvas
let renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
// 将当前 canvas(当前页) 添加到父容器中
allCanvasParentNode.appendChild(canvas);
i++;
if(i <= pageNumber){
requestAnimationFrame(()=>
mainAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName, outputScale, allCanvasParentNode, callback));
}else{
// 传入 pdf 总页数
if(callback) callback(pdf._pdfInfo.numPages);
// 发布渲染完成的消息
pubsub.publish('renderFinish');
}
};
渲染完成后, 发布消息 ‘renderFinish’ 告诉组件渲染已完成。
3、侧边的略缩图列表
/*
侧边导航略缩图
参数:pdf地址、父容器元素、中心展示区域设置的canvas的类名,设置当前canvas的类名,自定义当前canvas列表的缩放大小,回调函数
*/
function displayPDFLeftNav(pdfURL, parentNode, centerAreaCanvasClassName, canvasClassName, customScale){
// 使用 PDF 加载指定的 .pdf 文档
const loadingTask = PDF.getDocument(pdfURL);
loadingTask.promise.then(async pdf=>{
// pdf._pdfInfo.numPages 为 pdf 的总页数,isAllPage 为boolean值,表示是否展示全部
const pageNumber = pdf._pdfInfo.numPages;
/*
当前显示设备的物理像素分辨率与CSS像素分辨率之比,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素
<canvas>可能在视网膜屏幕上显得太模糊。 使用window.devicePixelRatio确定应添加多少额外的像素密度以使图像更清晰。
*/
const outputScale = window.devicePixelRatio || 1;
const allCanvasParentNode = document.createElement('div');
allCanvasParentNode.className = allCanvasParentNodeClassName;
allCanvasParentNode.style = `
width: 100%;
height: 100%;
overflow: auto;
padding: 0;
margin: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
padding: 10px 0;
`
// 将 canvas 列表的容器添加到给定的容器中
parentNode.appendChild(allCanvasParentNode);
// 根据 isAllPages 确定渲染多少页
let i = 1;
navAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName, outputScale, allCanvasParentNode, centerAreaCanvasClassName);
})
}
其中 navAreaAsyncRender 函数也是一个异步的渲染方案。
// 侧边略缩图列表 异步渲染解决方案
async function navAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName,
outputScale, allCanvasParentNode, centerAreaCanvasClassName){
// 获取 pdf 指定页
let page = await pdf.getPage(i);
// 设置 canvas 整体缩放的倍数
let scale = customScale || 0.35;
let viewport = page.getViewport({ scale });
// 创建 canvas 共同的容器
let canvas = document.createElement('canvas');
canvas.className = canvasClassName || 'canvas-left-nav-class-name';
let context = canvas.getContext("2d");
// 设置 canvas 内容 大小,添加悬浮事件
canvas.id = `canvas-l-${i}`;
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
canvas.style.border = '1px solid #888';
canvas.style.margin = '20px 0 10px 0';
canvas.addEventListener('mouseover',()=>{
canvas.style.cursor = 'pointer';
canvas.style.boxShadow = '2px 2px 1px 1px #888';
});
canvas.addEventListener('mouseleave',()=>{
canvas.style.cursor = '';
canvas.style.boxShadow = '';
})
canvas.addEventListener('click',(event)=>{
clickNavToSee(canvas, centerAreaCanvasClassName);
})
let transform = (outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null);
// 将指定页渲染到 canvas
let renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
// 底部的页码标注
let span = document.createElement('span');
span.innerText = i;
span.style = 'font-size: 0.85rem;color: #888;';
// 将当前 canvas(当前页) 添加到父容器中
allCanvasParentNode.appendChild(canvas);
allCanvasParentNode.appendChild(span);
i ++;
if(i < pageNumber){
requestAnimationFrame(()=>
navAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName,
outputScale, allCanvasParentNode, centerAreaCanvasClassName));
}
}
4、放大或缩小中心区域的 PDF
最好改变 customScale 进行异步重绘操作。
如果直接使用 document.querySelecterAll 方法 + canvas 类名(这个canvas类名是自定义的,即函数displayPDF、resizePDF 中要传的参数 canvasClassName)的方式获取到所有的canvas,然后通过遍历的方式给他们分别设置属性 taransform scale 来实现放大或缩小的效果,会导致PDF变模糊而影响阅览体验。
/*
在已经绘制过pdf的基础上 放大 canvas 和 pdf (重绘)
直接修改样式属性 transform scale 会导致 pdf 失真,需要重绘
传入 pdf地址、canvas类名、自定义缩放大小
*/
function resizePDF(pdfURL, canvasClassName, customScale){
// 使用 PDF 加载指定的 .pdf 文档
const loadingTask = PDF.getDocument(pdfURL);
loadingTask.promise.then(async pdf=>{
const canvasList = document.querySelectorAll(`.${canvasClassName}`);
/*
当前显示设备的物理像素分辨率与CSS像素分辨率之比,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素
<canvas>可能在视网膜屏幕上显得太模糊。 使用window.devicePixelRatio确定应添加多少额外的像素密度以使图像更清晰。
*/
const outputScale = window.devicePixelRatio || 1;
// 根据 isAllPages 确定渲染多少页(从 0 开始)
resizeAsyncRender(pdf, canvasList, 0, customScale, outputScale);
})
}
其中 resizeAsyncRender 为重绘时的异步解决方法。
// 根据放大和缩小的比例重绘中间pdf展示区
async function resizeAsyncRender(pdf, canvasList, i, customScale, outputScale){
let canvas = canvasList[i];
// 获取 pdf 指定页
let page = await pdf.getPage(i + 1);
// 设置 canvas 整体缩放的倍数
let scale = customScale || 1.4;
let viewport = page.getViewport({ scale });
let context = canvas.getContext("2d");
// 设置 canvas 和内容 大小
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
let transform = (outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null);
// 将指定页渲染到 canvas
let renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
i++;
if(i < canvasList.length){
requestAnimationFrame(()=>resizeAsyncRender(pdf, canvasList, i, customScale, outputScale));
}else{
// 发布重新渲染完成的消息
pubsub.publish("resizeFinish");
}
}
当重新渲染完成后,将发布 “resizeFinish” 消息传递重新渲染已完成的信号。
5、滚动到指定PDF页面的位置
/*
滚动到指定的pdf页面位置(num 为 pdf 第几页 从 1 开始)
*/
function scrollIntoPage(num, canvasClassName){
document.querySelectorAll(`.${canvasClassName}`)[num - 1].scrollIntoView({
behavior:'smooth',
});
}
6、点击左侧略缩图列表中的某略缩页,中间的展示区滚动到该页的位置
/*
点击左侧的略缩栏的某页,将该页滚动到展示区
*/
function clickNavToSee(canvasNode, centerAreaCanvasClassName){
let index = canvasNode.id.split('-')[2].trim();
document.querySelectorAll(`.${centerAreaCanvasClassName}`)[index - 1].scrollIntoView({
behavior:'smooth'
});
}
7、完整实现代码
注意:本地的 pdf 要放在public文件夹下;
(7-1)displayPDF.js
import * as PDF from 'pdfjs-dist'
import pubsub from 'pubsub-js'
import workerSrc from 'pdfjs-dist/build/pdf.worker.entry'
PDF.GlobalWorkerOptions.workerSrc = workerSrc;
// 承装所有 canvas 的容器的类名
const allCanvasParentNodeClassName = 'canvas-parent-container';
/*
绘制整个pdf,一个canvas绘制一页
传入 pdf 的地址、父容器标签元素、是否展示全部(false 即展示首页) 、指定canvas类名, 自定义缩放大小、自定义内联样式
*/
function displayPDF(pdfURL, parentNode, isAllPages, canvasClassName, customScale, callback){
// 使用 PDF 加载指定的 .pdf 文档
const loadingTask = PDF.getDocument(pdfURL);
loadingTask.promise.then(async pdf=>{
// pdf._pdfInfo.numPages 为 pdf 的总页数,isAllPage 为boolean值,表示是否展示全部
const pageNumber = isAllPages ? pdf._pdfInfo.numPages : 1;
/*
当前显示设备的物理像素分辨率与CSS像素分辨率之比,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素
<canvas>可能在视网膜屏幕上显得太模糊。 使用window.devicePixelRatio确定应添加多少额外的像素密度以使图像更清晰。
*/
const outputScale = window.devicePixelRatio || 1;
const allCanvasParentNode = document.createElement('div');
allCanvasParentNode.className = allCanvasParentNodeClassName;
allCanvasParentNode.style = `
width: 100%;
height: 100%;
overflow: auto;
padding: 0;
margin: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
`
// 将 canvas 列表的容器添加到给定的容器中
parentNode.appendChild(allCanvasParentNode);
// 根据 isAllPages 确定渲染多少页
mainAreaAsyncRender(pdf, pageNumber, 1, customScale, canvasClassName, outputScale, allCanvasParentNode, callback);
})
}
/*
侧边导航略缩图
参数:pdf地址、父容器元素、中心展示区域设置的canvas的类名,设置当前canvas的类名,自定义当前canvas列表的缩放大小,回调函数
*/
function displayPDFLeftNav(pdfURL, parentNode, centerAreaCanvasClassName, canvasClassName, customScale){
// 使用 PDF 加载指定的 .pdf 文档
const loadingTask = PDF.getDocument(pdfURL);
loadingTask.promise.then(async pdf=>{
// pdf._pdfInfo.numPages 为 pdf 的总页数,isAllPage 为boolean值,表示是否展示全部
const pageNumber = pdf._pdfInfo.numPages;
/*
当前显示设备的物理像素分辨率与CSS像素分辨率之比,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素
<canvas>可能在视网膜屏幕上显得太模糊。 使用window.devicePixelRatio确定应添加多少额外的像素密度以使图像更清晰。
*/
const outputScale = window.devicePixelRatio || 1;
const allCanvasParentNode = document.createElement('div');
allCanvasParentNode.className = allCanvasParentNodeClassName;
allCanvasParentNode.style = `
width: 100%;
height: 100%;
overflow: auto;
padding: 0;
margin: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
padding: 10px 0;
`
// 将 canvas 列表的容器添加到给定的容器中
parentNode.appendChild(allCanvasParentNode);
// 根据 isAllPages 确定渲染多少页
let i = 1;
navAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName, outputScale, allCanvasParentNode, centerAreaCanvasClassName);
})
}
/*
在已经绘制过pdf的基础上 放大 canvas 和 pdf (重绘)
直接修改样式属性 transform scale 会导致 pdf 失真,需要重绘
传入 pdf地址、canvas类名、自定义缩放大小
*/
function resizePDF(pdfURL, canvasClassName, customScale){
// 使用 PDF 加载指定的 .pdf 文档
const loadingTask = PDF.getDocument(pdfURL);
loadingTask.promise.then(async pdf=>{
const canvasList = document.querySelectorAll(`.${canvasClassName}`);
/*
当前显示设备的物理像素分辨率与CSS像素分辨率之比,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素
<canvas>可能在视网膜屏幕上显得太模糊。 使用window.devicePixelRatio确定应添加多少额外的像素密度以使图像更清晰。
*/
const outputScale = window.devicePixelRatio || 1;
// 根据 isAllPages 确定渲染多少页(从 0 开始)
resizeAsyncRender(pdf, canvasList, 0, customScale, outputScale);
})
}
/*
滚动到指定的pdf页面位置(num 为 pdf 第几页 从 1 开始)
*/
function scrollIntoPage(num, canvasClassName){
document.querySelectorAll(`.${canvasClassName}`)[num - 1].scrollIntoView({
behavior:'smooth',
});
}
/*
点击左侧的略缩栏的某页,将该页滚动到展示区
*/
function clickNavToSee(canvasNode, centerAreaCanvasClassName){
let index = canvasNode.id.split('-')[2].trim();
document.querySelectorAll(`.${centerAreaCanvasClassName}`)[index - 1].scrollIntoView({
behavior:'smooth'
});
}
/*
获取 pdf 的页面总数,就是获取 canvas 的总数
*/
// function getPDFPageSum(canvasClassName){
// return document.querySelectorAll(`.${canvasClassName}`).length;
// }
// 主要展示区 异步渲染解决方案,使之一上来就有页面
async function mainAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName, outputScale, allCanvasParentNode, callback){
// 获取 pdf 指定页
let page = await pdf.getPage(i);
// 设置 canvas 整体缩放的倍数
let scale = customScale || 1.4;
let viewport = page.getViewport({ scale });
// 创建 canvas 共同的容器
let canvas = document.createElement('canvas');
canvas.className = canvasClassName || '';
let context = canvas.getContext("2d");
// 设置 canvas 和内容 大小
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
canvas.style.margin = '10px 0';
let transform = (outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null);
// 将指定页渲染到 canvas
let renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
// 将当前 canvas(当前页) 添加到父容器中
allCanvasParentNode.appendChild(canvas);
i++;
if(i <= pageNumber){
requestAnimationFrame(()=>
mainAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName, outputScale, allCanvasParentNode, callback));
}else{
// 传入 pdf 总页数
if(callback) callback(pdf._pdfInfo.numPages);
// 发布渲染完成的消息
pubsub.publish('renderFinish');
}
};
// 侧边略缩图列表 异步渲染解决方案
async function navAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName,
outputScale, allCanvasParentNode, centerAreaCanvasClassName){
// 获取 pdf 指定页
let page = await pdf.getPage(i);
// 设置 canvas 整体缩放的倍数
let scale = customScale || 0.35;
let viewport = page.getViewport({ scale });
// 创建 canvas 共同的容器
let canvas = document.createElement('canvas');
canvas.className = canvasClassName || 'canvas-left-nav-class-name';
let context = canvas.getContext("2d");
// 设置 canvas 内容 大小,添加悬浮事件
canvas.id = `canvas-l-${i}`;
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
canvas.style.border = '1px solid #888';
canvas.style.margin = '20px 0 10px 0';
canvas.addEventListener('mouseover',()=>{
canvas.style.cursor = 'pointer';
canvas.style.boxShadow = '2px 2px 1px 1px #888';
});
canvas.addEventListener('mouseleave',()=>{
canvas.style.cursor = '';
canvas.style.boxShadow = '';
})
canvas.addEventListener('click',(event)=>{
clickNavToSee(canvas, centerAreaCanvasClassName);
})
let transform = (outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null);
// 将指定页渲染到 canvas
let renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
// 底部的页码标注
let span = document.createElement('span');
span.innerText = i;
span.style = 'font-size: 0.85rem;color: #888;';
// 将当前 canvas(当前页) 添加到父容器中
allCanvasParentNode.appendChild(canvas);
allCanvasParentNode.appendChild(span);
i ++;
if(i < pageNumber){
requestAnimationFrame(()=>
navAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName,
outputScale, allCanvasParentNode, centerAreaCanvasClassName));
}
}
// 根据放大和缩小的比例重绘中间pdf展示区
async function resizeAsyncRender(pdf, canvasList, i, customScale, outputScale){
let canvas = canvasList[i];
// 获取 pdf 指定页
let page = await pdf.getPage(i + 1);
// 设置 canvas 整体缩放的倍数
let scale = customScale || 1.4;
let viewport = page.getViewport({ scale });
let context = canvas.getContext("2d");
// 设置 canvas 和内容 大小
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
let transform = (outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null);
// 将指定页渲染到 canvas
let renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
i++;
if(i < canvasList.length){
requestAnimationFrame(()=>resizeAsyncRender(pdf, canvasList, i, customScale, outputScale));
}else{
// 发布重新渲染完成的消息
pubsub.publish("resizeFinish");
}
}
export {
displayPDF,
displayPDFLeftNav,
resizePDF,
scrollIntoPage,
// getPDFPageSum
}
(7-2)showPDF.jsx
import React from 'react'
import {displayPDF,resizePDF,scrollIntoPage,displayPDFLeftNav} from './displayPDF';
import pubsub from 'pubsub-js';
import './ShowPDF.scss'
export default class ShowPDF extends React.Component{
leftNav = React.createRef();
controlArea = React.createRef();
state = {
// 注意 pdf 要放在 public 文件夹下
PDFURL:'/中国计算机学会推荐国际学术会议和期刊目录-2019.pdf',
leftNavCanvasClassName:'left-canvas', // 左侧略缩图列表各canvas的类名(共同
centerCanvasClassName:'can', // 主要展示区各canvas的类名(共同
controlDisable:false, // 是否允许操作
pdfScaleSize:1.4, // pdf 展示的缩放比例
totalPage:0, // pdf 的页数
targetPage:1, // 用户输入,要跳转到多少页
}
render(){
return(
<div className='container'>
<div ref={this.controlArea} className='control-area'>
<button disabled={this.state.controlDisable} onClick={this.changeSketch}>展开或收起略缩图</button>
<button disabled={this.state.controlDisable} onClick={this.larger}>放大</button>
<button disabled={this.state.controlDisable} onClick={this.smaller}>缩小</button>
<div className='inp-area'>
<input disabled={this.state.controlDisable} type="number" placeholder='to' onChange={this.toPageChange} min={1} max={this.state.totalPage}/>
/
<input type="text" disabled value={this.state.totalPage}/>
<button disabled={this.state.controlDisable} onClick={this.toThisPage}>到指定页</button>
</div>
<button disabled={this.state.controlDisable} onClick={this.toTop}>返回顶部</button>
</div>
<div className='display-area'>
{/* 左侧的略缩图 */}
<div ref={this.leftNav} className='left-nav'></div>
{/* 中间的展示区 */}
<div className='center-area'></div>
</div>
</div>
)
}
componentDidMount(){
let {PDFURL, leftNavCanvasClassName, pdfScaleSize, centerCanvasClassName} = this.state;
// 左侧的略缩图列表
displayPDFLeftNav(PDFURL, document.querySelector('.left-nav'), centerCanvasClassName,
leftNavCanvasClassName, 0.35);
// 中间的主要展示区域
displayPDF(PDFURL, document.querySelector('.center-area'), true,
centerCanvasClassName, pdfScaleSize, (pageSum)=>{
this.setState({totalPage:pageSum});
});
// 当中间展示区域初次渲染完成,顶部的控制部件显现
pubsub.subscribe('renderFinish',()=>{
this.controlArea.current.classList.add('control-area-show');
pubsub.unsubscribe('renderFinish');
})
// 监听中间展示区域canvas的放大缩小,完成操作时,设置控制部件可操作
pubsub.subscribe('resizeFinish',()=>{
this.setState({controlDisable: false});
})
}
componentWillUnmount(){
pubsub.unsubscribe('resizeFinish');
}
// 返回顶部
toTop = ()=>{
scrollIntoPage(1, this.state.centerCanvasClassName);
}
// 展开或收起略缩图
changeSketch = () =>{
this.leftNav.current.classList.toggle('left-nav-change');
}
// 用户输入,将要跳转到指定页
toPageChange = (event)=>{
let val = event.target.value;
val = parseInt(val);
this.setState({targetPage:val});
}
// 点击按钮 根据用户输入页数 跳转到指定页面
toThisPage = ()=>{
scrollIntoPage(this.state.targetPage, this.state.centerCanvasClassName);
}
// 整体放大pdf
larger = ()=>{
let size = this.state.pdfScaleSize;
if(size >= 3){ // 阈值
return;
}
this.setState({controlDisable: true});
size += 0.2;
this.setState({pdfScaleSize:size});
resizePDF(this.state.PDFURL, this.state.centerCanvasClassName, size);
}
// 整体缩小pdf
smaller = ()=>{
let size = this.state.pdfScaleSize;
if(size <= 0.6){ // 阈值
return;
}
this.setState({controlDisable: true});
size -= 0.2;
this.setState({pdfScaleSize:size});
resizePDF(this.state.PDFURL, this.state.centerCanvasClassName, size);
}
}
(7-3)showPDF.scss
.container{
width: 100vw;
height: 100vh;
min-width: 800px;
border: 1px solid black;
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
.control-area{
flex-grow: 2;
box-sizing: border-box;
width: 100%;
height: 0px;
background-color: rgb(255, 255, 255);
border-bottom: 2px solid black;
display: flex;
justify-content: center;
align-items: center;
transition: height 300ms ease, opacity 300ms ease;
opacity: 0;
// overflow: hidden;
.inp-area{
margin: 0 70px;
input{
width: 50px;
text-align: center;
outline: none;
margin: 0 10px;
}
}
button{
margin: 0 8px;
}
}
.control-area-show{
height: 45px;
padding: 8px;
opacity: 1;
}
.display-area{
flex-grow: 1;
flex-shrink: 1;
display: flex;
overflow: hidden;
.left-nav{
width: 20%;
height: 100%;
transition: width 300ms ease;
background-color: #222222f6;
}
.left-nav-change{
width: 0;
overflow: hidden;
}
.center-area{
flex-grow: 1;
display: flex;
justify-content: center;
align-items: center;
transition: width 300ms ease;
background-color: #222;
}
}
}
(7-4)App.js
import React from 'react';
// 引入展示 pdf 的组件
import ShowPDF from './components/ShowPDF/ShowPDF'
export default class App extends React.Component{
render(){
const style = {
width:'100vw',
padding:'0',
margin:'0',
display:'flex',
justifyContent:'center',
alignItem:'center'
}
return(
<div style={style}>
<ShowPDF/>
</div>
)
}
}
参考:
[1]、pdf.js/helloworld.html at master · mozilla/pdf.js · GitHub
[2]、Examples