在很多 Web 应用场景中,我们都需要实现截图功能,例如生成报告、保存页面状态、用户反馈或分享内容等。通常,市面上有 HTML2Canvas、dom-to-image 等工具,但如果我们希望不依赖这些第三方库,纯粹依靠浏览器原生 API 来实现截图的话该如何去实现呢? 本文将介绍两种使用原生 API 实现前端网页截图的的方法,并给出关键代码的演示,最后再介绍两种开源的截图工具的使用。
使用 Canvas 绘制 Dom 元素 核心原理 基本思路是通过解析 DOM 树,并使用 Canvas 2D API 将每个 DOM 元素(包括其样式、文本和图片)逐个绘制到 canvas 上,最终生成一张完整的截图。
遍历 DOM 树 :从目标元素开始递归遍历所有子元素。
获取计算样式 :利用 window.getComputedStyle
获取每个元素的最终样式(背景、字体、边框等)。
定位绘制 :根据元素的 getBoundingClientRect()
数据,计算相对于目标区域的位置。
使用 Canvas 绘制 :调用 Canvas 2D API,将每个元素绘制到 canvas 上,再通过 canvas.toDataURL()
生成图片。
代码示例 由于 HTML2Canvas 需要处理大量 CSS 特性和复杂布局,这里我们给出一个简化示例,只实现了对基本背景颜色和文本节点的支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 <!DOCTYPE html > <html lang ="zh-CN" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1" > <title > 原生截图示例</title > <style > #capture { width : 400px ; padding : 20px ; background : #f5f5f5 ; border : 1px solid #ccc ; } #capture h1 { color : #333 ; font-size : 24px ; margin : 0 ; } #capture p { color : #666 ; font-size : 16px ; } </style > </head > <body > <div id ="capture" > <h1 > 示例标题</h1 > <p > 这是一个简单的示例,用于演示如何原生实现截图。</p > </div > <button id ="btnCapture" > 截取 #capture</button > <br > <img id ="resultImg" alt ="截图结果" /> <script > function captureElement (element ) { const rect = element.getBoundingClientRect (); const canvas = document .createElement ('canvas' ); canvas.width = rect.width ; canvas.height = rect.height ; const ctx = canvas.getContext ('2d' ); function renderNode (node ) { if (node.nodeType === Node .ELEMENT_NODE ) { const style = window .getComputedStyle (node); const nodeRect = node.getBoundingClientRect (); const x = nodeRect.left - rect.left ; const y = nodeRect.top - rect.top ; const bgColor = style.backgroundColor ; if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent' ) { ctx.fillStyle = bgColor; ctx.fillRect (x, y, nodeRect.width , nodeRect.height ); } Array .from (node.childNodes ).forEach (child => renderNode (child)); } else if (node.nodeType === Node .TEXT_NODE ) { const text = node.textContent .trim (); if (text) { const parentStyle = window .getComputedStyle (node.parentNode ); const font = parentStyle.font ; const color = parentStyle.color ; const parentRect = node.parentNode .getBoundingClientRect (); const x = parentRect.left - rect.left ; const y = parentRect.top - rect.top + parseInt (parentStyle.fontSize , 10 ); ctx.fillStyle = color; ctx.font = font; ctx.fillText (text, x, y); } } } renderNode (element); return canvas; } document .getElementById ('btnCapture' ).addEventListener ('click' , function ( ) { const captureElementNode = document .getElementById ('capture' ); const canvas = captureElement (captureElementNode); const imgData = canvas.toDataURL ('image/png' ); document .getElementById ('resultImg' ).src = imgData; }); </script > </body > </html >
代码解析:
创建 canvas :根据目标元素 #capture
的尺寸创建 canvas,确保捕获的图片与实际尺寸一致。
递归遍历 DOM :renderNode
函数递归遍历目标元素的所有子节点,对每个元素获取其 getBoundingClientRect()
数据,并调用 getComputedStyle
获取样式。
绘制背景与文本 :
输出图片 :最后,将 canvas 内容转换为 DataURL,并设置到 <img>
标签中展示。
核心原理 浏览器提供了 navigator.mediaDevices.getDisplayMedia API,用于捕获用户屏幕、窗口或标签页的媒体流。通过该 API,我们可以请求用户授权选择要共享的屏幕或窗口,然后从返回的媒体流中提取视频帧,将其绘制到 canvas 上,再转换为图片数据。
实例代码 下面的示例展示了如何使用 getDisplayMedia 捕获屏幕,并将一帧图像绘制到 canvas 上,然后生成图片并展示给用户。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 <!DOCTYPE html > <html lang ="zh-CN" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > 原生实现截图示例</title > <style > body { font-family : sans-serif; text-align : center; padding : 20px ; } #resultImg { margin-top : 20px ; max-width : 100% ; border : 1px solid #ccc ; } </style > </head > <body > <h1 > 原生截图示例</h1 > <button id ="captureBtn" > 点击截屏</button > <br > <img id ="resultImg" alt ="截图结果" /> <script > const captureBtn = document .getElementById ('captureBtn' ); const resultImg = document .getElementById ('resultImg' ); captureBtn.addEventListener ('click' , async () => { try { const stream = await navigator.mediaDevices .getDisplayMedia ({ video : true }); const video = document .createElement ('video' ); video.srcObject = stream; await video.play (); video.onloadedmetadata = () => { const canvas = document .createElement ('canvas' ); canvas.width = video.videoWidth ; canvas.height = video.videoHeight ; const ctx = canvas.getContext ('2d' ); ctx.drawImage (video, 0 , 0 , canvas.width , canvas.height ); const imgData = canvas.toDataURL ('image/png' ); resultImg.src = imgData; stream.getTracks ().forEach (track => track.stop ()); }; } catch (error) { console .error ("截图失败:" , error); } }); </script > </body > </html >
代码解析:
getDisplayMedia 当点击按钮时,通过 navigator.mediaDevices.getDisplayMedia({ video: true })
请求用户授权屏幕捕获,返回一个视频流(MediaStream)。
video 元素播放流 创建一个 video 元素,并将视频流设置为其 srcObject
,启动视频播放以确保能捕获到有效帧。
canvas 绘制与截图 在 video.onloadedmetadata
事件中,根据视频尺寸创建一个 canvas,然后调用 ctx.drawImage(video, 0, 0)
将当前视频帧绘制到 canvas 上。最后,调用 canvas.toDataURL('image/png')
将 canvas 内容转换为 Base64 格式的图片数据。
释放资源 捕获截图后,通过停止视频流中所有轨道来释放资源。
注意事项
用户授权
getDisplayMedia 需要用户主动授权,并且用户可以选择整个屏幕、某个窗口或标签页,这意味着截图结果可能不是你期望的特定 DOM 区域,而是用户所选的视图。
捕获内容范围
由于该 API 捕获的是媒体流,如果需要针对某个具体 DOM 元素进行截图,必须让该元素单独展示(例如,在一个专门的页面或弹窗中),否则很难仅捕获页面的一部分。
跨域资源问题
如果页面中存在跨域图片或资源,可能需要服务器支持 CORS,否则捕获的图片可能会受到 tainting 影响,导致无法调用 canvas.toDataURL()
。
性能开销
屏幕捕获和 canvas 绘制均有一定性能开销,建议仅在用户明确需要截图时调用,并适当提供 loading 状态反馈。
使用开源工具包 一般在生产环境中,需要前端对网页进行截图操作的时候,都会使用成熟的开源工具的方案,接下来介绍两个市面上比较常用的两个工具 HTML2Canvas
和 dom-to-image
。
HTML2Canvas HTML2Canvas 是一个流行的 JavaScript 库,它通过遍历 DOM,将页面上的内容”拍照”并转换为 canvas 元素,进而生成图片数据(Base64 或 Blob 格式)。 优点:
缺点:
对复杂的 CSS 样式和跨域图片可能存在限制
截图精度可能受限于浏览器渲染
工具的安装使用代码示例
1 2 3 4 npm install html2canvas --save <script src="https://cdn.jsdelivr.net/npm/[email protected] /dist/html2canvas.min.js" ></script>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <!DOCTYPE html > <html lang ="zh-CN" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > 网页截图示例</title > <style > #capture { padding : 20px ; background : #f5f5f5 ; border : 1px solid #ccc ; } </style > </head > <body > <div id ="capture" > <h1 > 这是需要截图的区域</h1 > <p > 这里包含一些文本和样式。</p > </div > <button id ="btnCapture" > 截图</button > <img id ="resultImage" alt ="截图结果" /> <script src ="https://cdn.jsdelivr.net/npm/[email protected] /dist/html2canvas.min.js" > </script > <script > document .getElementById ('btnCapture' ).addEventListener ('click' , function ( ) { const captureElement = document .getElementById ('capture' ); html2canvas (captureElement).then (canvas => { const imgData = canvas.toDataURL ('image/png' ); document .getElementById ('resultImage' ).src = imgData; }).catch (err => { console .error ('截图失败:' , err); }); }); </script > </body > </html >
dom-to-image dom-to-image 也是一个流行的库,它可以将 DOM 节点转换为图片(SVG、PNG、JPEG 等格式)。 优点:
支持多种图片格式
提供更多配置选项,可以定制输出效果
缺点:
对复杂样式和嵌入资源(例如跨域图片)可能需要额外配置
安装与使用代码示例
1 2 3 4 npm install dom-to-image --save <script src="https://cdn.jsdelivr.net/npm/[email protected] /src/dom-to-image.min.js" ></script>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <!DOCTYPE html > <html lang ="zh-CN" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > dom-to-image 截图示例</title > <style > #capture { padding : 20px ; background : #eef ; border : 1px solid #99c ; } </style > </head > <body > <div id ="capture" > <h1 > 这是需要截图的区域</h1 > <p > 这里包含一些文本和样式。</p > </div > <button id ="btnCapture" > 截图</button > <img id ="resultImage" alt ="截图结果" /> <script src ="https://cdn.jsdelivr.net/npm/[email protected] /src/dom-to-image.min.js" > </script > <script > document .getElementById ('btnCapture' ).addEventListener ('click' , function ( ) { const node = document .getElementById ('capture' ); domtoimage.toPng (node) .then (function (dataUrl ) { const img = document .getElementById ('resultImage' ); img.src = dataUrl; }) .catch (function (error ) { console .error ('截图失败!' , error); }); }); </script > </body > </html >
希望这篇文章能帮到你!
Happy Coding!