用浏览器把 HTML 详情页导出成长图 / PDF(附核心思路)
写商品详情、图文教程时,经常会遇到这种情况:
- 内容是HTML写的,预览很舒服
- 店铺、小程序、网盘说明却只要长图或PDF
- 截屏拼接费劲,打印另存又容易跑版
其实在浏览器里就能做:把 HTML 渲染出来 → 调好宽度 → 截成一张长图,或再塞进 PDF。下面只讲实现思路和关键代码,方便自己改、自己集成。
1. 整体流程
载入 HTML 片段 ↓ 放进固定宽度的预览容器(如 750 / 1200) ↓ 等图片加载完成 ↓ DOM → Canvas(截图) ↓ Canvas → PNG 下载 或 Canvas → jsPDF → PDF 下载宽度建议先定死再截:
| 场景 | 常用宽度 |
|---|---|
| 手机详情长图 | 750 |
| 部分电商稿 | 790 |
| 电脑宽图 | 1200 |
预览用多少宽,导出就按这个宽截,比「先截再缩放」稳得多。
2. 预览容器(先所见,再所得)
<divid="stage"style="width:750px;background:#fff;margin:0 auto;"><!-- 这里塞你的 HTML 详情片段 --></div>如果原文根节点写了max-width:750px,在导出容器里可以强制撑满:
#stage .export-root{max-width:none!important;width:100%!important;margin-left:0!important;margin-right:0!important;box-sizing:border-box;}宽度切换只改容器,不要改用户 HTML 里每一处样式:
functionsetPreviewWidth(px){document.getElementById('stage').style.width=px+'px';}3. 导出前先等图片
外链图没加载完就截,容易出现空白块:
asyncfunctionwaitForImages(root){constimgs=[...root.querySelectorAll('img')];awaitPromise.all(imgs.map((img)=>{if(img.complete&&img.naturalWidth)return;returnnewPromise((resolve)=>{img.addEventListener('load',resolve,{once:true});img.addEventListener('error',resolve,{once:true});});}));}外链图若站点不允许跨域,Canvas 会「污染」或缺图。实务上常见处理:
- 图床开启 CORS;或
- 导出前改成本地可访问路径;或
- 用能把资源嵌进结果的截图库(仍依赖对方是否放行)
4. DOM 转长图(两种常见做法)
方案 A:html2canvas(兼容广)
asyncfunctionexportPng(stage,scale=2){awaitwaitForImages(stage);constcanvas=awaithtml2canvas(stage,{scale,// 2 = 高清,但个别排版可能和预览略有差异useCORS:true,backgroundColor:'#ffffff',width:stage.offsetWidth,height:stage.scrollHeight,});canvas.toBlob((blob)=>{consta=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='export.png';a.click();});}注意:scale > 1时,库会按放大后的逻辑重算文字,行内带背景的代码块最容易和浏览器预览对不齐。不对齐时优先试scale: 1。
方案 B:基于 SVG foreignObject 的截图(更贴预览)
很多「精确模式」工具会走类似modern-screenshot/html-to-image这条路:先克隆 DOM,再画进 Canvas,文字换行通常更接近真实页面。
// 示意:具体 API 以你引入的库为准constcanvas=awaitmodernScreenshot.domToCanvas(stage,{scale:2,backgroundColor:'#ffffff',});工程上可以:默认精确模式,失败再回退 html2canvas。
5. 长图再转 PDF
有了 Canvas 之后,用jsPDF很直接:
asyncfunctionexportPdf(canvas,mode='long'){const{jsPDF}=window.jspdf;constimg=canvas.toDataURL('image/jpeg',0.92);constpxToMm=0.264583;// 按 96dpi 近似if(mode==='long'){// 单页长 PDF:页面尺寸跟图走constw=canvas.width*pxToMm;consth=canvas.height*pxToMm;constpdf=newjsPDF({orientation:w>h?'l':'p',unit:'mm',format:[w,h],});pdf.addImage(img,'JPEG',0,0,w,h);pdf.save('export.pdf');return;}// A4 分页:一张长图切多页constpdf=newjsPDF({orientation:'p',unit:'mm',format:'a4'});constpageW=pdf.internal.pageSize.getWidth();constpageH=pdf.internal.pageSize.getHeight();constimgW=pageW;constimgH=(canvas.height*imgW)/canvas.width;letheightLeft=imgH;letposition=0;pdf.addImage(img,'JPEG',0,position,imgW,imgH);heightLeft-=pageH;while(heightLeft>0.5){position-=pageH;pdf.addPage();pdf.addImage(img,'JPEG',0,position,imgW,imgH);heightLeft-=pageH;}pdf.save('export.pdf');}浏览器对 Canvas 高度有上限(常见大约一万多像素量级)。内容特别长时,需要降清晰度、拆段截取,或换服务端渲染(Puppeteer 一类),这是前端方案的硬边界。
6. 减少「预览正常、导出错位」
实操里最有用的几条:
- 预览宽度 = 导出逻辑宽度(例如直接用 750,而不是 375 再 ×2 指望布局一样)
- 出问题先把清晰度改成1x
- 行内
code/ 带背景的span可加:
code, kbd, samp{box-decoration-break:clone;-webkit-box-decoration-break:clone;}短路径文字必要时display: inline-block; white-space: nowrap;,避免截图引擎把灰底拆成两截。
7. 最小可运行骨架(拼起来)
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"/><title>HTML → 长图 Demo</title><scriptsrc="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script></head><body><buttonid="btn">导出 PNG</button><divid="stage"style="width:750px;margin:20px auto;background:#fff;padding:16px;"><h1>示例标题</h1><p>把你的详情 HTML 贴到这里。</p></div><script>document.getElementById('btn').onclick=async()=>{conststage=document.getElementById('stage');constcanvas=awaithtml2canvas(stage,{scale:2,useCORS:true,backgroundColor:'#ffffff',});consta=document.createElement('a');a.download='demo.png';a.href=canvas.toDataURL('image/png');a.click();};</script></body></html>本地保存成.html,用 Chrome / Edge 打开即可试。正式使用再补:宽度预设、PDF、精确截图引擎、跨域与错位处理。
8. 小结
- 核心不是「神奇算法」,而是固定宽度预览 + DOM 截图 +(可选)写入 PDF
- 难度主要在:图片跨域、超长画布、高清缩放导致的文字错位
- 自己写个 Demo 很快;要做成每天能用的成品,还得把预览、预设、导出选项和避坑打磨一遍
如果你只是想直接用现成的本地小工具(调宽度预览、导出 PNG/PDF),可以在这边看看整理好的版本:
https://zhixuanxingqiu.com
(浏览器打开即用,按需自取;本文只讲原理,方便学习与二次改造。)