最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端基于Mozilla?PDF.js實現(xiàn)一個功能完善的PDF預(yù)覽組件

 更新時間:2026年01月08日 09:23:19   作者:干前端  
本文將詳細(xì)介紹如何基于Mozilla?PDF.js實現(xiàn)一個功能完善、安全可靠的PDF預(yù)覽組件,重點講解虛擬滾動、雙模式渲染、水印實現(xiàn)等核心技術(shù),需要的朋友可以參考下

前言

在Web應(yīng)用中實現(xiàn)PDF預(yù)覽功能是常見需求,尤其是在線教育、文檔管理等場景。然而,簡單的PDF預(yù)覽往往無法滿足實際業(yè)務(wù)需求,特別是在安全性方面。本文將介紹如何基于PDF.js實現(xiàn)一個功能完善的PDF預(yù)覽組件,并重點講解如何添加自定義防下載和水印功能,為文檔安全提供保障。

功能概覽

我們的PDF預(yù)覽組件實現(xiàn)了以下核心功能:

  1. 基礎(chǔ)功能:PDF文件加載與渲染、自定義尺寸控制、頁面縮放規(guī)則配置、主題切換
  2. 安全增強:動態(tài)水印添加、防下載功能、右鍵菜單禁用、打印控制
  3. 用戶體驗:頁面渲染事件通知、響應(yīng)式布局適配、加載狀態(tài)反饋

技術(shù)實現(xiàn)

1. 虛擬滾動加載

對于大型PDF文件,一次性渲染所有頁面會導(dǎo)致嚴(yán)重的性能問題。我們通過虛擬滾動技術(shù)優(yōu)化大文檔的加載性能,只渲染當(dāng)前可見區(qū)域和附近的頁面:

// 頁面緩存管理
class PDFPageViewBuffer {
  #buf = new Set();
  #size = 0;

  constructor(size) {
    this.#size = size;  // 緩存頁面數(shù)量限制
  }

  push(view) {
    const buf = this.#buf;
    if (buf.has(view)) {
      buf.delete(view);
    }
    buf.add(view);
    if (buf.size > this.#size) {
      this.#destroyFirstView();  // 超出限制時銷毀最早的頁面
    }
  }
}

優(yōu)勢

  • 內(nèi)存優(yōu)化:只保留有限數(shù)量的頁面在內(nèi)存中
  • 性能提升:減少不必要的渲染操作
  • 流暢體驗:滾動時動態(tài)加載頁面

2. 雙模式渲染:Canvas與HTML

PDF.js支持兩種渲染模式,可根據(jù)不同需求選擇。兩種渲染方式在視覺效果和性能上有明顯差異:

圖:HTML渲染模式下的PDF顯示效果

圖:Canvas渲染模式下的PDF顯示效果

Canvas渲染(默認(rèn))

// 創(chuàng)建Canvas元素
const canvas = document.createElement("canvas");
canvas.setAttribute("role", "presentation");

// 獲取2D渲染上下文
const ctx = canvas.getContext("2d", {
  alpha: false,           // 禁用透明度通道,提高性能
  willReadFrequently: !this.#enableHWA  // 根據(jù)硬件加速設(shè)置優(yōu)化
});

// 渲染PDF頁面到Canvas
const renderContext = {
  canvasContext: ctx,
  transform,
  viewport,
  // 其他參數(shù)...
};
const renderTask = pdfPage.render(renderContext);

HTML渲染

// HTML渲染模式(文本層)
if (!this.textLayer && this.#textLayerMode !== TextLayerMode.DISABLE) {
  this.textLayer = new TextLayerBuilder({
    pdfPage,
    highlighter: this._textHighlighter,
    accessibilityManager: this._accessibilityManager,
    enablePermissions: this.#textLayerMode === TextLayerMode.ENABLE_PERMISSIONS,
    onAppend: (textLayerDiv) => {
      this.#addLayer(textLayerDiv, "textLayer");
    }
  });
}

兩種模式對比

特性Canvas渲染HTML渲染
性能中等
文本選擇不支持支持
縮放質(zhì)量中等
內(nèi)存使用
兼容性極好

3. 水印渲染實現(xiàn)

水印是保護文檔版權(quán)的重要手段。我們在PDF頁面渲染完成后,直接在Canvas上添加水印,確保水印與內(nèi)容融為一體:

// 在渲染完成后添加水印
const resultPromise = renderTask.promise.then(async () => {
  showCanvas?.(true);
  await this.#finishRenderTask(renderTask);

  // 添加水印
  createWaterMark({ fontText: warterMark, canvas, ctx });

  // 其他處理...
});

// 水印繪制函數(shù)
function createWaterMark({
  ctx,
  canvas,
  fontText = '默認(rèn)水印',
  fontFamily = 'microsoft yahei',
  fontSize = 30,
  fontcolor = 'rgba(218, 218, 218, 0.5)',
  rotate = 30,
  textAlign = 'left'
}) {
  // 保存當(dāng)前狀態(tài)
  ctx.save();

  // 計算響應(yīng)式字體大小
  const canvasW = canvas.width;
  const calfontSize = (fontSize * canvasW) / 800;
  ctx.font = `${calfontSize}px ${fontFamily}`;
  ctx.fillStyle = fontcolor;
  ctx.textAlign = textAlign;
  ctx.textBaseline = 'Middle';

  // 添加多個水印
  const pH = canvas.height / 4;
  const pW = canvas.width / 4;
  const positions = [
    { x: pW, y: pH },
    { x: 3 * pW, y: pH },
    { x: pW * 1.3, y: 3 * pH },
    { x: 3 * pW, y: 3 * pH }
  ];

  positions.forEach((pos) => {
    ctx.save();
    ctx.translate(pos.x, pos.y);
    ctx.rotate(-rotate * Math.PI / 180);
    ctx.fillText(fontText, 0, 0);
    ctx.restore();
  });

  // 恢復(fù)狀態(tài)
  ctx.restore();
}

水印技術(shù)亮點

  • 響應(yīng)式設(shè)計:根據(jù)Canvas寬度自動調(diào)整水印尺寸
  • 多點布局:四個位置分布水印,覆蓋整個頁面
  • 旋轉(zhuǎn)效果:每個水印獨立旋轉(zhuǎn)30度,增加覆蓋范圍
  • 透明度處理:使用半透明顏色,不影響內(nèi)容可讀性

4. 防下載與打印控制

為了增強文檔安全性,我們實現(xiàn)了全面的防下載和打印控制功能:

// 禁用右鍵菜單
document.addEventListener('contextmenu', function(e) {
  e.preventDefault();
  return false;
});

// 禁用文本選擇
document.addEventListener('selectstart', function(e) {
  e.preventDefault();
  return false;
});

// 禁用拖拽
document.addEventListener('dragstart', function(e) {
  e.preventDefault();
  return false;
});

// 攔截Ctrl+P打印快捷鍵
window.addEventListener("keydown", function (event) {
  if (event.keyCode === 80 && (event.ctrlKey || event.metaKey) && 
      !event.altKey && (!event.shiftKey || window.chrome || window.opera)) {
    // 自定義打印行為或完全禁用
    event.preventDefault();
    event.stopImmediatePropagation();
  }
}, true);

Vue組件實現(xiàn)

基于以上技術(shù),我們實現(xiàn)了一個功能完善的Vue3 PDF預(yù)覽組件:

<template>
  <iframe
    :width="viewerWidth"
    :height="viewerHeight"
    id="ifra"
    frameborder="0"
    :src="`/pdfJs/web/viewer.html?file=${src}&waterMark=${waterMark}`"
    @load="pagesRendered"
  />
</template>

<script setup>
import { computed } from 'vue'
import { useUserStore } from '~/store/user'

const props = defineProps({
  src: String,
  width: [String, Number],
  height: [String, Number],
  pageScale: [String, Number],
  theme: String,
  fileName: String
})

const emit = defineEmits(['loaded'])

// 默認(rèn)值設(shè)置
const propsWithDefaults = withDefaults(props, {
  width: '100%',
  height: '100vh',
  pageScale: 'page-width',
  theme: 'dark',
  fileName: ''
})

// 尺寸計算
const viewerWidth = computed(() => {
  if (typeof props.width === 'number') {
    return props.width + 'px'
  } else {
    return props.width
  }
})

const viewerHeight = computed(() => {
  if (typeof props.height === 'number') {
    return props.height + 'px'
  } else {
    return props.height
  }
})

// 用戶信息和水印
const userStore = useUserStore()
const userInfo = computed(() => userStore.userInfo)

const waterMark = computed(() => {
  const { userName, phoneNum } = userInfo.value
  const phoneSuffix = phoneNum && phoneNum.substring(phoneNum.length - 4)
  return userName + phoneSuffix
})

// 頁面渲染事件
function pagesRendered(pdfApp) {
  emit('loaded', pdfApp)
}
</script>

<style scoped>
#ifra {
  max-width: 100%;
  height: 100%;
  margin-left: 50%;
  transform: translateX(-50%);
}
</style>

使用方法

基本使用

<template>
  <PDFViewer
    src="path/to/your/pdf/file.pdf"
    :width="800"
    :height="600"
    @loaded="handlePdfLoaded"
  />
</template>

<script setup>
import PDFViewer from '@/components/PDFViewer/index.vue'

function handlePdfLoaded(pdfApp) {
  console.log('PDF已加載完成', pdfApp)
}
</script>

高級配置

<template>
  <PDFViewer
    src="path/to/your/pdf/file.pdf"
    width="100%"
    height="90vh"
    page-scale="page-fit"
    theme="light"
    file-name="自定義文件名.pdf"
    @loaded="handlePdfLoaded"
  />
</template>

性能優(yōu)化

1. 渲染性能優(yōu)化

// 設(shè)置合理的maxCanvasPixels
const maxCanvasPixels = isHighEndDevice ? 
  16777216 * 4 :  // 4K顯示器
  8388608 * 2;   // 普通顯示器

const pdfViewer = new PDFViewer({
  container: document.getElementById('viewer'),
  maxCanvasPixels: maxCanvasPixels
});

2. 內(nèi)存管理優(yōu)化

// 限制緩存頁面數(shù)量,防止內(nèi)存溢出
pdfViewer.setDocument(pdfDocument);
pdfViewer.currentScaleValue = 'auto';

// 定期清理不可見頁面
setInterval(() => {
  const visiblePages = pdfViewer._getVisiblePages();
  // 清理不可見頁面的緩存
}, 30000);

3. 按需渲染

// 只渲染可見頁面
pdfViewer.onPagesLoaded = () => {
  const visiblePages = pdfViewer._getVisiblePages();
  // 只渲染可見頁面,延遲渲染其他頁面
};

注意事項

  1. PDF.js版本:確保使用兼容的PDF.js版本,不同版本API可能有差異
  2. 跨域處理:PDF文件可能存在跨域問題,需確保服務(wù)器配置了正確的CORS頭
  3. 大文件處理:對于大型PDF文件,考慮添加加載進度提示
  4. 移動端適配:在移動設(shè)備上可能需要額外的樣式調(diào)整
  5. 安全限制:雖然實現(xiàn)了防下載和水印,但無法完全防止技術(shù)用戶獲取PDF內(nèi)容

擴展功能建議

  1. 頁面跳轉(zhuǎn):添加頁面導(dǎo)航功能,支持直接跳轉(zhuǎn)到指定頁面
  2. 文本搜索:實現(xiàn)PDF內(nèi)容搜索功能
  3. 注釋工具:添加PDF注釋、標(biāo)記功能
  4. 水印樣式自定義:支持更多水印樣式和位置配置
  5. 訪問控制:基于用戶角色限制PDF訪問權(quán)限

總結(jié)

本文介紹了如何基于Mozilla PDF.js實現(xiàn)一個功能完善的PDF預(yù)覽組件,并重點講解了如何添加自定義的防下載和水印功能。通過合理的技術(shù)選型和組件設(shè)計,我們實現(xiàn)了一個既美觀又安全的PDF預(yù)覽解決方案。

在實際應(yīng)用中,您可以根據(jù)具體需求進一步擴展功能,如添加頁面導(dǎo)航、文本搜索等高級特性,為用戶提供更豐富的PDF閱讀體驗,同時確保文檔內(nèi)容的安全性。

以上就是前端基于Mozilla PDF.js實現(xiàn)一個功能完善的PDF預(yù)覽組件的詳細(xì)內(nèi)容,更多關(guān)于前端實現(xiàn)PDF預(yù)覽組件的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

安宁市| 大洼县| 六枝特区| 察雅县| 定州市| 陆良县| 阿克| 永仁县| 贡嘎县| 瑞金市| 卢龙县| 温州市| 万山特区| 太谷县| 商丘市| 五常市| 年辖:市辖区| 武汉市| 景泰县| 偏关县| 镇宁| 泰州市| 思南县| 司法| 石棉县| 万宁市| 南部县| 澳门| 雷州市| 社会| 新乡县| 阿合奇县| 中牟县| 鹰潭市| 江西省| 平度市| 公主岭市| 南安市| 章丘市| 厦门市| 肇源县|