- 導讀
之前花了一部分時間研究pdf.js 在react 上面的使用髓霞,也寫了五篇文章記錄自己的學習成果筐咧,實現(xiàn)了簡單的pdf預覽和工具欄的翻頁强戴、縮放责鳍,文字復制的簡單功能以及采用虛擬dom的方式優(yōu)化頁數(shù)比較多的情況下渲染慢的問題肩钠。但是有一個關(guān)鍵的功能沒有實現(xiàn)就是pdf文字查找功能泣港,這篇我們就實現(xiàn)一下這個功能
推薦閱讀
- 1暂殖、前端頁面如何優(yōu)雅的顯示PDF:原理說明
- 2、前端頁面如何優(yōu)雅的顯示PDF(上):渲染頁面
- 3当纱、前端頁面如何優(yōu)雅的顯示PDF(中):渲染文本
- 4呛每、前端頁面如何優(yōu)雅的顯示PDF(下):添加工具欄
- 5、【最終篇】前端頁面如何優(yōu)雅的顯示PDF:虛擬滾動
修改渲染方式
如果你看過前幾篇文章坡氯,就會知道我們之前的渲染方式是通過創(chuàng)建一個承載pdf的canvas標簽通過pdf.render()的方式渲染到創(chuàng)建的canvas晨横,如果需要渲染pdf的文字需要創(chuàng)建一個與canvas樣式一樣的div承載文字信息,就可以實現(xiàn)pdf里面文字的右鍵復制功能箫柳。
當然了渲染PDF的方式不止上面一種,上面那種也是官網(wǎng)不太推薦的使用方式手形,因為pdf.js 已經(jīng)實現(xiàn)了一個pdf預覽的viewer,我們可以直接拿來使用
import { PDFLinkService, PDFFindController, PDFViewer } from 'pdfjs-dist/web/pdf_viewer';
const linkService = new PDFLinkService();
const findController = new PDFFindController({
linkService,
});
const newViewer = new PDFViewer({
container: containerRef.current,
linkService,
useOnlyCssZoom: USE_ONLY_CSS_ZOOM,
textLayerMode: TEXT_LAYER_MODE,
// renderer:'svg',
findController,
});
linkService.setViewer(newViewer);
// 設(shè)置初始縮放
newViewer.currentScaleValue = scale;
以上方法注冊了一個pdf預覽的viewer 對應(yīng)的html如下
<div
id="viewerContainer"
className="viewerContainer"
ref={containerRef}
>
<div
className="pdfViewer"
id="innerContainer"
/>
</div>
</div>
以上代碼我也是根據(jù)pdf.js 給出的例子不斷的摸索出來的悯恍,有些內(nèi)容也是一知半解库糠,這里只解釋我會的東西
new PDFViewer({}) 官方給提供的一個viewer,pdf.js中demo例子上面所有的功能都在這個viewer中涮毫,本節(jié)需要有查找功能用到 PDFFindController(),把它作為參數(shù)傳給viewer瞬欧。在后面使用他的 viewer.findController.executeCommand('findagain', searcher);方法進行查找,searcher 查找對象在后面會說明里面內(nèi)容
其他參數(shù)說明
- container 顯示PDF的容器dom罢防,在他里面需要有一個div顯示PDF頁面信息艘虎,一個頁面是一個div,里面div的className建議使用pdfViewer可以直接使用pdf.js提供的樣式篙梢,(也可以自己寫樣式比較費勁)
- linkService 不是很懂顷帖,但是需要^?_?^
- useOnlyCssZoom 是否可以通過css控制頁面的縮放,默認 false
- textLayerMode 是否顯示文字 默認0 不顯示,1 顯示
- findController 傳入文字查找控制器
還有一些別的參數(shù)
- render 渲染的類型 默認是canvas 可選svg
- maxCanvasPixels 最大canvas像素
- enablePrintAutoRotate 啟用打印旋轉(zhuǎn)
如果使用這個viewer 還有一些屬性通過newViewer對象直接訪問
- pagesCount 總頁數(shù)
- pageViewsReady 是否渲染完成渤滞,通過這個屬性判斷是不是所有的頁面都渲染完成,因為pdf渲染是異步的榴嗅,有一些事件監(jiān)聽是需要渲染完成后再進行
- currnetPageNumber 當前頁碼妄呕,翻頁使用
- currentScale 縮放比率,設(shè)置頁面縮放需要用currentScaleValue
- pagesRotation 頁面旋轉(zhuǎn)
- isPageVisible(pageNumber) 傳入頁碼判斷頁面是否不可見
以上就是使用PDFViewer()實現(xiàn)預覽的基本理論和相關(guān)使用到的參數(shù)
開始編碼
進入頁面創(chuàng)建viewer
const initialViewer = (url) => {
const linkService = new PDFLinkService();
const findController = new PDFFindController({
linkService,
});
const newViewer = new PDFViewer({
container: containerRef.current,
linkService,
useOnlyCssZoom: true,
textLayerMode: 1,
// renderer:'svg',
findController,
});
linkService.setViewer(newViewer);
// 設(shè)置初始縮放
newViewer.currentScaleValue = scale;
const loadingTask = pdfjs.getDocument({ url });
loadingTask.promise.then(pdf => {
if (pdf) {
const nums = pdf.numPages
setNumPages(nums)
newViewer.setDocument(pdf);
linkService.setDocument(pdf);
setViewer(newViewer)
// 判斷是否已經(jīng)渲染完畢
const interval = setInterval(() => { loadPdf() }, 1000);
function loadPdf() {
if (newViewer.pageViewsReady) {
// ... 渲染完成操作
}
}
}
})
}
對應(yīng)的html 就是上面的代碼嗽测,其中container是通過useRef 創(chuàng)建的
const containerRef = useRef(null)
實現(xiàn)翻頁和縮放
- 翻頁
翻頁的實現(xiàn)就是改變上面提到的currentPageNumber值
viewer.currentPageNumber = num
- 縮放
修改縮放的方式也是通過修改屬性值改變 currentScaleValue
viewer.currentScaleValue = newScale;
縮放的參數(shù)是有固定格式的绪励,如果是數(shù)字的話可以隨便多少就行,但是縮放也是可以是字符串形式的唠粥,如下:
<select
value={`${scale}`}
onChange={e => {
const newScale = e.target.value
viewer.currentScaleValue = newScale;
setScale(newScale)
}}
>
<option value="auto">自動縮放</option>
<option value="page-actual">實際大小</option>
<option value="page-fit">適合頁面</option>
<option value="page-width">適合頁寬</option>
<option value="0.50">50%</option>
<option value="0.75">75%</option>
<option value="1">100%</option>
<option value="1.25">125%</option>
<option value="1.50">150%</option>
<option value="1.75">175%</option>
<option value="2">200%</option>
<option value="3">300%</option>
<option value="4">400%</option>
</select>
文字查找
- 定義查找對像
const [searcher = {}, setSearcher] = useState({
phraseSearch: true, // 是否短語查找
query: '', // 查詢字段
findPrevious: true, // 是否循環(huán)查找
highlightAll: true, // 是否高亮
});
用戶輸入的時候改變
<input
type="text"
id="searchInput"
onChange={e => {
setSearcher({
...searcher,
query: e.target.value,
});
}}
/>
通過事件監(jiān)聽回車事件進行查找
# 使用 umi hooks 進行事件監(jiān)聽
useKeyPress('enter', event => {
viewer.findController.executeCommand('findagain', searcher);
});
回車查找進行了頁面的跳轉(zhuǎn)和循環(huán)查找
其他事件監(jiān)聽
- 滾動頁碼 ,滾動時監(jiān)聽滾動到第幾頁
document.addEventListener('pagechanging', function (evt) {
const page = evt.detail.pageNumber;
changePage(page)
})
const changePage = (num) => {
viewer.currentPageNumber = num
setCurrentPageNumber(num)
}
- 查詢統(tǒng)計
useEffect(() => {
window.addEventListener('updatefindcontrolstate', e => {
setMatchesCount(e.detail.matchesCount);
});
window.addEventListener('updatefindmatchescount', e => {
setMatchesCount(e.detail.matchesCount);
})
})
matchesCount: {
current: // 當前查找的第幾項
total: // 總共有幾項
}