在 Vue 3 中,如果希望在导出 PDF 时 不仅包含 iframe
内的预览内容,还包含编辑器、按钮等页面其他部分 ,可以使用 html2canvas
捕获整个页面或指定区域,然后通过 jspdf
生成 PDF。以下是优化后的方案:
1. 修改 App.vue
(关键部分)
核心思路
- 捕获整个页面(或指定容器)的 DOM 并转为 Canvas。
- 调整 PDF 布局(如分页、缩放等)。
- 处理
iframe
的特殊情况(确保其内容也被正确捕获)。
修改后的代码
xml
<template>
<div class="app" ref="exportContainer"> <!-- 添加 ref,用于捕获整个区域 -->
<h1>在线代码编辑器 (HTML/CSS/JS)</h1>
<div class="editors">
<!-- HTML 编辑器 -->
<div class="editor-container">
<h3>HTML</h3>
<Codemirror
v-model="htmlCode"
:extensions="[html()]"
:style="{ height: '300px' }"
/>
</div>
<!-- CSS 编辑器 -->
<div class="editor-container">
<h3>CSS</h3>
<Codemirror
v-model="cssCode"
:extensions="[css()]"
:style="{ height: '300px' }"
/>
</div>
<!-- JS 编辑器 -->
<div class="editor-container">
<h3>JavaScript</h3>
<Codemirror
v-model="jsCode"
:extensions="[javascript()]"
:style="{ height: '300px' }"
/>
</div>
</div>
<!-- 预览区域 -->
<div class="preview-container">
<h2>预览效果</h2>
<button @click="exportToPDF">导出完整页面为 PDF</button>
<iframe ref="previewFrame" class="preview-frame"></iframe>
</div>
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { html } from '@codemirror/lang-html';
import { css } from '@codemirror/lang-css';
import { javascript } from '@codemirror/lang-javascript';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
// 代码状态(同原代码)
const htmlCode = ref(`...`);
const cssCode = ref(`...`);
const jsCode = ref(`...`);
// 实时更新预览(同原代码)
const previewFrame = ref(null);
watchEffect(() => {
if (!previewFrame.value) return;
const frameDoc = previewFrame.value.contentDocument;
frameDoc.open();
frameDoc.write(`
<!DOCTYPE html>
<html>
<head>
<style>${cssCode.value}</style>
</head>
<body>
${htmlCode.value}
<script>${jsCode.value}<\/script>
</body>
</html>
`);
frameDoc.close();
});
// 导出完整页面为 PDF
const exportToPDF = async () => {
const container = document.querySelector('.app'); // 捕获整个应用区域
if (!container) return;
// 1. 捕获 DOM 为 Canvas(包括 iframe 内容)
const canvas = await html2canvas(container, {
scale: 2, // 提高分辨率
useCORS: true, // 允许跨域资源
allowTaint: true, // 允许污染画布(处理 iframe 内容)
logging: false, // 关闭日志
windowHeight: container.scrollHeight, // 捕获完整高度
});
// 2. 计算 PDF 尺寸(A4 尺寸:210mm × 297mm)
const imgWidth = 190; // 页面宽度(留边距)
const imgHeight = (canvas.height * imgWidth) / canvas.width; // 按比例缩放高度
// 3. 创建 PDF 并添加图片
const pdf = new jsPDF('p', 'mm', 'a4');
let position = 10; // 初始位置(顶部留白)
// 如果内容超过一页,分页处理
if (imgHeight > 277) { // 297mm - 20mm(边距)
const pageHeight = 277; // 每页可用高度
let remainingHeight = imgHeight;
let offsetY = 0;
while (remainingHeight > 0) {
const currentHeight = Math.min(pageHeight, remainingHeight);
const clip = {
x: 0,
y: offsetY * (canvas.height / imgHeight), // 按比例计算裁剪位置
width: canvas.width,
height: currentHeight * (canvas.height / imgHeight),
};
// 临时 Canvas 裁剪(需额外处理,此处简化)
// 实际项目中建议使用 html2canvas 的 `scrollX/scrollY` 或分块渲染
pdf.addImage(
canvas.toDataURL('image/png'),
'PNG',
10, // 左边距
position,
imgWidth,
currentHeight
);
remainingHeight -= currentHeight;
offsetY += currentHeight;
position += pageHeight; // 移动到下一页
if (remainingHeight > 0) {
pdf.addPage(); // 添加新页
}
}
} else {
// 单页直接添加
pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 10, position, imgWidth, imgHeight);
}
// 4. 保存 PDF
pdf.save('full-page-preview.pdf');
};
</script>
<style scoped>
/* 原有样式保持不变 */
.app {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: white; /* 确保背景为白色(避免透明) */
}
/* 其他样式同原代码 */
</style>
2. 关键优化点
(1) 捕获整个页面
- 通过
ref="exportContainer"
或document.querySelector('.app')
指定要捕获的区域。 html2canvas
默认会捕获所有子元素,包括iframe
(需配置allowTaint: true
)。
(2) 处理 iframe
内容
iframe
的内容需要确保可访问(同源或已配置 CORS)。- 如果
iframe
是跨域的,可能需要通过postMessage
通信或使用代理服务器。
(3) 分页处理
- 单页:直接计算图片尺寸并添加到 PDF。
- 多页:通过循环计算剩余高度,动态添加新页并调整图片位置。
(4) 分辨率优化
- 设置
scale: 2
提高 Canvas 分辨率,避免 PDF 模糊。 - 调整
imgWidth
和imgHeight
确保内容适配 A4 纸张。
3. 替代方案(更精准的分页)
如果 html2canvas
的分页处理不够精确,可以:
-
分块渲染:将页面分成多个部分,分别渲染为 Canvas 后合并到 PDF。
-
使用
puppeteer
(后端方案) :- 通过 Node.js 调用
puppeteer
打印页面为 PDF(支持复杂布局和分页)。 - 示例代码:
- 通过 Node.js 调用
csharp
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://localhost:5173', { waitUntil: 'networkidle0' });
await page.pdf({ path: 'output.pdf', format: 'A4' });
await browser.close();
})();
4. 最终效果
- 导出内容 :包含编辑器、按钮、预览区域(
iframe
内外全部内容)。 - 分页支持:自动根据内容高度分页。
- 高分辨率:PDF 清晰可读。