赞
踩
"pdfjs-dist": "^4.1.392",
"lodash-es": "^4.17.21",
"vite-plugin-top-level-await": "^1.4.1",
"vue": "^3.4.15",
- <template>
- <div class="pdf-container">
- <canvas :id="`pdf-canvas`" />
- </div>
- <div class="page-tool">
- <div class="page-tool-item" @click="lastPage">上一页</div>
- <div class="page-tool-item" @click="nextPage">下一页</div>
- <div class="page-tool-item">{{state.pageNum}}/{{state.numPages}}</div>
- <div class="page-tool-item" @click="pageZoomOut">放大</div>
- <div class="page-tool-item" @click="pageZoomIn">缩小</div>
- </div>
- </template>
-
- <script lang="ts" setup>
-
- import * as PDFJS from 'pdfjs-dist'
- import * as PdfWorker from 'pdfjs-dist/build/pdf.worker.mjs'
- import { nextTick, ref, Ref, watch, reactive, computed } from 'vue'
- import { isEmpty, debounce } from 'lodash-es'
- (window as any).PdfWorker = PdfWorker
-
-
- const props : any = defineProps({
- pdf: {
- required: true
- }
- })
-
- const state = reactive({
- source: "", //预览pdf文件地址
- pageNum: 1, //当前页面
- scale: 1, // 缩放比例
- numPages: 0, // 总页数
- showpdf: true,
- });
-
- const scaleFun = computed(() => 'transform:scale(${state.scale})')
-
- let pdfDoc : any = null
- const pdfPages : Ref = ref(0)
- const pdfScale : Ref = ref(1.3)
- const loadFile = async (url : any) => {
- // 设定pdfjs的 workerSrc 参数
- // PDFJS.GlobalWorkerOptions.workerSrc = PdfWorker
- const loadingTask = PDFJS.getDocument(url)
- loadingTask.promise.then(async (pdf : any) => {
- pdfDoc = pdf // 保存加载的pdf文件流
- state.numPages = pdfPages.value = pdfDoc.numPages // 获取pdf文件的总页数
- await nextTick(() => {
- renderPage(1) // 将pdf文件内容渲染到canvas
- })
- }).catch((error : any) => {
- //可以用自己组件库弹出提示框
- console.log(error)
- })
- }
-
- const renderPage = (num : any) => {
- pdfDoc.getPage(num).then((page : any) => {
- page.cleanup()
- const canvas : any = document.getElementById(`pdf-canvas`)
- if (canvas) {
- const ctx = canvas.getContext('2d')
- const dpr = window.devicePixelRatio || 1
- const bsr = ctx.webkitBackingStorePixelRatio ||
- ctx.mozBackingStorePixelRatio ||
- ctx.msBackingStorePixelRatio ||
- ctx.oBackingStorePixelRatio ||
- ctx.backingStorePixelRatio ||
- 1
- const ratio = dpr / bsr
- const viewport = page.getViewport({ scale: pdfScale.value })
- canvas.width = viewport.width * ratio
- canvas.height = viewport.height * ratio
- canvas.style.width = viewport.width + 'px'
- canvas.style.height = viewport.height + 'px'
- ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
- const renderContext = {
- canvasContext: ctx,
- viewport: viewport
- }
- page.render(renderContext)
- state.pageNum = num;
- // if (num < pdfPages.value) {
- // renderPage(num + 1)
- // }
- }
- })
- }
- function lastPage() {
- if (state.pageNum > 1) {
- renderPage(state.pageNum - 1);
- }
- }
- function nextPage() {
- if (state.pageNum < state.numPages) {
- renderPage(state.pageNum + 1);
- }
- }
- function pageZoomOut() {
- if (pdfScale.value < 5) {
- pdfScale.value += 0.1;
- renderPage(state.pageNum);
- }
- }
- function pageZoomIn() {
- if (pdfScale.value > 0.5) {
- pdfScale.value -= 0.1;
- renderPage(state.pageNum);
- }
- }
-
- const debouncedLoadFile = debounce((pdf : any) => loadFile(pdf), 1000)
- watch(() => props.pdf, (newValue : any) => {
- !isEmpty(newValue) && debouncedLoadFile(newValue)
- }, {
- immediate: true
- })
- </script>
-
- <style scoped>
- .page-tool {
- position: absolute;
- bottom: 35px;
- padding-left: 15px;
- padding-right: 15px;
- display: flex;
- align-items: center;
- background: rgb(66, 66, 66);
- color: white;
- border-radius: 19px;
- z-index: 100;
- cursor: pointer;
- margin-left: 50%;
- transform: translateX(-50%);
- }
-
- .page-tool-item {
- padding: 8px 15px;
- padding-left: 10px;
- cursor: pointer;
- }
-
- .pdf-container {
- width: 1000px;
- height: 600px;
- resize: both;
- overflow: auto;
- /* 启用滚动条 */
- }
-
- canvas {
- width: 100%;
- pointer-events: none;
- max-height: 100vh;
- /* 设置最大高度为视口高度 */
- }
- </style>
效果: