vue项目页面的打印和下载PDF加loading效果的实现(加水印)

作者:牛先森家的牛奶 时间:2024-04-30 10:29:39 

vue页面的打印和下载PDF(加水印)

vue项目页面的打印

打印的不用说,调用 window.print() 的方法即可;

注意点:如果用到背景图的话,需要CSS中添加设置;

// 标签看哪些地方用到背景图就加哪些,不然调打印机会把背景图隐藏掉
div {
 // webkit 为Google Chrome Safari 等浏览器内核
 -webkit-print-color-adjust: exact;
 print-color-adjust: exact;
 color-adjust: exact;
}

vue项目页面下载PDF

封装代码如下:

新建utils/pdf.js文件;

import html2canvas from "html2canvas"
import jsPDF from "jspdf"export const downloadPDF = (el, title) => {
 html2canvas(el, {
   allowTaint: true,
   useCORS: true,
   dpi: 120, // 图片清晰度问题
   background: '#FFFFFF', //如果指定的div没有设置背景色会默认成黑色
 }).then(canvas => {
   // 未生成pdf的html页面高度
   let leftHeight = canvas.height

//A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
   let a4Width = 595.28
   let a4Height = 841.89
   //一页pdf显示html页面生成的canvas高度;
   let a4HeightRef = Math.floor((canvas.width / a4Width) * a4Height)
   //pdf页面偏移
   let position = 0    // canvas.toDataURL() 返回一个包含图片展示的 数据URL。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。
   // 返回值是一个数据url,是base64组成的图片的源数据、可以直接赋值给图片的src属性。
   let pageData = canvas.toDataURL('image/jpeg', 1.0)    let pdf = new jsPDF('p', 'pt', 'a4') //A4纸,纵向

// let index = 1
   let createCanvas = document.createElement('canvas')
   let height    pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen')    let pdfName = title || "个人简历"
   function createImpl(canvas) {
     console.log(leftHeight, a4HeightRef)
     if (leftHeight > 0) {
       // index++
       let checkCount = 0
       if (leftHeight > a4HeightRef) {
         let i = position + a4HeightRef
         for (i = position + a4HeightRef; i >= position; i--) {
           let isWrite = true
           for (let j = 0; j < canvas.width; j++) {
             let c = canvas.getContext('2d').getImageData(j, i, 1, 1).data
             if (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff) {
               isWrite = false
               break
             }
           }
           if (isWrite) {
             checkCount++
             if (checkCount >= 10) {
               break
             }
           } else {
             checkCount = 0
           }
         }
         height = Math.round(i - position) || Math.min(leftHeight, a4HeightRef)
         if (height <= 0) {
           height = a4HeightRef
         }
       } else {
         height = leftHeight
       }        createCanvas.width = canvas.width
       createCanvas.height = height        // console.log(index, 'height:', height, 'pos', position)
       console.log('height:', height, 'pos', position)        // getContext()方法可返回一个对象,该对象提供了用于在画布上绘图的方法和属性。
       let ctx = createCanvas.getContext('2d')        ctx.drawImage(
         canvas,
         0,
         position,
         canvas.width,
         height,
         0,
         0,
         canvas.width,
         height,
       )        // let pageHeight = Math.round((a4Width / canvas.width) * height)
       // pdf.setPageSize(null, pageHeight)
       if (position != 0) {
         pdf.addPage()
       }
       pdf.addImage(
         createCanvas.toDataURL('image/jpeg', 1.0),
         'JPEG',
         10,
         20,
         a4Width -30,
         (a4Width / createCanvas.width) * height -30,
       )
       leftHeight -= height
       position += height
       if (leftHeight > 0) {
         setTimeout(createImpl, 100, canvas)
       } else {
         pdf.save(pdfName + '.pdf')
       }
     }
   }    //当内容未超过pdf一页显示的范围,无需分页
   if (leftHeight < a4HeightRef) {
     pdf.addImage(
       pageData,
       'JPEG',
       10,
       20,
       a4Width - 30,
       (a4Width / canvas.width) * leftHeight -30,
     )
     pdf.save(pdfName + '.pdf')
   } else {
     try {
       pdf.deletePage(0)
       setTimeout(createImpl, 100, canvas)
     } catch (err) {
       console.log(err)
     }
   }
 })
}
// 页面水印 小密
// export const previewWater = (str, str2) => {
//   console.log(str)
//   let ctx = document.createElement("canvas")
//   ctx.width = 800
//   ctx.height = 1200
//   ctx.style.display = "block"
//   let cans = ctx.getContext("2d")
//   cans.rotate((-45 * Math.PI) / 180)
//   cans.font = "16px Microsoft YaHei"
//   cans.fillStyle = "rgba(0, 0, 0, 0.3)"
//   cans.textAlign = "left"
//   cans.textBaseline = "Middle"
//   cans.fillText(str, 0, 100)
//   cans.fillText(str2, 0, 120) // 第二行字体
//   cans.save()
//   return ctx.toDataURL()
// }// 页面水印 大中
export const previewWater = (strName, strLink) => {
 // 创建一个画布
 const can = document.createElement('canvas')
 // 设置画布的长宽
 can.width = 500
 can.height = 750  const cans = can.getContext('2d')
 // 旋转角度 canvas旋转不是以图片旋转,而是以画布左上角为原点旋转
 cans.rotate((-45 * Math.PI) / 180)
 cans.translate(0, 0)  const txtLen = strName.length
 // 水印如果都短设置为50px字体,长水印则30px字体
 const fontSize = txtLen > 12 ? '30px Simsun' : '40px Simsun'
 cans.font = fontSize
 // 设置填充绘画的颜色、渐变或者模式
 cans.fillStyle = 'rgba(0, 0, 0, 0.3)'
 // 设置文本内容的当前对齐方式
 cans.textAlign = 'center'
 // 设置在绘制文本时使用的当前文本基线
 cans.textBaseline = 'Middle'
 // 在画布上绘制填色的文本(输出的文本,开始绘制文本的X坐标位置,开始绘制文本的Y坐标位置)
 cans.fillText(strName, -txtLen * 12, 400)
 cans.fillText(strLink, -txtLen * 12, 440)
 // save()方法就是保存你在 cans 中设置的各种样式以及属性
 // save()方法相当于将设置的内容隔离出来,不会对外面的任何内容造成影响
 cans.save()
 return can.toDataURL()
}/*let createCanvas = document.createElement('canvas')
createCanvas.width = width
createCanvas.height = height
let ctx = createCanvas.getContext('2d')
ctx.drawImage(
 canvas,
 0,
 position,
 canvas.width,
 height,
 0,
 0,
 canvas.width,
 height,
)drawImage(image, dx, dy) 在画布指定位置绘制原图
drawImage(image, dx, dy, dw, dh) 在画布指定位置上按原图大小绘制指定大小的图
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) 剪切图像,并在画布上定位被剪切的部分image  规定要使用的图像、画布或视频
sx    可选。开始剪切图片的 x 坐标位置
sy    可选。开始剪切图片的 y 坐标位置
sw    可选。被剪切图像的宽度(就是裁剪之前的图片宽度,这里的宽度若小于图片的原宽。则图片多余部分被剪掉;若大于,则会以空白填充)
sh    可选。被剪切图像的高度(就是裁剪之前的图片高度)
dx    在画布上放置图像的 x 坐标位置
dy    在画布上放置图像的 y 坐标位置
dw    可选。要使用的图像的宽度(就是裁剪之后的图片高度,放大或者缩放)
dh    可选。要使用的图像的高度(就是裁剪之后的图片高度,放大或者缩放)
*/

注意要提前下载 jspdfhtml2canvas 这两个包,项目中要用到;

项目里面是用另一种方式是把js包下载到本地,动态新建script标签引入,注意这样的方式可以在window的环境下调用方法,要用 window.html2canvas 或者 window.jspdf等等;

handleCreateScript() {
   // 动态生成script引入js文件
   let html2Canvas = document.createElement('script')
   html2Canvas.src = `${process.env.VUE_LOCAL_PATH}js/html2canvas.min.js`
   document.body.appendChild(html2Canvas)
   let jspdf = document.createElement('script')
   jspdf.src = `${process.env.VUE_LOCAL_PATH}js/jspdf.umd.min.js`
   document.body.appendChild(jspdf)
}

// vue 的 created 钩子函数中调用
created(){
   this.handleCreateScript();
}// 封装的jspdf文件中调方法,就不用import 方式
// 可以使用window.html2canvas 或者 window.jspdf

注意说一下水印,因为需求要求水印必须放到页面的上面,而不是页面的下方,只能另辟蹊径了,我用的方法是直接canvas的创建生成方式,用个div定位到页面上面,动态获取内容的高度,然后给这个div加高度、加背景图的方式添加页面水印;

效果如下:

vue项目页面的打印和下载PDF加loading效果的实现(加水印)

具体代码如下:

<template>
 <div class="preview-wrapper">
   <div class="preview-btn">
     <div class="content">
       <div class="download-btn">
         <div><span @click="handlePrint">打印</span></div>
         <div><span @click="handleExport">下载</span></div>
       </div>
     </div>
   </div>

<div class="user-info" ref="pdfWrapper">
     <!--  内容自己定义 -->
     ...
     ....
     .....
     ......
   .......

<!-- 水印要放到页面上面 用个div 定位到页面上方-->
     <div
       class="water-wrapper"
       :style="{ backgroundImage: `url(${orgBackground})` }"
     ></div>
   </div>

</div>
</template><script>
//工具方法,导出操作
import { downloadPDF,  previewWater } from '@/utils/pdf.js'export default {
 name: 'userinfo',
 components: {

},
 props: {},
 data() {
   return {
     orgBackground: '',
   }
 },
 computed: {},
 created() {},
 mounted() {
   this.orgBackground = previewWater('XXXX网站', 'http://xxxxxxx.xxx.xx')
   // 打印的当前元素的内容区域的高度
   let userInfoWrapper = document.querySelector('.user-info')
   console.log(userInfoWrapper, userInfoWrapper.offsetHeight)
   // 获取水印的遮罩层,因为水印要放到内容区域的上方,而背景图默认是放下方
   let waterWrapper = document.querySelector('.water-wrapper')
   waterWrapper.style.cssText = `height: ${userInfoWrapper.offsetHeight}px`
 },
 methods: {
   handlePrint() {
     window.print()
   },
   handleExport() {
     downloadPDF(this.$refs.pdfWrapper, this.pfdName)
   }
 }
}
</script><style scoped lang="scss">
div {
 // webkit 为Google Chrome Safari 等浏览器内核
 -webkit-print-color-adjust: 'exact';
 print-color-adjust: 'exact';
 color-adjust: 'exact';
}
.preview-wrapper {
 // position: relative;
 z-index: 999;
}
.user-info {
 width: 1000px;
 background: #fff;
 position: relative;
 top: 0;
 left: 0;
 right: 0;
 bottom: 0;
 margin: 0 auto;
 padding: 0 50px 100px;
 z-index: 1;
}// 水印的样式
.water-wrapper {
 position: absolute;
 top: 0;
 left: 0;
 width: 1000px;
 // height: 2237px; // 水印这里的高度不能写死,需要获取上面内容的高度
 z-index: 999;
}
</style>

注意:之前写页面考虑不周到,发现对接接口后,添加水印的高度可能会有问题,尝试发现是父元素高度问题,因为我子元素都是组件的形式,请求数据前高度和请求后高度可能存在偏差和不一样,考虑到vue渲染的问题,导致水印获取父元素的高度不对,大家在对接接口后尝试会发现这个问题,现在改下mounted里面的调用,需要在获取数据后去获取父元素的高度

具体看如下代码:

<template>
 <div class="preview-wrapper">
   <div class="preview-btn">
     <div class="content">
       <div class="download-btn">
         <div><span @click="handlePrint">打印</span></div>
         <div><span @click="handleExport">下载</span></div>
       </div>
     </div>
   </div>

<div class="user-info" ref="pdfWrapper">
     <!--  内容自己定义 -->
     ...
     ....
     .....
     ......
   .......

<!-- 水印要放到页面上面 用个div 定位到页面上方-->
     <div
       class="water-wrapper"
       :style="{ backgroundImage: `url(${orgBackground})` }"
     ></div>
   </div>

</div>
</template><script>
//工具方法,导出操作
import { downloadPDF,  previewWater } from '@/utils/pdf.js'
import { dataInfo } from '@/api/userinfo.js'export default {
 name: 'userinfo',
 components: {},
 props: {},
 data() {
   return {
     orgBackground: '',
     // 接口数据
     dataInfo: {}
   }
 },
 computed: {},
 watch: {
   // 注意这里的dataInfo 是调接口查询后的数据 变化时去添加水印
 dataInfo(){
 // 等待 DOM 渲染后处理
  this.$nextTick(() => {
 this.orgBackground = previewWater('XXXX网站', 'http://xxxxxxx.xxx.xx')
   // 打印的当前元素的内容区域的高度
   let userInfoWrapper = document.querySelector('.user-info')
   console.log(userInfoWrapper, userInfoWrapper.offsetHeight)
   // 获取水印的遮罩层,因为水印要放到内容区域的上方,而背景图默认是放下方
   let waterWrapper = document.querySelector('.water-wrapper')
   waterWrapper.style.cssText = `height: ${userInfoWrapper.offsetHeight}px;`
     })
 }
 },
 created() {},
 mounted() {},
 methods: {
   handlePrint() {
     window.print()
   },
   handleExport() {
     downloadPDF(this.$refs.pdfWrapper, this.pfdName)
   },
   // 调接口数据
   handleDatainfo(){
     dataInfo().then((res) => {
       this.dataInfo = res.data
     }).catch((error) => {
     console.log(error)
     })
   }
 }
}
</script><style scoped lang="scss">
div {
 // webkit 为Google Chrome Safari 等浏览器内核
 -webkit-print-color-adjust: 'exact';
 print-color-adjust: 'exact';
 color-adjust: 'exact';
}
.preview-wrapper {
 // position: relative;
 z-index: 999;
}
.user-info {
 width: 1000px;
 background: #fff;
 position: relative;
 top: 0;
 left: 0;
 right: 0;
 bottom: 0;
 margin: 0 auto;
 padding: 0 50px 100px;
 z-index: 1;
}// 水印的样式
.water-wrapper {
 position: absolute;
 top: 0;
 left: 0;
 width: 1000px;
 // height: 2237px; // 水印这里的高度不能写死,需要获取上面内容的高度
 z-index: 999;
}
</style>

友情提示:对于处理这部分类型的功能,要考虑的地方要注意,比如获取页面后的高度处理,图片的高度问题等等,希望程序猿们遇到问题解决问题!

封装异步PDF下载函数并添加loading效果

因产品需求的需要,当用户下载PDF时发现可能需要等待一段时间,所以加个loading效果;

export const downloadPDFTwo = (el, title) => {
 return new Promise(async (resolve, reject) => {
   try {
     html2canvas(el, {
       allowTaint: true,
       useCORS: true,
       dpi: 120, // 图片清晰度问题
       background: '#FFFFFF', //如果指定的div没有设置背景色会默认成黑色
     }).then(canvas => {
       //未生成pdf的html页面高度
       let leftHeight = canvas.height
       console.log(leftHeight)        //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
       let a4Width = 595.28
       let a4Height = 841.89        //一页pdf显示html页面生成的canvas高度;
       let a4HeightRef = Math.floor((canvas.width / a4Width) * a4Height)        //pdf页面偏移
       let position = 0        // canvas.toDataURL() 返回一个包含图片展示的 数据URL。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。
       // 返回值是一个数据url,是base64组成的图片的源数据、可以直接赋值给图片的src属性。
       let pageData = canvas.toDataURL('image/jpeg', 1.0)        let pdf = new jsPDF('p', 'pt', 'a4') //A4纸,纵向

// let index = 1
       let createCanvas = document.createElement('canvas')
       let height        pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen')        let pdfName = title || "个人简历"
       function createImpl(canvas) {
         console.log(leftHeight, a4HeightRef)
         if (leftHeight > 0) {
           // index++
           let checkCount = 0
           if (leftHeight > a4HeightRef) {
             let i = position + a4HeightRef
             for (i = position + a4HeightRef; i >= position; i--) {
               let isWrite = true
               for (let j = 0; j < canvas.width; j++) {
                 let c = canvas.getContext('2d').getImageData(j, i, 1, 1).data
                 if (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff) {
                   isWrite = false
                   break
                 }
               }
               if (isWrite) {
                 checkCount++
                 if (checkCount >= 10) {
                   break
                 }
               } else {
                 checkCount = 0
               }
             }
             height = Math.round(i - position) || Math.min(leftHeight, a4HeightRef)
             if (height <= 0) {
               height = a4HeightRef
             }
           } else {
             height = leftHeight
           }            createCanvas.width = canvas.width
           createCanvas.height = height            // console.log(index, 'height:', height, 'pos', position)
           console.log('height:', height, 'pos', position)            // getContext()方法可返回一个对象,该对象提供了用于在画布上绘图的方法和属性。
           let ctx = createCanvas.getContext('2d')            ctx.drawImage(
             canvas,
             0,
             position,
             canvas.width,
             height,
             0,
             0,
             canvas.width,
             height,
           )            // let pageHeight = Math.round((a4Width / canvas.width) * height)
           // pdf.setPageSize(null, pageHeight)
           if (position != 0) {
             pdf.addPage()
           }
           pdf.addImage(
             createCanvas.toDataURL('image/jpeg', 1.0),
             'JPEG',
             10,
             20,
             a4Width - 30,
             (a4Width / createCanvas.width) * height - 30,
           )
           leftHeight -= height
           position += height
           if (leftHeight > 0) {
             setTimeout(createImpl, 100, canvas)
           } else {
             pdf.save(pdfName + '.pdf')
             pdf.successFlag = true
             resolve(pdf)
           }
         }
       }        //当内容未超过pdf一页显示的范围,无需分页
       if (leftHeight < a4HeightRef) {
         pdf.addImage(
           pageData,
           'JPEG',
           10,
           20,
           a4Width - 30,
           (a4Width / canvas.width) * leftHeight - 30,
         )
         pdf.save(pdfName + '.pdf')
         pdf.successFlag = true
         resolve(pdf)
       } else {
         try {
           pdf.deletePage(0)
           setTimeout(createImpl, 100, canvas)
         } catch (err) {
           console.log(err)
           reject(err)
         }
       }
     })
   } catch (error) {
     reject(error)
   }
 })
}

页面里面使用时添加loading效果即可

async handleExport() {
     try {
       this.fullscreenLoading = true
       let pdf = await downloadPDFTwo(this.$refs.pdfWrapper, this.pfdName)
       // 对 封装的函数添加成功的属性 这里去判断是否成功
       console.log(pdf, pdf.successFlag)
       if (pdf.successFlag) {
         this.fullscreenLoading = false
       } else {
         this.fullscreenLoading = false
         console.log('下载打印失败,请重新尝试');
       }
     } catch (error) {
       console.log(error);
     }
},

页面代码如下:

<template>
 <div class="preview-wrapper">
   <div class="preview-btn">
     <div class="content">
       <div class="download-btn">
         <div><span @click="handlePrint">打印</span></div>
         <div><span @click="handleExport" v-loading.fullscreen.lock="fullscreenLoading">下载</span></div>
       </div>
     </div>
   </div>

<div class="user-info" ref="pdfWrapper">
     <!--  内容自己定义 -->
     ...
     ....
     .....
     ......
   .......

<!-- 水印要放到页面上面 用个div 定位到页面上方-->
     <div
       class="water-wrapper"
       :style="{ backgroundImage: `url(${orgBackground})` }"
     ></div>
   </div>
 </div>
</template><script>
//工具方法,导出操作
import { downloadPDF,  previewWater } from '@/utils/pdf.js'
import { dataInfo } from '@/api/userinfo.js'export default {
 name: 'userinfo',
 components: {},
 props: {},
 data() {
   return {
     orgBackground: '',
     // 接口数据
     dataInfo: {},
     fullscreenLoading: false,
   }
 },
 computed: {},
 watch: {
   // 注意这里的dataInfo 是调接口查询后的数据 变化时去添加水印
 dataInfo(){
 // 等待 DOM 渲染后处理
  this.$nextTick(() => {
 this.orgBackground = previewWater('XXXX网站', 'http://xxxxxxx.xxx.xx')
   // 打印的当前元素的内容区域的高度
   let userInfoWrapper = document.querySelector('.user-info')
   console.log(userInfoWrapper, userInfoWrapper.offsetHeight)
   // 获取水印的遮罩层,因为水印要放到内容区域的上方,而背景图默认是放下方
   let waterWrapper = document.querySelector('.water-wrapper')
   waterWrapper.style.cssText = `height: ${userInfoWrapper.offsetHeight}px;`
     })
 }
 },
 created() {},
 mounted() {},
 methods: {
   handlePrint() {
     window.print()
   },
   async handleExport() {
      try {
       this.fullscreenLoading = true
       let pdf = await downloadPDFTwo(this.$refs.pdfWrapper, this.pfdName)
       // 对 封装的函数添加成功的属性 这里去判断是否成功
       console.log(pdf, pdf.successFlag)
       if (pdf.successFlag) {
         this.fullscreenLoading = false
       } else {
         this.fullscreenLoading = false
         console.log('下载打印失败,请重新尝试');
       }
     } catch (error) {
       console.log(error);
     }
   },
   // 调接口数据
   handleDatainfo(){
     dataInfo().then((res) => {
       this.dataInfo = res.data
     }).catch((error) => {
     console.log(error)
     })
   }
 }
}
</script><style scoped lang="scss">
div {
 // webkit 为Google Chrome Safari 等浏览器内核
 -webkit-print-color-adjust: 'exact';
 print-color-adjust: 'exact';
 color-adjust: 'exact';
}
.preview-wrapper {
 // position: relative;
 z-index: 999;
}
.user-info {
 width: 1000px;
 background: #fff;
 position: relative;
 top: 0;
 left: 0;
 right: 0;
 bottom: 0;
 margin: 0 auto;
 padding: 0 50px 100px;
 z-index: 1;
}// 水印的样式
.water-wrapper {
 position: absolute;
 top: 0;
 left: 0;
 width: 1000px;
 // height: 2237px; // 水印这里的高度不能写死,需要获取上面内容的高度
 z-index: 999;
}
</style>

来源:https://blog.csdn.net/weixin_42681295/article/details/125387581

标签:vue,页面打印,页面下载,loading
0
投稿

猜你喜欢

  • Python德劳内三角剖分详解

    2021-02-18 23:08:49
  • antd-日历组件,前后禁止选择,只能选中间一部分的实例

    2024-04-27 15:56:35
  • MySql创建分区的方法实例

    2024-01-23 22:17:40
  • Pytorch中torch.flatten()和torch.nn.Flatten()实例详解

    2021-09-15 06:39:43
  • master数据库损坏的解决办法有哪些

    2024-01-16 16:30:06
  • Pytorch中的广播机制详解(Broadcast)

    2022-11-17 05:22:51
  • python练习程序批量修改文件名

    2022-01-29 06:53:12
  • ASP实例:幻灯片新闻代码

    2008-11-21 17:40:00
  • python tqdm用法及实例详解

    2023-11-03 01:54:53
  • Python获取DLL和EXE文件版本号的方法

    2023-09-07 11:43:23
  • SQL 2008 FileStream数据类型

    2008-10-28 21:07:00
  • 关于Dreamweaver乱码问题的解决方案

    2010-09-02 12:36:00
  • python实现从ftp服务器下载文件的方法

    2023-08-02 20:50:54
  • jquery效率探索

    2008-01-08 18:06:00
  • 多种语言下获取当前页完整URL及其参数

    2022-02-28 23:23:29
  • ASP中3种分页显示的性能比较

    2007-08-15 13:37:00
  • C#程序连接数据库及读取数据库中字段的简单方法总结

    2024-01-15 18:01:51
  • javascript面向对象技术基础(四)

    2010-02-07 13:15:00
  • Python实现的生成自我描述脚本分享(很有意思的程序)

    2023-08-14 20:21:06
  • 在JavaScript中,为什么要尽可能使用局部变量?

    2009-03-01 12:38:00
  • asp之家 网络编程 m.aspxhome.com