업로드한 이미지에 마우스를 갖다대면, 해당 부분을 확대해서 보여주는 기능을 구현하고자 했다.
기능 구현은 완료했지만 깔끔하게 완성하지는 못한 것 같아서 일단 해당 글을 정리하고 부족한 부분을 추가로 수정해갈 예정이다.
---> 해당 기능은 사용 안하게 되었지만.. 그래도 끝까지 완성했다는 것에 의의를 두기로 했다!
참고링크
https://velog.io/@eenaree/react-image-zoom-in
https://prod.velog.io/@oneook/Image-Zoom-on-Hover-바닐라-자바스크립트로-만들기
https://lts0606.tistory.com/514
Scanner
View
MouseMove
이벤트를 활용해야한다.const onMouseMove = (e) => {
// 이벤트 처리 내용 작성
}
...
<div onMouseMove={onMouseMove}>
<img
src={imagePreview}
alt="image"
onLoad={handleImageLoad}
ref={imageRef}
/>
...
</div>
MouseMove 이벤트는 img요소에 부착하면 안된다.
Scanner를 img요소와 동일한 자식요소로 추가해야 이벤트 버블링이 발생해, 박스를 커서 아래에 두고 움직일 수 있기 때문이다. → 따라서 부모요소div
에 이벤트 핸들러를 작성한다.
position absolute
속성을 적용하여 top, left
값에 offset 값을 적용하여 동적으로 위치를 설정한다.relative
는 페이지 상단이 아닌, Scanner의 부모요소에 지정한다.const [scannerPosition, setScannerPosition] = useState({
left: 0,
top: 0,
}); // 마우스커서 위치에 따라 변경되는 Scanner Offset 값
...
<div
className="relative flex justify-center items-center z-0 w-10/12 h-480 min-w-640"
onMouseMove={onMouseMove}
>
<img
src={imagePreview}
alt="image"
onLoad={handleImageLoad}
ref={imageRef}
/>
// Scanner
<span
className="absolute w-256 h-256 z-0 "
style={{
top: `${scannerPosition.top}px`,
left: `${scannerPosition.left}px`,
}}
/>
</div>
💡 이미지 안에서만 Scanner가 존재하기 위한 조건
- Scanner의 왼쪽 위 모서리 좌표
- 이 외의 값을 갖게 되면 이미지 요소를 벗어나게 된다.
// 입력 이미지 크기
const imageWidth = imageRect.width;
const imageHeight = imageRect.height;
// Scanner 크기
const scannerWidth = 256;
const scannerHeight = 256;
// 마우스 커서 Scanner 가운데에 위치하도록 설정
const scannerPosLeft = e.clientX - scannerWidth / 2 - imageRect.x;
const scannerPosTop = e.clientY - scannerHeight / 2 - imageRect.y;
**// 이미지 안에서만 움직일 수 있는 Scanner 범위 구하기**
const allowedPosLeft =
scannerPosLeft >= 0 && scannerPosLeft <= imageWidth - scannerWidth;
const allowedPosTop =
scannerPosTop >= 0 && scannerPosTop <= imageHeight - scannerHeight;
left, top
값을 활용하여 구현할 수 있다.const scannerPosLeft = e.clientX - scannerWidth / 2;
const scannerPosTop = e.clientY - scannerHeight / 2;
element.getBoundingClientRect
메소드를 사용하여 해당 요소의 크기와 현재 브라우저 창 기준 좌표를 확인한다.const scannerPosLeft = e.clientX - scannerWidth / 2 - imageRect.x;
const scannerPosTop = e.clientY - scannerHeight / 2 - imageRect.y;
useRef
를 활용하여 요소의 참조를 갖고오는 방식을 활용할 수 있다.const imageRef = useRef(null); // 입력한 이미지 정보
const [imageRect, setImageRect] = useState(null); // 입력된 이미지 객체 정보
// 이미지 요소의 위치와 크기 정보를 가져오는 함수
const handleImageLoad = () => {
const imageRectVal = imageRef.current.getBoundingClientRect(); // 요소의 상대적 위치와 크기 정보가 담긴 객체반환
setImageRect(imageRectVal);
};
...
<img
src={imagePreview}
alt="image"
onLoad={handleImageLoad}
ref={imageRef}
/>
Scanner가 이미지 일부 요소에 걸쳐있고, 밖으로 튀어나와있는 경우
imageRect
의 값이 첫 렌더링시에는 null
값을 갖고있기 때문에 추가적으로 예외처리를 진행한다.
// 마우스가 움직일 때 마다 실행되는 이벤트함수
const onMouseMove = (e) => {
// 입력된 이미지 객체 정보가 존재하지 않는다면 함수 종료
if (!imageRect) {
return;
}
// 입력 이미지 크기
const imageWidth = imageRect.width;
const imageHeight = imageRect.height;
// Scanner 크기
const scannerWidth = 256;
const scannerHeight = 256;
// 마우스 커서 Scanner 가운데에 위치하도록 설정
const scannerPosLeft = e.clientX - scannerWidth / 2 - imageRect.x;
const scannerPosTop = e.clientY - scannerHeight / 2 - imageRect.y;
// 이미지 안에서만 움직일 수 있는 Scanner 범위 구하기
const allowedPosLeft =
scannerPosLeft >= 0 && scannerPosLeft <= imageWidth - scannerWidth;
const allowedPosTop =
scannerPosTop >= 0 && scannerPosTop <= imageHeight - scannerHeight;
// 현재 scanner offset값 초기화
const scannerPosition = { left: 0, top: 0 };
// 이미지 보더라인 안에서만 움직일 수 있도록 제한
if (allowedPosLeft) {
scannerPosition.left = scannerPosLeft;
} else {
if (scannerPosLeft < 0) {
scannerPosition.left = 0;
} else {
scannerPosition.left = imageRect.width - scannerWidth;
}
}
if (allowedPosTop) {
scannerPosition.top = scannerPosTop;
} else {
if (scannerPosTop < 0) {
scannerPosition.top = 0;
} else {
scannerPosition.top = imageRect.height - scannerHeight;
}
}
// Scanner offset 설정
setScannerPosition({
left: scannerPosition.left,
top: scannerPosition.top,
});
};
const onMouseMove = (e) => {
...
// View offset 설정 -> 2배 확대
setViewPotision({
left: scannerPosition.left * -2,
top: scannerPosition.top * -2,
});
}
**...**
{imageRect && viewPosition && (
<div
className="absolute bg-no-repeat object-scale-down bg-contain z-10 border-4 border-solid border-border-gray"
style={{
width: `${imageRect.width}px`,
height: `${imageRect.height}px`,
top: `${scannerPosition.top}px`,
left: `${scannerPosition.left}px`,
backgroundImage: `url(${imagePreview})`,
backgroundPosition: `${viewPosition.left}px ${viewPosition.top}px`,
backgroundSize: "200% 200%",
}}
/>
)}
img
요소가 아닌, background
이미지로 적용한다.viewPosition
의 상태값이 추가적으로 필요-2
값을 곱해서 설정해야한다. (2배 줌 기준)position
값을 null로 설정해준다.// 마우스가 이벤트가 사라지면 Scanner, View도 사라지게 설정
const onMouseLeave = () => {
if (imageRect) {
setScannerPosition(null);
setViewPotision(null);
}
};
...
<div
className="relative flex justify-center items-center z-0 w-10/12 h-480 min-w-640"
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
>
...
일단 윗글 내용을 바탕으로 이미지 확대기능을 구현했다.
하지만 요소 레이아웃 설계를 잘못한 것도 있고, 이에 맞춰서 개발을 하다보니 이미지 요소의 크기에 따라 zoom 이미지가 올바르게 되지않는 경우가 존재한다.
따라서 레이아웃 설계부터 다시해서 오류를 수정해 나가볼 예정이다.
→ 레이아웃을 다시 설계해서 특정 부분은 확대가 안됐던 문제 해결함.