반응형
아래 결과는 링크에서 확인할 수 있다.
이미지를 캡처하여 웹 프론트엔드에 붙여넣기를 해보자.
아래와 같이 div 태그로 이미지를 붙여넣을 영역을 만든다.
onPaste는 Ctrl + V (붙여넣기) 를 할 경우 동작하는 이벤트다.
<div
onPaste={handleImagePaste}
style={{ border: "1px solid #ddd", padding: "20px", textAlign: "center" }}
>
<h1>이미지 붙여넣기</h1>
{image && <img src={image} />}
</div>
onPaste는 useState와 함께 아래와 같이 동작한다.
클립 보드에 존재하는 데이터 중 "image"를 찾는다. (originalEvent는 옛 브라우저에서 사용된다.)
그리고 해당 이미지를 Blob 객체로 만들어서 FileReader로 Load한다.
const [image, setImage] = useState(null);
const handleImagePaste = (event) => {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
const blob = items[i].getAsFile();
const reader = new FileReader();
reader.onload = () => {
setImage(reader.result);
};
reader.readAsDataURL(blob);
break;
}
}
};
실제로 Blob 객체로 이미지를 로드하면 아래와 같이 확인을 할 수 있다.
전체 코드는 다음과 같다.
import React, { useState } from "react";
const Capture = () => {
const [image, setImage] = useState(null);
const handleImagePaste = (event) => {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
const blob = items[i].getAsFile();
const reader = new FileReader();
reader.onload = () => {
setImage(reader.result);
};
reader.readAsDataURL(blob);
break;
}
}
};
return (
<div
onPaste={handleImagePaste}
style={{ border: "1px solid #ddd", padding: "20px", textAlign: "center" }}
>
<h1>이미지 붙여넣기</h1>
{image && <img src={image} />}
</div>
);
};
export default Capture;
선택한 영역에 붙여넣기
이제 조금 응용해서 선택된 영역에 이미지를 붙여넣자.
4개의 영역을 grid로 구분을 하여, 선택한 영역을 useState로 관리한다.
그리고 선택한 영역과 일치하는 곳은 임시로 배경색을 변경하도록 하자.
const [quadrant, setQuadrant] = useState(null);
...
const handleAreaClick = (newQuadrant) => {
setQuadrant(newQuadrant);
};
return (
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr", // Col 1 : 1 분할
gridTemplateRows: "1fr 1fr", // Row 1 : 1 분할
height: "500px",
}}
>
{[0, 1, 2, 3].map((area) => (
<div
key={area}
style={{
border: "1px solid #ddd",
padding: "20px",
textAlign: "center",
backgroundColor: quadrant === area ? "#e0e0e0" : "transparent",
cursor: "pointer",
}}
onClick={() => handleAreaClick(area)}
//onPaste={...}
>
{images[area] && <img src={images[area]} />}
</div>
))}
</div>
);
위의 코드를 실행하면 아래와 같이 선택한 영역의 배경색이 변경된다.
handleImagePaste에 영역이 없는 경우 return 처리하고, 해당 영역에 image를 복사하였다.
const handleImagePaste = (event) => {
if (quadrant === null) {
return;
}
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
const blob = items[i].getAsFile();
const reader = new FileReader();
reader.onload = () => {
setImages((prevImages) => {
const newImages = [...prevImages];
newImages[quadrant] = reader.result;
return newImages;
});
setQuadrant(null);
};
reader.readAsDataURL(blob);
break;
}
}
};
전체 코드는 다음과 같다.
import React, { useState } from "react";
const Capture = () => {
const [quadrant, setQuadrant] = useState(null);
const [images, setImages] = useState(Array(4).fill(null));
const handleImagePaste = (event) => {
if (quadrant === null) {
return;
}
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
const blob = items[i].getAsFile();
const reader = new FileReader();
reader.onload = () => {
setImages((prevImages) => {
const newImages = [...prevImages];
newImages[quadrant] = reader.result;
return newImages;
});
setQuadrant(null);
};
reader.readAsDataURL(blob);
break;
}
}
};
const handleAreaClick = (newQuadrant) => {
setQuadrant(newQuadrant);
};
return (
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr", // Col 1 : 1 분할
gridTemplateRows: "1fr 1fr", // Row 1 : 1 분할
height: "500px",
}}
>
{[0, 1, 2, 3].map((area) => (
<div
key={area}
style={{
border: "1px solid #ddd",
padding: "20px",
textAlign: "center",
backgroundColor: quadrant === area ? "#e0e0e0" : "transparent",
cursor: "pointer",
}}
onClick={() => handleAreaClick(area)}
onPaste={handleImagePaste}
>
{images[area] && <img src={images[area]} />}
</div>
))}
</div>
);
};
export default Capture;
navigator로 구현하기
위의 코드를 navigator로 구현하면 아래와 같다.
import React, { useState } from "react";
const Capture = () => {
const [quadrant, setQuadrant] = useState(null);
const [images, setImages] = useState(Array(4).fill(null));
const handleImagePaste = async () => {
if (quadrant === null) {
return;
}
try {
const clipboardItems = await navigator.clipboard.read();
for (const item of clipboardItems) {
if (
item.types.includes("image/png") ||
item.types.includes("image/jpeg")
) {
const blob = await item.getType("image/png" || "image/jpeg");
const reader = new FileReader();
reader.onload = () => {
setImages((prevImages) => {
const newImages = [...prevImages];
newImages[quadrant] = reader.result;
return newImages;
});
setQuadrant(null);
};
reader.readAsDataURL(blob);
break;
}
}
} catch (error) {
console.error("Error reading clipboard:", error);
}
};
const handleAreaClick = (newQuadrant) => {
setQuadrant(newQuadrant);
};
return (
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr", // Col 1 : 1 분할
gridTemplateRows: "1fr 1fr", // Row 1 : 1 분할
height: "500px",
}}
>
{[0, 1, 2, 3].map((area) => (
<div
key={area}
style={{
border: "1px solid #ddd",
padding: "20px",
textAlign: "center",
backgroundColor: quadrant === area ? "#e0e0e0" : "transparent",
cursor: "pointer",
}}
onClick={() => handleAreaClick(area)}
onPaste={handleImagePaste}
>
{images[area] && <img src={images[area]} />}
</div>
))}
</div>
);
};
export default Capture;
navigator는 최신 브라우저에서만 지원되고 비동기 처리를 해야하지만,
navigator.clipboard에서 다양한 데이터 타입을 다룰 수 있다.
반응형
'개발 > React' 카테고리의 다른 글
리액트 - 깃허브 API로 이미지 업로드하기 (Upload Images with GitHub RESTful API) (0) | 2024.01.17 |
---|---|
리액트 Material - 깃허브 리포지토리 이미지 불러오기 (Load GitHub Repository Images with Image List) (0) | 2024.01.17 |
리액트 - useLocation으로 페이지 이동시 스크롤 상단으로 초기화하기 (Scroll to Top on Page Transition) (0) | 2023.12.16 |
리액트 - Toast UI Editor로 메일 보내기 with nodemailer (0) | 2023.12.10 |
리액트 - IP 변환하기 (IP Converter) (0) | 2023.12.09 |
댓글