JS를 이용해 스크린샷을 찍는다는 발상은 정말 말도 안 되는 일이라고 생각합니다. 우선 JS는 운영체제의 스크린샷 기능을 호출할 수 있는 권한이 없습니다. 둘째, 브라우저(BOM)에서는 관련 스크린샷을 제공하지 않습니다. 몇 가지 고민 끝에 몇 가지 아이디어가 떠올랐습니다. 예제 코드에서는 최근 NetEase Hearthstone 관련 페이지 개발에 참여했습니다. Box. 카드 그룹 공유 페이지를 작업 중입니다(주소: Hearthstone Box 카드 그룹 공유). 요구 사항: 사용자는 이 덱을 사진 형식으로 친구들과 공유할 수 있습니다. 원래 접근 방식은 서버를 사용하여 페이지를 이미지로 변환한 다음 이미지 주소를 프런트 엔드에 반환하는 것이었습니다. 글쎄, 이것은 꽤 좋으며 서버는 변환된 이미지를 캐시할 수도 있고 다음 요청에서 이미지 주소를 직접 반환할 수도 있습니다. 원칙적으로는 아무런 문제가 없습니다. 그런데 문제가 발생합니다. 백그라운드에서 변환된 사진이 페이지 내용과 일치하지 않는 경우가 있고, 일부 내용이 누락되는 경우도 있습니다. PM 언니는 이 문제를 해결해야 한다고 매우 불쾌해합니다. 어쨌든 페이지를 이미지로 변환하는 인터페이스는 백그라운드에서 수행되므로 내 알 바가 아닙니다! 은근히 기뻐하던 중에 비극적인 일이 일어났는데, 페이지의 일부 콘텐츠가 비동기적으로 로드되기 때문에(예: 하단의 QR 코드가 캔버스를 통해 생성됨) 가끔 서버 변환이 불안정하다고 합니다. 비동기적으로 렌더링된 콘텐츠는 가로챌 수 없습니다. 직설적으로 말하면 그는 이 문제를 해결할 방법이 없습니다. 누가 프런트 엔드에 비동기 렌더링을 사용하라고 지시했습니까? 마지막으로 리더는 JS를 사용하여 직접 스크린샷을 찍을 수 있는지 시도해 보라고 요청했습니다. 이렇게 하면 서버에 대한 부담을 줄일 수 있을 뿐만 아니라 위의 버그도 해결할 수 있습니다.
처음에는 JS를 사용하여 스크린샷을 찍는다는 아이디어가 매우 터무니없다고 생각했습니다(내 무지를 비난합니다. 지난 몇 년 동안 프론트엔드가 너무 빨리 발전했습니다). 우선 JS에는 다음과 같은 권한이 없습니다. 운영 체제의 스크린샷 기능을 호출하고 두 번째로 브라우저(BOM)를 호출하며 관련 스크린샷 인터페이스를 제공하지 않습니다. 어떻게 해야 하나요? 궁금한 점이 있으면 Google에 문의하세요. 그런 다음 JS html to png를 검색한 후 render-html-to-an-image를 찾았습니다. 나는 몇 가지 아이디어를 얻기 시작했습니다. 답변에서 누군가 DOM을 캔버스로 변환할 수 있다고 언급했습니다. 또 캔버스다! 산과 강을 벗어나 길을 찾기가 정말 어려웠지만, 또 다른 마을에는 아직 밝은 미래가 있었습니다! 그런 다음 캔버스에 대한 DOM을 검색하여 잘 알려진 mdn 문서 Drawing_DOM_objects_into_a_canvas를 찾았습니다. 그러다가 나는 그 문서를 진지하게 읽기 시작했다. 문서 시작 부분에서 언급했듯이 DOM을 캔버스로 변환할 수는 없지만 DOM을 SVG로 변환한 후 SVG를 캔버스에 그릴 수는 있습니다. 어떤 사람들은 왜 먼저 dom을 svg로 변환해야 합니까?라고 묻습니다. 이는 SVG가 XML 표현을 사용하고 구조가 DOM과 일치하기 때문일 수 있습니다.
다음은 공식 문서의 단계별 튜토리얼입니다.
1. Blob의 미디어 유형은 "image/svg+xml
"여야 합니다.
2 svg 요소가 필요합니다. 3. svg에서 foreignObject
요소를 요소image/svg+xml
"
2.需要一个 svg 元素
3.在 svg 元素里面插入一个 foreignObject
元素
4.在 foreignObject 元素里面放入符合规范的 html
把dom转成canvas就这么简单,就上面几个步骤。下面是文档给出的一上简单的demo:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <canvas id="canvas" style="border:2px solid black;" width="200" height="200"> </canvas> <script> var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' + '<foreignObject width="100%" height="100%">' + '<p xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' + '<em>I</em> like ' + '<span style="color:white; text-shadow:0 0 2px blue;">' + 'cheese</span>' + '</p>' + '</foreignObject>' + '</svg>'; var DOMURL = window.URL || window.webkitURL || window; var img = new Image(); var svg = new Blob([data], {type: 'image/svg+xml'}); var url = DOMURL.createObjectURL(svg); img.onload = function() { ctx.drawImage(img, 0, 0); DOMURL.revokeObjectURL(url); } img.src = url; </script> </body> </html>
复制代码,运行一下,哇,帅呆了,浏览器上出现了超酷的两行艺术字呢!
嗯,原来dom转成canvas这么简单啊?那我通过 document.body.innerHTML
把body里面的所有dom取出来,然后放到 foreignObject 元素里面,不就OK了、把整个页面都截取下来了吗?
demo仅仅是个Hello World,但是实际项目中的Dom结构比这个复杂多了,比如,引入了外部样式表、图片、而且还可能某些标签不符合xml规范(如缺少闭合标签等)。下面的举个简单的例子,.container不是使用行内样式的,而是在style标签里面定义,字体红色,转成图片后,样式不生效。
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> .container { color: red; } </style> </head> <body> <p class="container" > Hello World! </p> <canvas id="canvas" style="border:2px solid black;" width=200" height="200"> </canvas> <script> var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' + '<foreignObject width="100%" height="100%">' + '<p xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' + document.querySelector('.container').innerHTML + '</p>' + '</foreignObject>' + '</svg>'; var DOMURL = window.URL || window.webkitURL || window; var img = new Image(); var svg = new Blob([data], {type: 'image/svg+xml'}); var url = DOMURL.createObjectURL(svg); img.onload = function() { ctx.drawImage(img, 0, 0); DOMURL.revokeObjectURL(url); } img.src = url; </script> </body> </html>
既然外部样式不生效,那我们可以通过JS遍历所有的dom元素,把全部的样式通过element.style对象添加到行内样式啊。这个思路听起来不错,但是,实现这个把外部样式转成行内样式的函数我还真写不出来啊。需求比较紧,也没有那 多时间去瞎折腾了,所以,就想找找有没有现成的库。于是又去google一下。很幸运, 一下子就搜到了一个叫做html2canvas的库,非常棒的一个库,很强大、但用法非常简单.就这么简单的方法,就可以把我的整个页面截图下来了:
function convertHtml2Canvas() { html2canvas(document.body, { allowTaint: false, taintTest: true }).then(function(canvas) { document.body.appendChild(canvas); }).catch(function(e) { console.error('error', e); }); }
目前还有一个问题,就是这种方法默认是把整个页面截取下来的(就是说,会以你的innerHeight和innerWidth为边界,会存在大量的空白),可是,我的卡组只是占了页面的一小部分,我只想要卡组的部分啊。其实已经有了canvas就好办了,我们可以对它进行处理啊。大概思路是:1.把上面得到的canvas对象转成Blob并放到一个img元素。然后再把img.src绘制到canvas里面。这时候调用canvas.drawImage
// Converts canvas to an image function convertCanvasToImage(canvas) { var image = new Image(); image.src = canvas.toDataURL("image/png", 0.1); return image; } // Converts image to canvas; returns new canvas element function convertImageToCanvas(image, startX, startY, width, height) { var canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; canvas.getContext("2d").drawImage(image, startX, startY, width, height, 0, 0, width, height); return canvas; }
document.body.innerHTML
을 사용하여 본문의 DOM을 모두 꺼낸 다음 foreignObject 요소에 넣으면 괜찮지 않나요? 전체 페이지가 캡처됩니다. 🎜🎜🎜데모는 단지 Hello World이지만 실제 프로젝트의 Dom 구조는 이보다 훨씬 더 복잡합니다. 예를 들어 외부 스타일 시트, 그림 및 일부 태그가 XML 사양을 따르지 않을 수 있습니다. 닫는 태그 등). 다음은 간단한 예입니다. .container는 인라인 스타일을 사용하지 않지만 스타일 태그에 정의되어 있습니다. 글꼴은 이미지로 변환된 후에는 스타일이 적용되지 않습니다. 🎜function convertHtml2Canvas() { html2canvas(document.body, { allowTaint: false, taintTest: true }).then(function(canvas) { var img = convertCanvasToImage(canvas); document.body.appendChild(img); img.onload = function() { img.onload = null; canvas = convertImageToCanvas(img, 0, 0, 384, 696); img.src = convertCanvasToImage(canvas).src; $(img).css({ display: 'block', position: 'absolute', top: 0, left: 400 + 'px' }); } }).catch(function(e) { console.error('error', e); }); }
canvas.drawImage
메소드를 호출하면 우리가 원하는 콘텐츠를 가로챌 수 있습니다. 다음 두 함수는 캔버스를 이미지로 변환하거나 그 반대로 변환합니다. 🎜// Converts canvas to an image function convertCanvasToImage(canvas) { var image = new Image(); image.src = canvas.toDataURL("image/png", 0.1); return image; } // Converts image to canvas; returns new canvas element function convertImageToCanvas(image, startX, startY, width, height) { var canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; canvas.getContext("2d").drawImage(image, startX, startY, width, height, 0, 0, width, height); return canvas; }
然后,再把我们上面的写的 convertHtml2Canvas 改成下面的:
function convertHtml2Canvas() { html2canvas(document.body, { allowTaint: false, taintTest: true }).then(function(canvas) { var img = convertCanvasToImage(canvas); document.body.appendChild(img); img.onload = function() { img.onload = null; canvas = convertImageToCanvas(img, 0, 0, 384, 696); img.src = convertCanvasToImage(canvas).src; $(img).css({ display: 'block', position: 'absolute', top: 0, left: 400 + 'px' }); } }).catch(function(e) { console.error('error', e); }); }
这时候就可以把它的页面的某部分内容进行截取下来了。效果如卡组分享测试页面。页面左边部分是DOM结构的,右边部分是则是使用html2canvas转换出来的图片。长得一模一样,毫无毛病哈。
关于JS页面截图的就写到这里啦,因为也只刚刚接触,很多东西也理解得不到位,欢迎各大神指点。后面会深入学习一下html2canvas的源码,进一步理解dom to canvas的原理。
위 내용은 스크린샷 기능을 구현한 JavaScript 예시 공유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!