Image resizer by react

바다·2022년 7월 27일
1

project

목록 보기
4/6
post-thumbnail

1. 개발 동기

노션 클론 코딩 프로젝트를 하고 있었고, 노션에서 이미지를 삽입하고 이미지의 상하좌우를 마우스로 드래그 하면 이미지의 사이즈를 변경할 수 있고, 변경된 사이즈대로 이미지 파일이 저장되는 기능을 제공하고 있어서 해당 기능을 만들어야했다.

2. 프로젝트 소개

🐳 image resizer 프로젝트 사이트로 바로가기

🐳 image resizer Github으로 바로가기

3. 배운 것들

1) label 을 통한 input type="file" 커스텀 시, onChange 실행

label 의 for(typescript에서는 htmlFor)에 커스텀할 input의 id를 넣고 input의 display :none 을 하면, label을 클릭하면 input 이 실행되는 것은 알고 있었다.

        <div id="loader">
          <label
            id="loaderActualBtn"
            htmlFor='loaderInput'
          >
            Upload image file
          </label>
          <input
            id="loaderInput"
            type="file"
            accept ="image/jpeg, image/jpg, image/png"
            onChange={onChangeFile}
          />
        </div>

그러나, 파일을 선택할 수 있는 창이 뜨지만, input의 onChange 함수가 실행되지 않는 문제점이 있었다. 이는 input의 display 가 none이기 때문에 발생하는 오류인 것 같았다. 그래서 input이 화면상에서 보이지 않는 방법으로 display: none이 아닌, input의 위치를 화면 밖에 위치하는 방법을 사용해 오류를 잡을 수 있었다.

  • display:none으로 인한 onchange 오류 해결 방법
 input{
    position: absolute;
    top:-1000vh;
  }

2) onMouseDown , onMouseUp and onMouseMove

이미지의 사이즈를 줄이는 방법으로 사용한 이벤트가 onMouseDown,onMouseUp ,onMouseMove이다. (터치를 통한 조작이 필요하다면 onTouchStart, onTouchEnd, onTouchMove를 이용하면 된다.)
사이즈 조절 버튼을 눌렀을때(onMouseDown) 원래의 사이즈와 드래그 여부를 의미하는 객체의 값을 정하고, 마우스를 드래그하며(이미지의 상위 요소영역에서 마우스가 움직임-onMouseMove) 이미지와 그를 감싸는 영역의 사이즈를 변경하고 원하는 사이즈가 되었을때 사용자가 눌렀던 마우스의 버튼에서 손을 떼면(onMouseUp) 이미지의 사이즈가 변경된 사이즈로 확정되고 다운로드 받을 수 있는 버튼이 생긴다.

mousedown 과 mouseup은 click 이벤트와 다른 점을 무엇일까🧐?

차이점은 마우스의 어느 버튼을 조작했을 경우에 이벤트가 발생하느냐와 지정된 영역(=버튼의 영역)밖에서도 이벤트가 실행되느냐이다.

mousedown 이벤트와 mouseup 이벤트는 마우스의 좌우 버튼 모두에서 일어나지만 click 이벤트는 오직 왼쪽 버튼에서만 일어난다.

버튼의 영역을 벗어나서 누른 마우스의 버튼에서 손을 떼면 mouseup이벤트는 실행되지만 click이벤트는 실행되지 않는다.
해당 프로젝트에서는 조작이 보다 용이하게 하기 위해 사이즈 조작버튼이 있는 image 의 상위 요소인 inner에서 이미지 사이즈를 확정 하는 함수를 실행하기 때문에 실제 프로젝트에서 mouseup와 click의 차이가 없지만, 사이즈 조작버튼에 사이즈를 확정하는 함수를 clcik 이벤트로 지정할 경우에는 마우스 드래그와 함께 사이즈가 변경되면서 사이즈 조작버튼의 위치가 바뀌어서 click 이벤트가 발생하지 않는다.

만약 하나의 객체에 mousedown, mouseup, click 이벤트를 모두 적용시킨가면 어떻게 될까? 이벤트는 down-up-click 순으로 진행된다.

3) useRef 와 리렌더링

프로젝트에서 드래그 여부(사진을 편집한다는 의미) 와 이미지의 이전 사이즈의 상태변화를 위해 useState, useReducer가 아닌 useRef를 사용했다. 그 이유는 useRef는 state의 value값을 변경하지만 useState, useReducer와 달리 re-render하지 않기때문이다.

  • Hoops API 설명

    Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render.

리렌더링 하지 않는 useRef가 이 프로젝트에 더 적합한 이유는 사진 편집의 여부와 이미지의 이전 사이즈의 상태 변경이 수시로 이루어 지기 때문이다. 특히 마우스를 드래그하고 있는 동안 이전 사이즈의 변경은 계속 이루어진다. 사이즈 변경시마다, 사이즈 조정 버튼을 누를 때마다 리렌더링이 일어난다면 속도저하, 버범임, 사용자의 불편함,에러 발생 가능성으로 인해 상태 변화 시 리렌더링이 일어나는 useState와 useReducer는 적합하지 않다.

4) 변경된 사이즈의 이미지 다운로드

변경된 사이즈의 이미지를 다운로드할 수 있게 하기위해서 canvas에서 변경된 사이즈의 이미지를 그리고, 해당 url을 a의 href를 넣고, a태그의 download를 이용했다.

A. canvas에 이미지 그리기

 function drawImgCanvas(){
   const downloadCanvas =document.getElementById("downloadCanvas") as HTMLCanvasElement;
   const ctx =downloadCanvas.getContext('2d');
   if(ctx !==null && imgLoadStyle !== undefined && url !==null){
     ctx.clearRect(0,0, downloadCanvas.width, downloadCanvas.height);
     // 변경된 이미지를 그리기 전에 이전에 캔버스에 있는 이미지를 지움
     const newImg = new Image();
     const width = Number(imgLoadStyle.width);
     //imgLoaStyle 는 변경된 이미지의 사이즈를  참조함
     const height= Number(imgLoadStyle.height);
     downloadCanvas.width =width;
     downloadCanvas.height =height;
     newImg.onload=()=>{
       ctx.drawImage(newImg,0,0 , width, height );
     };
     newImg.src =url; 
   }
 };

B. a tag를 이용해 이미지 다운로드

  • a.href= 다운로드 받은 이미지url (canvas에서 받음)
  • a.downlad = 다운로드 시 파일의 이름
  • a.click : 다운로드 실행
const downloadImg=()=>{
    const downlink =document.getElementById("downlink") as HTMLAnchorElement ;
    const downloadCanvas =document.getElementById("downloadCanvas") as HTMLCanvasElement;
    const canvasUrl =downloadCanvas.toDataURL();
    downlink.href =canvasUrl;
    if(fileName!==null){
      const dotIndex = fileName.indexOf(".");
      const name =fileName.slice(0, dotIndex);
      const fileType =fileName.slice(dotIndex);
      const reName =`${name}_resizer${fileType}`;
      downlink.download = reName;
    }
    downlink.click();
  };

C. Canvas에 이미지를 꽉 채우기

canvas의 사이즈가 이미지의 사이즈 보다 작거나 크다면, 사용자가 다운로드 받은 이미지는 화면에 보이는 변경된 이미지와 다르게 된다. 따라서 canvas와 이미지의 사이즈를 동일하게 하는 것이 중요하다. 그러기 위해서는 꼭 다음과 같이 canvas와 이미지를 그리는 영역의 사이즈를 이미지의 사이즈와 동일하게 해주어야한다.


downloadCanvas.width =width;
downloadCanvas.height =height;
///width = 이미지 가로 길이, height =이지미 세로 길이
newImg.onload=()=>{
  ctx.drawImage(newImg,0,0 , width, height );
 };
profile
🐣프론트 개발 공부 중 (우테코 6기)

0개의 댓글