생성한 그래프를 이미지로 저장하는 방법을 구글링 하다 html2canvas 라는 라이브러리를 알게 되었다.
DOM 요소를 이미지로 저장해주는 라이브러리에는 대표적인 라이브러리가 몇개 더 있다.
그래프를 svg로 그려주고 있는데, 해당 라이브러리가 SVG 요소도 함께 캡쳐할 수 있기에 채택하게 되었다.
내가 경험한 바로는 이런 테스트 서비스는 채팅이나 sns로 공유해서 모바일로 실행하는 경우가 많았다.
그래서 모바일에서도 파일 저장하는 로직이 동일한가? 하는 궁금증이 있었다.
몇 개의 블로그를 찾아보던 중 아래 블로그에서 html2canvas 만으로 이미지 다운로드를 구현했을때
아이폰 기준 Safari 브라우저 외에는 작동하지 않는다고 하며 솔루션을 제시해주셨다.
https://yong-nyong.tistory.com/53
[React] 화면(DOM) 캡쳐 및 저장 기능 구현하기 (feat. html2canvas, file-saver)
📖 들어가며 React에서 화면 (DOM)를 캡처, 저장하는 기능에 대해서 알아보려 합니다. 작은 토이 프로젝트를 하면서 여러가지 문제를 맞이 했었는데, 결과적으로 성공한 방법들로 포스팅하게 됩니
yong-nyong.tistory.com
바로 file-saver 와 함께 사용하는 것이다.
file-saver는 클라이언트에서 파일 저장을 도와주는 라이브러리 인데, 웬만한 브라우저와 호환된다고 한다
▼ 라이브러리 설치 방법 (React + TypeScript 기준)
npm install html2canvas
npm install file-saver --save
npm install @types/file-saver --save-dev
블로그에 적어주신 예시 코드 그대로 적었을 뿐인데 버튼에 빠방! ! 이미지 파일이 저장되었다.
literally 입틀막
이미지 다운로드 구현을 처음 해보아서 너무 신기했다
//이미지 저장
const imageRef = useRef<HTMLDivElement>(null);
const handleDownload = async () => {
const image = imageRef.current;
if (!image) return;
try {
const canvas = await html2canvas(image, { scale: 2 });
canvas.toBlob((blob) => {
if (blob) {
saveAs(blob, "life-graph.png");
}
});
} catch (err) {
console.log("이미지 저장 중 에러 발생", err);
}
};
...
return (
<button type="button" onClick={handleDownload}>
🖼️ 저장
</button>
)
ref 영역 내에 캡쳐에서 제외하고 싶은 요소가 있다면?
data-html2canvas-ignore 속성을 추가해주면 된다!
속성을 명시했을때 기본값 true을 갖는다.
상위 컴포넌트에만 배경색을 지정해놨었는데, 캡쳐할때 포함이 안되어서 스타일에 추가해주었다😎
지피티 안쓰고 구현해서 더 기뿌다..
다음에 라이브러리가 제공한 기능을 구현해보는것도 좋을것같다.
클립보드에 복사하는 버튼도 추가해보았다
동일하게 html2canvas 를 사용해서 blob을 생성한다.
전역 Navigator.clipboard 로 클립보드에 접근할 수 있다!
//이미지 클립보드에 복사
const handleCopy = async () => {
const image = imageRef.current;
if (!image) return;
try {
const canvas = await html2canvas(image, { scale: 2 });
canvas.toBlob((blob) => {
if (blob) {
//클립보드 복사
navigator.clipboard.write([
new ClipboardItem({
"image/png": blob,
}),
]);
alert("이미지가 클립보드에 저장되었습니다.");
}
});
} catch (err) {
console.log("이미지 클립보드 복사 중 에러 발생", err);
}
};
저장 핸들러와 중복되는 코드가 많기때문에 하나의 함수로 합쳐주고, 파라미터에 따라 분기 처리한다.
const handleImageAction = async (action: "copy" | "save") => {
const image = imageRef.current;
if (!image) return;
try {
const canvas = await html2canvas(image, { scale: 2 });
canvas.toBlob(async (blob) => {
if (!blob) return;
if (action === "copy") {
//클립보드 복사
await navigator.clipboard.write([
new ClipboardItem({
"image/png": blob,
}),
]);
alert("이미지가 클립보드에 저장되었습니다.");
} else if (action === "save") {
// 이미지 다운로드
saveAs(blob, "life-graph.png");
}
});
} catch (err) {
console.log(`이미지 ${action === "copy" ? "클립보드 복사" : "다운로드"} 중 에러 발생`, err);
}
};
핸들러 함수에 파라미터를 넘겨줘야하므로 콜백함수 형태로 할당한다.
그렇지 않으면 함수를 실행한 결과값을 할당하게 되므로 버튼을 클릭하기도 전에 실행된다
return (
<button type="button" onClick={() => handleImageAction("save")} data-html2canvas-ignore>
🖼️ 저장
</button>
<button type="button" onClick={() => handleImageAction("copy")} data-html2canvas-ignore>
📋 복사
</button>)
'개발개발 > LifeGraph_인생그래프' 카테고리의 다른 글
[인생 그래프] SCSS 반응형 디자인 구현하기 (0) | 2025.02.12 |
---|---|
[인생 그래프] 이모지 피커를 추가하자 (0) | 2025.02.11 |
[인생 그래프] 클릭 이벤트 핸들러 뜯어보기 (0) | 2025.02.11 |
[인생 그래프] 그래프가 이상하다 (0) | 2025.02.10 |
[인생 그래프] 클릭 데이터 전역상태로 관리하기 (0) | 2025.02.10 |