HTML5 캔버스에서 드래그 앤 드롭을 이용하여 섬네일 만들기

이 글은 Creating thumbnails with drag and drop and HTML5 canvas 을 번역한 글입니다.

HTML5 캔버스는 뛰어난 특징을 가지고 있다. 겉보기에는 브라우저 내에서 저수준의 API를 이용하여 그림을 그리는 것뿐이지만 실제로는 이를 이용하여 이미지나 문서 내 비디오 컨텐츠를 조정하거나 변경할 수 있다. 이제 캔버스를 이용하여 브라우저 문서 안에 있는 이미지의 섬네일을 생성하기 위해 사용하는 FileReader API에 대해 살펴볼 것이다. 최종 코드는 [Github]에 올려져 있고 [온라인 데모]도 확인할 수 있다. YouTube에는 화면 캡춰도 올려져 있다.

1단계: 브라우저에서 파일 얻어내기

브라우저 내의 이미지 크기를 조정하기 위해 우선 첫 번째로 어떻게든 이미지를 가져와야 한다. 그래서 페이지에 엘리먼트를 추가해서 드래그 앤 드롭 이벤트 핸들러를 설정한다.

s.addEventListener( 'dragover', function ( evt ) {
  evt.preventDefault();
}, false );
s.addEventListener( 'drop', getfiles, false );

엘리먼트 위의 무언가를 드래그할 때 기본 동작을 하지 않는 코드를 추가했다. 그 이유는 이미지를 드래그했을 때 브라우저에서 이미지를 보여주지 않기 위해서이다. 그리고나서 문서 내 모든 파일을 읽어서 이미지 리사이징과 섬네일 생성을 하도록 전달하는 getfile()함수를 추가한다.

function getfiles( ev ) {
  var files = ev.dataTransfer.files;
  if ( files.length > 0 ) {
    var i = files.length;
    while ( i-- ) {
      var file = files[ i ];
      if ( file.type.indexOf( 'image' ) === -1 ) { continue; }
      var reader = new FileReader();
      reader.readAsDataURL( file );
      reader.onload = function ( ev ) {
        var img = new Image();
        img.src = ev.target.result;
        img.onload = function() {
        imagetocanvas( this, thumbwidth, thumbheight, crop, background );
        };
      };
    }
  }
  ev.preventDefault();
};

드롭 이벤트의 dataTransfer 속성에는 드롭된 모든 파일 목록을 포함하고 있어서 적어도 1개 이상의 파일이 있고 그 이상일 경우 반복해서 가져올 수 있다.

파일 중 이미지가 아닌 파일이 있다면 (다시 말해 파일 타입에 ‘image’라는 문자열을 포함하고 있지 않다면) 아무것도 할 필요가 없고 다음 파일을 탐색하면 된다.

이미지 파일이라면 새로운 FileReader의 인스턴스로 생성하여 Data URL로서 파일을 조회할 수 있도록 한다.

새로운 이미지를 생성한 후에 이미지를 로드할 때 전달받은 값으로 src 속성값을 설정한다. 그리고나서 그 이미지를 리사이즈할 값과 함께  imagetocanvas() 함수를 호출할 때 파라미터로 전달한다. (데모에서는 form 값으로 전달한다)

function imagetocanvas( img, thumbwidth, thumbheight, crop, background ) {
  c.width = thumbwidth;
  c.height = thumbheight;
  var dimensions = resize( img.width, img.height, thumbwidth, thumbheight );
  if ( crop ) {
    c.width = dimensions.w;
    c.height = dimensions.h;
    dimensions.x = 0;
    dimensions.y = 0;
  }
  if ( background !== 'transparent' ) {
    cx.fillStyle = background;
    cx.fillRect ( 0, 0, thumbwidth, thumbheight );
  }
  cx.drawImage( 
    img, dimensions.x, dimensions.y, dimensions.w, dimensions.h 
  );
  addtothumbslist( jpeg, quality );
};

imagetocanvas() 함수에서는 파라미터로 전달받은 크기로 캔버스 크기를 조절하면서 기존 이미지가 섬네일에 더해지지 않도록 캔버스를 정리한다. 그리고나서 resize()함수를 이용하여 사이즈에 맞게 이미지를 리사이즈한다. 소스 코드에서 무엇을 하는지 확인할 수 있듯이, 단지 이미지를 사이즈에 맞게 조정할 뿐이다. 이 함수를 사용하면 새로운 이미지 객체와 그 이미지가 위치할 캔버스 위의 x/y 좌표를 반환한다.

만약 전체 크기의 섬네일이 필요없다면 캔버스를 잘라내지 않고 x/y좌표를 0으로 초기화해서 캔버스 사이즈를 조정하면 된다.

배경화면이 필요하면 캔버스에 색깔을 입히면 된다. 그 이후에 캔버스에 이미지와 함께 x/y좌표와 width/height를 전달하면 된다.

여태까지 캔버스에 새로운 이미지를 생성하는 것에만 신경썼지만 아직 새로운 이미지를 얻어온 것은 아니다. 마지막에 addtothumbslist() 함수를 호출해야 한다.

function addtothumbslist( jpeg, quality ) {
  var thumb = new Image(),
      url = jpeg ? c.toDataURL( 'image/jpeg' , quality ) : c.toDataURL();
  thumb.src = url;
  thumb.title = Math.round( url.length / 1000 * 100 ) / 100 + ' KB';
  o.appendChild( thumb );
};

이 함수에서 새로운 이미지를 생성할 때 사용자가 JPG/PNG이미지를 원하는지 확인하도록 한다. 만약 PNG라면 이미지 품질은 좀 더 좋아지겠지만 파일 크기는 상대적으로 커질 것이다. JPG라면 캔버스의 toDataURL() 함수를 호출하면서 2개의 파라미터를 전달하는데 하나는 JPEG mime type이고 또 하나는 이미지 품질을 의미한다. (0-1사이의 값을 설정하고, 1인 경우 가장 좋은 품질을 의미한다.) PNG포맷을 원한다면 기본값으로 파라미터 없이 toDataURL() 함수를 호출하면 된다.

이미지 src값을 url 형태의 문자열로 생성하고 제목에 소수점 2자리 반올림 한 이미지 용량을  KB단위로 보여준다. 이제 섬네일을 페이지 위의 결과 엘리먼트에 더하면 된다.

이렇게 하면 끝이다. 이제 바로 브라우저 안의  이미지를 드래그 앤 드롭하여 섬네일을 만들 수 있고 그걸 저장하거나 다운로드한 이미지들을 한번에 얻을 수도 있다). Zip.js를 추가하여 zip파일로 만들수도 있으니 이 글을 읽는 독자는 아마도 시도해볼 것 같다. 🙂

더 읽을거리:

작성자: Joohee Kang

Joohee Kang가 작성한 문서들…


1개 댓글

  1. workdic

    PHP GD로나 만들수 있던 것을 자바스크립트로 대체할 수 있었다니.. 좋은 정보 감사합니다.

    10월 21st, 2015 at 11:22 오전

댓글 쓰기