亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Vue 集成 PDF.js 實現(xiàn) PDF 預(yù)覽和添加水印的步驟

 更新時間:2021年01月22日 10:17:54   作者:asing1elife  
這篇文章主要介紹了如何在 Vue 中集成 Mozilla/PDF.js ,實現(xiàn)自定義的 PDF 預(yù)覽器,以及給被預(yù)覽的 PDF 添加水印

實現(xiàn)效果

可用插件介紹

Mozilla 提供了 PDF.js pdfjs-dist ,兩者的區(qū)別如下:

  • PDF.js ,一個完整的 PDF 查看器,可以直接使用其提供的 viewer.html 查看 PDF 內(nèi)容,包含完整樣式和相關(guān)功能。優(yōu)點是快速集成,不需要自己實現(xiàn)查看器的功能和樣式。缺點是如果要自定義樣式和功能,反而會很麻煩。
  • pdfjs-dist ,PDF.js 的預(yù)購建版本,只包含 PDF 內(nèi)容的渲染功能,需要自己實現(xiàn)查看器的樣式和相關(guān)功能。

Vue 官方插件庫 Awesome Vue.js 推薦的vue-pdf 就是對 pdfjs-dist 進行了封裝實現(xiàn),一般情況下使用 vue-pdf 即可快速實現(xiàn) PDF 的預(yù)覽效果。

根據(jù)需求進行插件選型

我們的需求是在現(xiàn)有頁面中實現(xiàn) PDF 預(yù)覽的同時,在 PDF 內(nèi)容上添加水印。

PDF.js 這種完整版的查看器顯得過于臃腫,而 vue-pdf 雖然可以快速實現(xiàn)預(yù)覽效果,但在添加水印時需要對顯示 PDF 的 canvas 進行二次渲染,經(jīng)過嘗試后發(fā)現(xiàn)會拋出 Failed to execute 'drawImage' on 'CanvasRenderingContext2D': Overload resolution failed. 的錯誤。

所以最后選擇直接集成 pdfjs-dist 來完成全部功能

安裝和引入插件

安裝

yarn add pdfjs-dist

引入

必須手動指定 workerSrc ,不然會拋出 Setting up fake worker failed 的錯誤。

雖然本地目錄 node_modules/pdfjs-dist/build/pdf.worker.js 存在該文件,但實際引入時依舊會報錯,所以只能使用 CDN 地址下的 pdf.worker.js ??梢酝ㄟ^傳入 PDFJS.version 來提高引入的靈活性。

import * as PDFJS from 'pdfjs-dist'

PDFJS.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDFJS.version}/pdf.worker.js`

初始化插件

用于渲染內(nèi)容的 canvas 節(jié)點

<canvas id="pdfCanvas"></canvas>

用于接收 PDFJS 實例的對象

props: {
 // PDF 文件的實際鏈接
 url: {
 type: String
 }
},
data () {
 return {
 totalPage: 1,
 // PDFJS 實例
 pdfDoc: null
 }
},
methods: {
 _initPdf () {
 PDFJS.getDocument(this.url).promise.then(pdf => {
 // 文檔對象
 this.pdfDoc = pdf
 // 總頁數(shù)
 this.totalPage = pdf.numPages
 // 渲染頁面
 this.$nextTick(() => {
 this._renderPage()
 })
 })
 }
}

監(jiān)聽鏈接變化并初始化實例

當(dāng)外部傳入的 url 有效時,就可以觸發(fā) PDF 查看器的初始化函數(shù)

watch: {
 'url' (val) {
 if (!val) {
 return
 }

 this._initPdf()
 }
},

渲染 PDF 內(nèi)容

獲取當(dāng)前頁面比率,用于計算內(nèi)容的實際寬高

methods: {
 _getRatio (ctx) {
 let dpr = window.devicePixelRatio || 1
 let bsr =
 ctx.webkitBackingStorePixelRatio ||
 ctx.mozBackingStorePixelRatio ||
 ctx.msBackingStorePixelRatio ||
 ctx.oBackingStorePixelRatio ||
 ctx.backingStorePixelRatio ||
 1

 return dpr / bsr
 }
}

渲染當(dāng)前頁面

page.getViewport({ scale }) 中的 scale 非常關(guān)鍵,直接關(guān)系到渲染出來的內(nèi)容能不能撐滿整個父容器,所以這里分別獲取了父容器和頁面本身的寬度,父容器寬度 / 頁面寬度 后得出的比率就是實際頁面需要放大多少的比率。

page.view 是一個數(shù)組,里面有四個值,分別是 x軸偏移量、y軸偏移量、寬度、高度。 要獲取真實的寬度,還需要考慮當(dāng)前頁面比率,所以使用 page.view[2] * ratio 計算得出實際寬度。

data () {
 return {
 currentPage: 1,
 totalPage: 1,
 width: 0,
 height: 0,
 pdfDoc: null
 }
},
methods: {
 _renderPage () {
 this.pdfDoc.getPage(this.currentPage).then(page => {
 let canvas = document.querySelector('#pdfCanvas')
 let ctx = canvas.getContext('2d')
 // 獲取頁面比率
 let ratio = this._getRatio(ctx)

 // 根據(jù)頁面寬度和視口寬度的比率就是內(nèi)容區(qū)的放大比率
 let dialogWidth = this.$refs['pdfDialog'].$el.querySelector('.el-dialog').clientWidth - 40
 let pageWidth = page.view[2] * ratio
 let scale = dialogWidth / pageWidth

 let viewport = page.getViewport({ scale })

 // 記錄內(nèi)容區(qū)寬高,后期添加水印時需要
 this.width = viewport.width * ratio
 this.height = viewport.height * ratio

 canvas.width = this.width
 canvas.height = this.height

 // 縮放比率
 ctx.setTransform(ratio, 0, 0, ratio, 0, 0)

 page.render({
 canvasContext: ctx,
 viewport
 }).promise.then(() => {})
 })
 }
}

實現(xiàn)頁面跳轉(zhuǎn)

準備渲染隊列,防止渲染順序混亂

當(dāng)觸發(fā)頁面跳轉(zhuǎn)時,會調(diào)用 _renderQueue() 函數(shù),而不是直接調(diào)用 _renderPage() 函數(shù),因為是否開始渲染,要取決于當(dāng)前是否沒有正在被渲染的頁面。

data () {
 return {
 // 是否位于隊列中
 rendering: false
 }
},
methods: {
 _renderQueue () {
 if (this.rendering) {
 return
 }

 this._renderPage()
 }
}

在渲染頁面時改變隊列狀態(tài)

methods: {
 _renderPage () {
 // 隊列開始
 this.rendering = true

 this.pdfDoc.getPage(this.currentPage).then(page => {
 // ... 省略實現(xiàn)代碼

 page.render({
 canvasContext: ctx,
 viewport
 }).promise.then(() => {
 // 隊列結(jié)束
 this.rendering = false
 })
 })
 }
}

實現(xiàn)翻頁函數(shù)

data () {
 return {
 currentPage: 1,
 totalPage: 1
 }
},
computed: {
 // 是否首頁
 firstPage () {
 return this.currentPage <= 1
 },
 // 是否尾頁
 lastPage () {
 return this.currentPage >= this.totalPage
 },
},
methods: {
 // 跳轉(zhuǎn)到首頁
 firstPageHandler () {
 if (this.firstPage) {
 return
 }

 this.currentPage = 1
 this._renderQueue()
 },
 // 跳轉(zhuǎn)到尾頁
 lastPageHandler () {
 if (this.lastPage) {
 return
 }

 this.currentPage = this.totalPage
 this._renderQueue()
 },
 // 上一頁
 previousPage () {
 if (this.firstPage) {
 return
 }

 this.currentPage--
 this._renderQueue()
 },
 // 下一頁
 nextPage () {
 if (this.lastPage) {
 return
 }

 this.currentPage++
 this._renderQueue()
 }
}

在頁面內(nèi)容中添加平鋪的文字水印

前端添加水印的方式毋庸置疑都是使用 canvas 進行繪制。

最開始找到的方案是準備一個 div 作為透明的遮罩層擋在內(nèi)容區(qū)的上層,然后將 canvas 繪制的水印使用 canvas.toDataURL('image/png') 導(dǎo)出成 Base64 格式,作為遮罩層的背景圖片進行平鋪。 雖然可以實現(xiàn)效果,但這種方式只要簡單的打開瀏覽器控制臺,刪除這個遮罩層就可以去除水印。

之后在 Canvas 繪制另一個 Canvas 中找到 canvas 其實是可以將一個 canvas 作為圖片繪制到自身上的,于是有了接下來的方案。

繪制作為水印的 canvas

因為是組件,所以水印的文字 watermark 由外部傳入。

繪制水印的 canvas 不需要添加到頁面中,繪制完成后直接將 DOM 元素返回即可,注意,返回的是 DOM 元素 ,而不是使用 getContext(2d) 獲取的畫布實例。

ctx.fillStyle 表示文字的透明度。 ctx.fillText(this.watermark, 50, 50) 表示文字在畫布中的位置,第一個值是文字內(nèi)容,第二個值是 x軸偏移量,第三個值是 y軸偏移量。

props: {
 watermark: {
 type: String,
 default: 'asing1elife'
 }
},
methods: {
 _initWatermark () {
 let canvas = document.createElement('canvas');
 canvas.width = 200
 canvas.height = 200

 let ctx = canvas.getContext('2d')
 ctx.rotate(-18 * Math.PI / 180)
 ctx.font = '14px Vedana'
 ctx.fillStyle = 'rgba(200, 200, 200, .3)'
 ctx.textAlign = 'left'
 ctx.textBaseline = 'middle'
 ctx.fillText(this.watermark, 50, 50)

 return canvas
 }
}

將水印平鋪到渲染內(nèi)容的 canvas 中

該方法參考自 HTML5 canvas 平鋪的幾種方法 ,ctx.rect(0, 0, this.width, this.height) 中的 width 和 height 就是在 _renderPage() 函數(shù)中記錄的頁面內(nèi)容區(qū)的實際寬高。只要將實際寬高傳入,canvas 就會自動根據(jù)水印圖片的大小和內(nèi)容區(qū)的大小自動實現(xiàn) x軸和 y軸的重復(fù)次數(shù)。

methods: {
 _renderWatermark () {
 let canvas = document.querySelector('#pdfCanvas')
 let ctx = canvas.getContext('2d')

 // 平鋪水印
 let pattern = ctx.createPattern(this._initWatermark(), 'repeat')
 ctx.rect(0, 0, this.width, this.height)
 ctx.fillStyle = pattern
 ctx.fill()
 }
}

頁面內(nèi)容渲染完成后,再次觸發(fā)水印渲染

methods: {
 // 渲染頁面
 _renderPage () {
 this.pdfDoc.getPage(this.currentPage).then(page => {
 // ... 省略實現(xiàn)代碼

 page.render({
 canvasContext: ctx,
 viewport
 }).promise.then(() => {
 // 渲染水印
 this._renderWatermark()
 })
 })
 }
}

以上就是Vue 集成 PDF.js 實現(xiàn) PDF 預(yù)覽和添加水印的的詳細內(nèi)容,更多關(guān)于vue 實現(xiàn) PDF 預(yù)覽和添加水印的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺談Vue的加載順序探討

    淺談Vue的加載順序探討

    本篇文章主要介紹了Vue的加載順序探討,詳細的介紹了加載順序以及如何判斷所有的子組件加載完成。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Vue插槽slot詳細介紹(對比版本變化,避免踩坑)

    Vue插槽slot詳細介紹(對比版本變化,避免踩坑)

    Vue中的Slot對于編寫可復(fù)用可擴展的組件是再合適不過了,下面這篇文章主要給大家介紹了關(guān)于Vue插槽slot詳細介紹的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06
  • Vue?先初始化父組件再初始化子組件的方法(自定義父子組件mounted執(zhí)行順序)

    Vue?先初始化父組件再初始化子組件的方法(自定義父子組件mounted執(zhí)行順序)

    這篇文章主要介紹了Vue?先初始化父組件再初始化子組件的方法(自定義父子組件mounted執(zhí)行順序),本篇內(nèi)容內(nèi)容主要講述了,在使用?Konva?進行開發(fā)過程中遇到的一些問題,需要的朋友可以參考下
    2024-07-07
  • vue實現(xiàn)顯示消息提示框功能

    vue實現(xiàn)顯示消息提示框功能

    這篇文章主要介紹了vue實現(xiàn)顯示消息提示框功能,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • VUE+Canvas 實現(xiàn)桌面彈球消磚塊小游戲的示例代碼

    VUE+Canvas 實現(xiàn)桌面彈球消磚塊小游戲的示例代碼

    這篇文章主要介紹了VUE+Canvas 實現(xiàn)桌面彈球消磚塊小游戲,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • vue通過video.js解決m3u8視頻播放格式的方法

    vue通過video.js解決m3u8視頻播放格式的方法

    這篇文章主要給大家介紹了關(guān)于vue通過video.js解決m3u8視頻播放格式的方法,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • vue項目中錨點定位替代方式

    vue項目中錨點定位替代方式

    今天小編就為大家分享一篇vue項目中錨點定位替代方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • Vue中input被賦值后,無法再修改編輯的問題及解決

    Vue中input被賦值后,無法再修改編輯的問題及解決

    這篇文章主要介紹了Vue中input被賦值后,無法再修改編輯的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue實現(xiàn)的上拉加載更多數(shù)據(jù)/分頁功能示例

    vue實現(xiàn)的上拉加載更多數(shù)據(jù)/分頁功能示例

    這篇文章主要介紹了vue實現(xiàn)的上拉加載更多數(shù)據(jù)/分頁功能,涉及基于vue的事件響應(yīng)、數(shù)據(jù)交互等相關(guān)操作技巧,需要的朋友可以參考下
    2019-05-05
  • Vue.js中的computed工作原理

    Vue.js中的computed工作原理

    這篇文章,我們通過實現(xiàn)一個簡單版的和Vue中computed具有相同功能的函數(shù)來了解computed是如何工作的。對Vue.js中的computed工作原理感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧
    2018-03-03

最新評論