PDFJS踩坑日记


项目中需要做一个pdfjs预览的功能

封了一个插件

<template>
  <div ref="pdfBoxRef" class="pdf-box" style="max-width: 794px; margin: 0 auto">
    <div class="pdf-center-box">
      <div class="post-flex flex-between">
        <div>
          <el-button class="el-icon-zoom-in" title="放大" type="text" :disabled="btnDisabled" @click="toScale(5)">
            放大
          </el-button>
          <el-button class="el-icon-refresh" title="重置" type="text" :disabled="btnDisabled" @click="toScale(0)">
            重置
          </el-button>
          <el-button class="el-icon-zoom-out" title="缩小" type="text" :disabled="btnDisabled" @click="toScale(-5)">
            缩小
          </el-button>
        </div>
        <el-button v-show="download" class="el-icon-download" type="text" size="mini" @click="handleDownload">
          下载
        </el-button>
      </div>
      <div ref="contentRef" class="left">
        <div ref="pdfViewerRef" class="pdfView"></div>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import * as PDFJS from "pdfjs-dist";
PDFJS.GlobalWorkerOptions.workerSrc = 'pdfjs-dist/build/pdf.worker';
// PDFJS.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/build/pdf.worker.min.js';

const props = defineProps({
  url: {
    type: String,
    default: "",
  },
  download: {
    type: Boolean,
    default: false,
  },
  loading: {
    type: Boolean,
    default: false,
  },
});
const pdfLoading = ref(true) 
const pdfLoadingPercent = ref(0)
const scale = ref(100)

const btnDisabled = computed(() => {
  return pdfLoading.value || props.loading
})

const loadNewPdf = (url) => { 
  unmountPdf();
  setTimeout(() => {
    hanle(url);
  }, 200); // 等待一小段时间再加载新的 PDF,防止渲染冲突
};

watch(() => props.url, (val) => { 
  if (val) {
    nextTick(()=>{
      loadNewPdf(val);
    })
  }
},{immediate: true})


const pdfViewerRef = ref()
const unmountPdf = () => {
  const pdfContainer = pdfViewerRef;
  if (pdfContainer) {
    pdfContainer.value.innerHTML = "";
  }
}
const pdfBoxRef = ref()
const  renderPDF = (pdf, i, id) =>{
  pdf.getPage(i).then((page) => {
    const [pageWidth] = page.view.slice(2); // 从索引 2 开始提取
    const maxWidth = Number(pdfBoxRef.value.style.maxWidth.split("px")[0]);
    const viewport = page.getViewport({
      // 计算默认缩放比
      scale: maxWidth / pageWidth,
      rotation: 0, // 不旋转
      dontFlip: false, // 允许翻转(默认)
    });

    // 准备用于渲染的 canvas 元素
    const canvas = pdfViewerRef.value.querySelector(`.${id}`);
    const context = canvas.getContext("2d");
    canvas.width = viewport.width;
    canvas.height = viewport.height;
    // 将 PDF 页面渲染到 canvas 上下文中
    const renderContext = {
      canvasContext: context,
      viewport: viewport,
    };
    page.render(renderContext);
  });
}

const handleDownload =() =>{
  window.open(props.url);
}

const toScale = (num) => {
  if (num === 0) {
    scale.value = 100;
  } else {
    scale.value += num;
  }
  let domArray = pdfViewerRef.value.querySelectorAll(".pdfClass");
  domArray.forEach((item) => {
    let widthTemp = item.width;
    let heightTemp = item.height;
    item.style.width = parseInt(widthTemp) * (scale.value / 100) + "px";
    item.style.height = parseInt(heightTemp) * (scale.value / 100) + "px";
  });
}

const hanle = (url) => {
  pdfLoadingPercent.value = 0;
  pdfLoading.value = true;
  const loadingTask = PDFJS.getDocument({
    url,
    rangeChunkSize: 65536, // 64KB 分块加载
    disableRange: false, // 确保启用 Range
    disableStream: false, // 允许流式加载
  });
  loadingTask.onProgress = ({ loaded, total }) => {
    pdfLoadingPercent.value = Math.min(100, parseFloat((loaded / total * 100).toFixed(2)));
  };
  loadingTask.promise.then((pdf) => {
    // 加载完毕
    pdfLoading.value = false;

    const idTemplate = "cw-pdf-";
    const pageNum = pdf.numPages;
    // 根据页码创建画布
    createSeriesCanvas(pageNum, idTemplate);
    // 将pdf渲染到画布上去
    for (let i = 1; i <= pageNum; i++) {
      renderPDF(pdf, i, idTemplate + i);
    }
  });
}

const createPdfContainer = (id, className) => {
  const pdfContainer = pdfViewerRef.value;
  const canvasNew = document.createElement("canvas");
  canvasNew.classList.add(id);
  canvasNew.classList.add(className);
  pdfContainer.appendChild(canvasNew);
  emits("ok");
}

const createSeriesCanvas = (num, template) => {
  var id = "";
  for (var j = 1; j <= num; j++) {
    id = template + j;
    createPdfContainer(id, "pdfClass");
  }
}
const emits = defineEmits(["ok"]);
    
</script>

<style lang="scss" scoped>
.flex-between {
  display: flex;
  justify-content: space-between;
  padding: 5px 40px;
  box-sizing: border-box;
}

.pdf-box {
  position: relative;
  width: 100%;
  height: 100%;

  .pdf-center-box {
    overflow-x: auto;
    width: 100%;
    padding: 10px;

    .pdfView {
      overflow: hidden;
      box-shadow: 0 0 6px 2px rgb(194, 194, 194);
      overflow-x: auto;

      .pdfClass {
        margin-bottom: 10px !important;
      }
    }

    &:hover .post-flex {
      display: block;
    }

    .post-flex {
      display: none;
      position: absolute;
      width: 665px;
      left: 10px;
      top: 10px;
      background: #00000066;
      z-index: 9999999;

      &::v-deep .el-button--text {
        color: #fff;
        margin: 0 10px;
      }
    }
  }
}

.pdf {
  margin: 5px 0;
  box-shadow: 0 0 6px 2px rgb(194, 194, 194);
}
</style>

遇到的坑是开始只能支持外部引用pdf.worker.min.js,但是速度非常慢,并且有时候半天打不开,

于是想到用本地引用,需要在index.html中挂载,在public文件夹中创建文件,并且将外部链接中代码的拷入文件中

<script src="/public/pdf.worker.min.js"></script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值