当前位置: 首页 > article >正文

前端解决方案:实现网页截图并导出PDF功能

前端解决方案:实现网页截图并导出PDF功能

在前端开发中,我们经常会遇到需要将网页内容导出为PDF的需求。本文将以一个准考证预览和导出的例子,带你一步步实现这个功能。我们会处理包括跨域图片、Canvas绘图、PDF生成等多个技术要点。

请添加图片描述

一、基础环境搭建

首先,我们需要搭建一个基础的HTML结构,并引入必要的依赖。

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>网页截图导出PDF示例</title>
  </head>
  <body>
    <!-- 引入依赖 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
  </body>
</html>

这里我们引入了两个重要的库:

  • html2canvas:用于将网页内容转换为canvas图像
  • jsPDF:用于生成PDF文件

二、创建页面内容

接下来,我们创建一个简单的准考证预览界面:

<div id="ticket">
  <h1>准考证</h1>
  <table border="1">
    <tr>
      <th>考生姓名</th>
      <th>张三猫</th>
    </tr>
    <tr>
      <td>照片</td>
      <td>
        <img src="https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg" alt="" />
      </td>
    </tr>
  </table>
</div>

<button onclick="fetchImage()">检测图片是否支持跨域</button>
<button onclick="newImage()">图片转base64</button>
<button onclick="exportToPDF()">导出PDF准考证</button>

三、处理跨域图片问题

在处理外部图片时,我们首先需要解决跨域问题。

运维:需设置图片允许跨域访问,以阿里云 OSS 跨域规则配置为例。
在这里插入图片描述

前端:先检测图片是否支持跨域访问,支持图片跨域访问的情况下,再把图片转base64。

1. 检测图片跨域支持

function fetchImage() {
  fetch('https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg')
    .then((res) => {
      console.log('支持跨域', res.type)
    })
    .catch((err) => {
      console.log('不支持跨域', err)
    })
}

2. 图片转Base64

function newImage() {
  // 创建图片
  const img = new Image()
  img.src = 'https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg'
  // 设置跨域
  img.crossOrigin = 'anonymous'
  // 监听图片加载
  img.onload = () => {
    // 创建canvas
    const canvas = document.createElement('canvas')
    // 设置canvas的宽高
    canvas.width = img.width
    canvas.height = img.height
    // 获取canvas的上下文
    const ctx = canvas.getContext('2d')
    // 绘制图片
    ctx.drawImage(img, 0, 0)
    // 转换为base64
    const base64 = canvas.toDataURL('image/jpeg')
    console.log('base64转换成功', base64)
  }
}

四、图片处理工具函数

为了确保所有图片都能正确加载和处理,我们需要两个重要的工具函数:

1. 转换图片为Base64

async function convertImageToBase64(url) {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.src = url
    img.crossOrigin = 'anonymous'
    img.onload = () => {
      const canvas = document.createElement('canvas')
      canvas.width = img.width
      canvas.height = img.height
      const ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0)
      resolve(canvas.toDataURL('image/jpeg'))
    }
    img.onerror = () => {
      console.log('图片加载失败')
      reject(new Error('图片加载失败'))
    }
  })
}

2. 等待所有图片加载完成

function waitForImagesLoaded() {
  return Promise.all(
    Array.from(document.images)
      .filter((img) => !img.complete)
      .map(
        (img) =>
          new Promise((resolve) => {
            img.onload = img.onerror = resolve
          })
      )
  )
}

五、实现PDF导出功能

最后,我们来实现核心的PDF导出功能:

async function exportToPDF() {
  try {
    // 1. 等待所有图片加载
    await waitForImagesLoaded()
    
    // 2. 处理页面中的所有图片
    const images = document.querySelectorAll('img')
    for (const img of images) {
      try {
        const base64 = await convertImageToBase64(img.src)
        img.src = base64
      } catch (e) {
        console.error('图片转换失败', e)
      }
    }

    // 3. 将页面转换为canvas
    const ticket = document.getElementById('ticket')
    const canvas = await html2canvas(ticket, {
      scale: 2, // 提高清晰度
      useCORS: true, // 允许跨域
    })
    const imgData = canvas.toDataURL('image/png')

    // 4. 创建PDF文档
    const pdf = new jspdf.jsPDF({
      orientation: 'portrait', // 竖向
      unit: 'mm', // 单位:毫米
      format: 'a4', // A4纸张
    })

    // 5. 计算适合的图片尺寸
    const pageWidth = pdf.internal.pageSize.getWidth()
    const pageHeight = pdf.internal.pageSize.getHeight()
    const imgWidth = pageWidth - 20 // 左右各留10mm边距
    const imgHeight = (canvas.height * imgWidth) / canvas.width

    // 6. 将图片添加到PDF中
    pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight)

    // 7. 下载PDF文件
    pdf.save('张三的准考证.pdf')
  } catch (error) {
    console.error('PDF导出失败', error)
  }
}

六、完整代码

将上述所有代码组合在一起,就构成了一个完整的网页截图并导出PDF的功能。

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>网页截图导出PDF示例</title>
  </head>
  <body>
    <!-- 引入依赖 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

    <div id="ticket">
      <h1>准考证</h1>
      <table border="1">
        <tr>
          <th>考生姓名</th>
          <th>张三猫</th>
        </tr>
        <tr>
          <td>照片</td>
          <td>
            <img src="https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg" alt="" />
          </td>
        </tr>
      </table>
    </div>

    <button onclick="fetchImage()">检测图片是否支持跨域</button>
    <button onclick="newImage()">图片转base64</button>
    <button onclick="exportToPDF()">导出PDF准考证</button>
    <script>
      function fetchImage() {
        fetch('https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg')
          .then((res) => {
            console.log('支持跨域', res.type)
          })
          .catch((err) => {
            console.log('不支持跨域', err)
          })
      }

      function newImage() {
        // 创建图片
        const img = new Image()
        img.src = 'https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg'
        // 设置跨域
        img.crossOrigin = 'anonymous'
        // 监听图片加载
        img.onload = () => {
          // 创建canvas
          const canvas = document.createElement('canvas')
          // 设置canvas的宽高
          canvas.width = img.width
          canvas.height = img.height
          // 获取canvas的上下文
          const ctx = canvas.getContext('2d')
          // 绘制图片
          ctx.drawImage(img, 0, 0)
          // 转换为base64
          const base64 = canvas.toDataURL('image/jpeg')
          console.log('base64转换成功', base64)
        }
      }

      // 先获取图片的base64编码
      async function convertImageToBase64(url) {
        return new Promise((resolve, reject) => {
          const img = new Image()
          img.src = 'https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg'
          img.crossOrigin = 'anonymous'
          img.onload = () => {
            const canvas = document.createElement('canvas')
            canvas.width = img.width
            canvas.height = img.height
            const ctx = canvas.getContext('2d')
            console.log(11111, img)
            ctx.drawImage(img, 0, 0)
            resolve(canvas.toDataURL('image/jpeg'))
          }
          img.onerror = () => {
            console.log(222222, '图片加载失败')
          }
        })
      }

      // 等待图片加载
      function waitForImagesLoaded() {
        return Promise.all(
          Array.from(document.images)
            .filter((img) => !img.complete)
            .map(
              (img) =>
                new Promise((resolve) => {
                  img.onload = img.onerror = resolve
                }),
            ),
        )
      }

      // 修改导出函数
      async function exportToPDF() {
        // 先等待图片加载
        await waitForImagesLoaded()
        // 再处理图片
        const images = document.querySelectorAll('img')
        for (const img of images) {
          try {
            const base64 = await convertImageToBase64(img.src)
            img.src = base64
          } catch (e) {
            console.error('图片转换失败', e)
          }
        }

        // 截图到canvas中
        const ticket = document.getElementById('ticket')
        const canvas = await html2canvas(ticket, {
          scale: 2, // 缩放比例
          useCORS: true, // 允许跨域
        })
        const imgData = canvas.toDataURL('image/png')
        // 创建pdf
        const pdf = new jspdf.jsPDF({
          orientation: 'portrait', // 方向: 竖屏
          unit: 'mm', // 单位: 毫米
          format: 'a4', // 纸张大小: A4
        })

        // 获取pdf的宽高
        const pageWidth = pdf.internal.pageSize.getWidth()
        const pageHeight = pdf.internal.pageSize.getHeight()

        // 计算图片缩放比例以适应页面宽度
        const imgWidth = pageWidth - 20 // 留边距
        const imgHeight = (canvas.height * imgWidth) / canvas.width
        // 添加图片到pdf
        pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight)
        // 下载pdf
        pdf.save('张三的准考证.pdf')
      }
    </script>
  </body>
</html>

七、技术要点总结

  1. 跨域处理

    • 使用 crossOrigin = 'anonymous' 处理跨域图片
    • 将图片转换为Base64格式避免跨域问题
  2. 异步处理

    • 使用 Promise 处理图片加载
    • 使用 async/await 简化异步代码
  3. Canvas操作

    • 创建Canvas元素
    • 设置Canvas尺寸
    • 在Canvas中绘制图片
  4. PDF生成

    • 设置PDF属性(方向、单位、纸张大小)
    • 计算图片在PDF中的合适尺寸
    • 添加图片到PDF并下载

八、注意事项

  1. 确保服务器端图片资源允许跨域访问(设置正确的CORS头)
  2. 考虑图片加载失败的情况,添加适当的错误处理
  3. 根据实际需求调整PDF的参数(如边距、缩放比例等)
  4. 在生产环境中建议使用可靠的CDN或本地托管依赖库

九、扩展优化

  1. 添加加载提示
  2. 支持自定义PDF文件名
  3. 支持自定义PDF页面大小和方向
  4. 添加水印或其他安全标记
  5. 优化图片质量和文件大小

希望这篇教程能帮助你理解和实现网页截图并导出PDF的功能。如果你有任何问题,欢迎在评论区讨论!


http://www.kler.cn/a/601125.html

相关文章:

  • STC32单片机驱动UC1705X点阵屏调试VLCD没升压显示拖影
  • [7-01-03].SpringBoot3集成MinIo
  • Pytorch使用手册—计算机视觉领域的量化迁移学习教程(专题六十三)
  • 【大模型】数字人 EchoMimicV2 的环境配置和使用
  • 哈希表简单例子
  • Node.js 模块加载机制--详解
  • Javaweb后端登录会话技术jwt令牌
  • MySQL学习笔记(四)
  • git上传大文件到远程仓库中
  • RabbitMQ 学习整理2 - 消峰限流
  • 解锁 AWX+Ansible 自动化运维新体验:快速部署实战
  • 向量数据库学习笔记(1) —— 基础概念
  • apache安装脚本使用shell建立
  • C 语言中, scanf 函数在哪些情况下会结束输入读取:
  • 2025-03-25 学习记录--C/C++-PTA 习题11-7 奇数值结点链表
  • 修改git在提交代码时的名称
  • TARS:字节跳动开源的AI智能体,让生活更便捷、工作更高效
  • 蓝桥杯15届B组题解
  • Springboot 学习 之 Shardingsphere 按照日期水平分表(一)
  • 游戏引擎学习第182天