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

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

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

實(shí)現(xiàn)效果

可用插件介紹

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

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

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

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

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

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

所以最后選擇直接集成 pdfjs-dist 來(lái)完成全部功能

安裝和引入插件

安裝

yarn add pdfjs-dist

引入

必須手動(dòng)指定 workerSrc ,不然會(huì)拋出 Setting up fake worker failed 的錯(cuò)誤。

雖然本地目錄 node_modules/pdfjs-dist/build/pdf.worker.js 存在該文件,但實(shí)際引入時(shí)依舊會(huì)報(bào)錯(cuò),所以只能使用 CDN 地址下的 pdf.worker.js ??梢酝ㄟ^(guò)傳入 PDFJS.version 來(lái)提高引入的靈活性。

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é)點(diǎn)

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

用于接收 PDFJS 實(shí)例的對(duì)象

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

監(jiān)聽(tīng)鏈接變化并初始化實(shí)例

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

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

 this._initPdf()
 }
},

渲染 PDF 內(nèi)容

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

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)前頁(yè)面

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

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

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')
 // 獲取頁(yè)面比率
 let ratio = this._getRatio(ctx)

 // 根據(jù)頁(yè)面寬度和視口寬度的比率就是內(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ū)寬高,后期添加水印時(shí)需要
 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(() => {})
 })
 }
}

實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)

準(zhǔn)備渲染隊(duì)列,防止渲染順序混亂

當(dāng)觸發(fā)頁(yè)面跳轉(zhuǎn)時(shí),會(huì)調(diào)用 _renderQueue() 函數(shù),而不是直接調(diào)用 _renderPage() 函數(shù),因?yàn)槭欠耖_(kāi)始渲染,要取決于當(dāng)前是否沒(méi)有正在被渲染的頁(yè)面。

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

 this._renderPage()
 }
}

在渲染頁(yè)面時(shí)改變隊(duì)列狀態(tài)

methods: {
 _renderPage () {
 // 隊(duì)列開(kāi)始
 this.rendering = true

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

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

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

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

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

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

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

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

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

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

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

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

繪制作為水印的 canvas

因?yàn)槭墙M件,所以水印的文字 watermark 由外部傳入。

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

ctx.fillStyle 表示文字的透明度。 ctx.fillText(this.watermark, 50, 50) 表示文字在畫(huà)布中的位置,第一個(gè)值是文字內(nèi)容,第二個(gè)值是 x軸偏移量,第三個(gè)值是 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ù)中記錄的頁(yè)面內(nèi)容區(qū)的實(shí)際寬高。只要將實(shí)際寬高傳入,canvas 就會(huì)自動(dòng)根據(jù)水印圖片的大小和內(nèi)容區(qū)的大小自動(dòng)實(shí)現(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()
 }
}

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

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

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

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

相關(guān)文章

  • Vue中構(gòu)造數(shù)組數(shù)據(jù)之map和forEach方法實(shí)現(xiàn)

    Vue中構(gòu)造數(shù)組數(shù)據(jù)之map和forEach方法實(shí)現(xiàn)

    數(shù)組操作是前端最重要的數(shù)據(jù)操作,構(gòu)造數(shù)組數(shù)據(jù),又是數(shù)組操作中很常見(jiàn)的,本文將梳理下map和forEach方法在Vue項(xiàng)目中的使用,感興趣的朋友跟隨小編一起看看吧
    2022-09-09
  • Vue?如何關(guān)掉響應(yīng)式問(wèn)題

    Vue?如何關(guān)掉響應(yīng)式問(wèn)題

    這篇文章主要介紹了Vue?如何關(guān)掉響應(yīng)式問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 詳解如何在Vue中進(jìn)行表單自定義驗(yàn)證

    詳解如何在Vue中進(jìn)行表單自定義驗(yàn)證

    在Vue應(yīng)用中,表單驗(yàn)證是非常重要的一部分,Vue提供了一些內(nèi)置的驗(yàn)證規(guī)則,比如required、min、max等,但是有時(shí)候我們需要對(duì)表單進(jìn)行自定義驗(yàn)證,以滿足特定的業(yè)務(wù)需求,本文將介紹如何在Vue中進(jìn)行表單自定義驗(yàn)證,感興趣的朋友可以參考下
    2023-12-12
  • Vue axios設(shè)置訪問(wèn)基礎(chǔ)路徑方法

    Vue axios設(shè)置訪問(wèn)基礎(chǔ)路徑方法

    今天小編就為大家分享一篇Vue axios設(shè)置訪問(wèn)基礎(chǔ)路徑方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • vue transition的延時(shí)播放實(shí)踐過(guò)程

    vue transition的延時(shí)播放實(shí)踐過(guò)程

    文章介紹了如何使用JavaScript的`setTimeout`和Vue.js的`v-show`指令實(shí)現(xiàn)頁(yè)面自動(dòng)播放,并通過(guò)CSS的`transition`和`animation`屬性控制動(dòng)畫(huà)的播放時(shí)間
    2025-12-12
  • Vue跳轉(zhuǎn)頁(yè)面的幾種常用方法總結(jié)

    Vue跳轉(zhuǎn)頁(yè)面的幾種常用方法總結(jié)

    在Vue.js中,頁(yè)面跳轉(zhuǎn)是構(gòu)建單頁(yè)面應(yīng)用(SPA)的基本操作之一,本文將介紹Vue中實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的幾種方法,并通過(guò)實(shí)例代碼幫助理解每種方法的用法,需要的朋友可以參考下
    2024-09-09
  • watch里面的deep和immediate作用及說(shuō)明

    watch里面的deep和immediate作用及說(shuō)明

    這篇文章主要介紹了watch里面的deep和immediate作用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • vue?ui的安裝步驟以及使用詳解

    vue?ui的安裝步驟以及使用詳解

    最近公司開(kāi)發(fā)一個(gè)項(xiàng)目,采用的前后端分離的方式,前端采用vue,但是再項(xiàng)目開(kāi)發(fā)過(guò)程中遇到一個(gè)比較麻煩的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于vue?ui的安裝步驟以及使用的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • vue組件通信的多種方法詳解

    vue組件通信的多種方法詳解

    這篇文章主要為大家介紹了vue組件通信的幾種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2021-11-11
  • vue-cli腳手架的.babelrc文件用法說(shuō)明

    vue-cli腳手架的.babelrc文件用法說(shuō)明

    這篇文章主要介紹了vue-cli腳手架的.babelrc文件用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09

最新評(píng)論

阳山县| 青川县| 太和县| 宁河县| 虹口区| 大悟县| 太康县| 桂林市| 秭归县| 无棣县| 齐齐哈尔市| 苏尼特右旗| 皮山县| 都匀市| 威海市| 石楼县| 佛坪县| 北辰区| 故城县| 凌源市| 信丰县| 肥城市| 鹤峰县| 阳城县| 南充市| 准格尔旗| 宣威市| 镇沅| 武宁县| 高密市| 岗巴县| 芦山县| 西贡区| 新巴尔虎左旗| 双城市| 安泽县| 张家界市| 周口市| 潜山县| 永丰县| 河北省|