관련 지식
javascript, promise, canvas, html2canvas, polyfill, es6-promise
웹 페이지의 스크린샷을 만들기 위해 더 이상 화면캡쳐 프로그램이 필요가 없을수도 있습니다. html2canvas
는 screenshot 을 만들수 있는 매우 가벼운 라이브러리 입니다.
경로 : https://html2canvas.hertzen.com/
문서 : https://html2canvas.hertzen.com/documentation
설치 : npm, yarn, link …
버전 : v1.0.0-alpha.12
사용법은 문서에도 있지만 매우 간단합니다. 캡쳐 하고 싶은 DOM을 html2canvas()
함수의 파라미터로 전달해서 호출하면 Promise
객체를 리턴받을수 있고 그것을 통해 특정 영역을 포함한 canvas
객체를 받을수가 있습니다.
<div id="capture" style="padding: 10px; background: #f5da55">
<h4 style="color: #000; ">Hello world!</h4>
</div>
html2canvas(document.querySelector("#capture")).then(canvas => {
document.body.appendChild(canvas)
});
공식 예제인 위 샘플은 너무 단조롭죠. 캡쳐된 화면도 단지 화면에 보여줄 뿐입니다. 이것을 좀 응용하는 예제를 살펴 보겠습니다. 앞으로 사용할 HTML 예제는 아래와 같습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<title>CSS Template</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
/* Style the header */
header {
background-color: #666;
padding: 30px;
text-align: center;
font-size: 35px;
color: white;
}
/* Create two columns/boxes that floats next to each other */
nav {
float: left;
width: 30%;
height: 300px; /* only for demonstration, should be removed */
background: #ccc;
padding: 20px;
}
/* Style the list inside the menu */
nav ul {
list-style-type: none;
padding: 0;
}
article {
float: left;
padding: 20px;
width: 70%;
background-color: #f1f1f1;
height: 300px; /* only for demonstration, should be removed */
}
/* Clear floats after the columns */
section:after {
content: "";
display: table;
clear: both;
}
/* Style the footer */
footer {
background-color: #777;
padding: 10px;
text-align: center;
color: white;
}
/* Responsive layout - makes the two columns/boxes stack on top of each other instead of next to each other, on small screens */
@media (max-width: 600px) {
nav, article {
width: 100%;
height: auto;
}
}
</style>
</head>
<body>
<input type="button" value="캡쳐" />
<h2>CSS Layout Float</h2>
<p>In this example, we have created a header, two columns/boxes and a footer. On smaller screens, the columns will stack on top of each other.</p>
<p>Resize the browser window to see the responsive effect (you will learn more about this in our next chapter - HTML Responsive.)</p>
<header>
<h2>Cities</h2>
</header>
<section>
<nav>
<input type="button" value="캡쳐" />
<ul>
<li><a href="#">London</a></li>
<li><a href="#">Paris</a></li>
<li><a href="#">Tokyo</a></li>
</ul>
</nav>
<article>
<h1>London</h1>
<p>London is the capital city of England. It is the most populous city in the United Kingdom, with a metropolitan area of over 13 million inhabitants.</p>
<p>Standing on the River Thames, London has been a major settlement for two millennia, its history going back to its founding by the Romans, who named it Londinium.</p>
<input type="button" value="캡쳐" />
</article>
</section>
<footer>
<p>Footer</p>
</footer>
</body>
</html>
버튼이 위치한 영역을 캡쳐하기
위 html에는 3개의 버튼이 있습니다. 캡쳐할 요소를 일일이 지정하는 것도 괜찮겠지만 버튼이 존재하는 영역의 DOM을 캡쳐하는 것으로 만들어보겠습니다. 화살표 함수가 익숙치 않은 분들을 위해 보통 함수 형태로 변경했습니다.
$(":button").on('click', async function(e) {
html2canvas(e.target.parentElement).then(function(canvas) {
document.body.appendChild(canvas)
});
});
canvas -> image 변환
콜백함수로 전달된 변수 canvas
에는 실제로 canvas 요소가 들어있습니다. 화면에 그려진 캔버스 요소는 이미지로 저장도 되지만 일반적인 <img>
태그가 더 익숙하실 것입니다. 그렇다면 canvas에서 제공하는 api를 이용하여 이미지 태그에 사용할 수 있는 Data URL 문자열로 변환하면 됩니다.
canvas.toDataURL("image/jpeg")
위 함수를 호출하면 아래와 같은 Data URL 문자열을 리턴합니다.(“image/jpeg” 외 다른 포맷도 가능)
저 문자열을 이미지 태그로 만들어보겠습니다.
html2canvas(e.target.parentElement).then(function(canvas) {
$('body').append('<img src="' + canvas.toDataURL("image/jpeg") + '"/>');
});
이미지 파일로 다운로드 하기
미리보기 기능이 필요한 경우가 아니라면 캡쳐한 이미지를 화면에 보이고 싶은 경우 보다는 ‘파일 업로드’ 또는 ‘파일 다운로드’ 형태가 더 자주 쓰일 것입니다. 이미지 파일로 바로 다운로드 하는 방법은 약간의 꼼수만 추가하면 됩니다.
먼저 HTML 태그 하나를 추가합니다. 화면에 보일 요소가 아니므로 display
속성을 none
으로 합니다.
<a id="target" style="display: none"></a>
이미 짐작하신 분도 있겠지만 위에 추가한 앵커 태그의 속성에 Data URL을 넣어주고 클릭 이벤트만 발생 시키면 됩니다.
html2canvas(e.target.parentElement).then(function(canvas) {
var el = document.getElementById("target");
el.href = canvas.toDataURL("image/jpeg");
el.download = '파일명.jpg';
el.click();
});
폴리필 추가하기
html2canvas
는 promise
를 사용하기 때문에 프로미스를 지원하지 않는 인터넷 익스플로러에서는 사용할 수가 없습니다. 그러나 promise
를 polyfill
처리를 한다면 IE9 이상에서 동작 가능하다고 나와있습니다. 한번 폴리필까지 적용해보겠습니다.
다양한 폴리필 들이 있지만 (아마도) 가장 많이 사용되는 es6-promise
를 사용하겠습니다.
경로 : https://www.npmjs.com/package/es6-promise
설치 : npm, yarn, CDN, download
버전 : 4.2.6
샘플에서는 CDN 으로 적용할 것입니다. 아래 링크를 추가합니다.
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script>
그리고 익스플로러로 테스트를 해보면 이상하게도 아무런 반응이 없습니다. 개발자도구를 열어보면 콘솔 에러를 볼 수 있습니다.
링크에 Data URL을 넣고 다운로드 받는 방법은 크롬에선 이상 없지만 익스플로러에선 정상적으로 동작하지 않습니다. 따라서 다른 방법을 사용해야 합니다.
if (navigator.msSaveBlob) {
var blob = canvas.msToBlob();
return navigator.msSaveBlob(blob, '파일명.jpg');
}
정리
이상으로 웹 화면을 캡쳐해서 이미지로 저장하는 방법을 알아 보았습니다. 우리가 흔히 사용하던 캡쳐 프로그램과 달리 특정 영역을 겹쳐서 캡쳐할수 있는것은 아니지만 외부 플러그인 사용없이 웹 화면에 보이는 그대로 이미지로 내려받을 수 있었습니다.
그러나 이것도 100% 동일한 이미지로 캡쳐되진 않습니다. 캡쳐 방법이 HTML의 DOM과 CSS 속성을 캔버스에 옮겨 이미지화 시키는 것인데 일부 CSS는 지원하지 않기 때문입니다. 아래 요소로 인해 캡쳐에 문제가 있다면 다른 css를 사용하는 것을 고려하셔야 할것 같습니다.
최종 샘플
<!DOCTYPE html>
<html lang="en">
<head>
<title>CSS Template</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
/* Style the header */
header {
background-color: #666;
padding: 30px;
text-align: center;
font-size: 35px;
color: white;
}
/* Create two columns/boxes that floats next to each other */
nav {
float: left;
width: 30%;
height: 300px; /* only for demonstration, should be removed */
background: #ccc;
padding: 20px;
}
/* Style the list inside the menu */
nav ul {
list-style-type: none;
padding: 0;
}
article {
float: left;
padding: 20px;
width: 70%;
background-color: #f1f1f1;
height: 300px; /* only for demonstration, should be removed */
}
/* Clear floats after the columns */
section:after {
content: "";
display: table;
clear: both;
}
/* Style the footer */
footer {
background-color: #777;
padding: 10px;
text-align: center;
color: white;
}
/* Responsive layout - makes the two columns/boxes stack on top of each other instead of next to each other, on small screens */
@media (max-width: 600px) {
nav, article {
width: 100%;
height: auto;
}
}
</style>
</head>
<body>
<input type="button" value="캡쳐" />
<h2>CSS Layout Float</h2>
<p>In this example, we have created a header, two columns/boxes and a footer. On smaller screens, the columns will stack on top of each other.</p>
<p>Resize the browser window to see the responsive effect (you will learn more about this in our next chapter - HTML Responsive.)</p>
<header>
<h2>Cities</h2>
</header>
<section>
<nav>
<input type="button" value="캡쳐" />
<ul>
<li><a href="#">London</a></li>
<li><a href="#">Paris</a></li>
<li><a href="#">Tokyo</a></li>
</ul>
</nav>
<article>
<h1>London</h1>
<p>London is the capital city of England. It is the most populous city in the United Kingdom, with a metropolitan area of over 13 million inhabitants.</p>
<p>Standing on the River Thames, London has been a major settlement for two millennia, its history going back to its founding by the Romans, who named it Londinium.</p>
<input type="button" value="캡쳐" />
</article>
</section>
<footer>
<p>Footer</p>
</footer>
<a id="target" style="display: none"></a>
<script>
$(":button").on('click', function(e) {
// html2canvas(e.target.parentElement).then(function(canvas) {
// document.body.appendChild(canvas)
// });
// html2canvas(e.target.parentElement).then(function(canvas) {
// $('body').append('<img src="' + canvas.toDataURL("image/jpeg") + '"/>');
// });
html2canvas(e.target.parentElement).then(function(canvas) {
if (navigator.msSaveBlob) {
var blob = canvas.msToBlob();
return navigator.msSaveBlob(blob, '파일명.jpg');
} else {
var el = document.getElementById("target");
el.href = canvas.toDataURL("image/jpeg");
el.download = '파일명.jpg';
el.click();
}
});
});
</script>
</body>
</html>
'javascript' 카테고리의 다른 글
[javascript] 웹 화면 부분 캡쳐 만들기 (1) | 2019.04.18 |
---|---|
[javascript] UMD 모듈 만들기 (0) | 2019.04.17 |
[javascript] 다른 도메인의 iframe 리사이즈 하기 (0) | 2019.04.11 |
[javascript] 버튼 클릭으로 텍스트 복사하기 (1) | 2019.04.10 |
[javascript] 티스토리 오픈API 연동하기(Implicit 방식) (0) | 2019.04.01 |