HTML5 를 사용한 이미지 자르기 도구 제작

JavaScriptBeginner
지금 연습하기

소개

이 프로젝트는 간단한 이미지 자르기 도구를 만드는 과정을 안내합니다. 이 프로젝트를 완료하면 사용자가 이미지를 업로드, 표시 및 자를 수 있는 대화형 애플리케이션을 갖게 됩니다.

👀 미리보기

Image cropping tool demo

🎯 작업

이 프로젝트에서는 다음을 배우게 됩니다.

  • 이미지 자르기 도구의 HTML 구조를 만드는 방법
  • CSS 를 사용하여 웹 페이지의 스타일을 지정하여 시각적으로 매력적으로 만드는 방법
  • 사용자 상호 작용을 처리하기 위해 JavaScript 를 사용하여 변수 및 이벤트 리스너를 초기화하는 방법
  • JavaScript 의 FileReader API 를 사용하여 이미지 업로드 및 표시를 처리하는 방법
  • JavaScript 의 Canvas API 를 사용하여 자르기 메커니즘을 구현하는 방법
  • 잘린 이미지를 저장하고 결과를 표시하는 방법

🏆 성과

이 프로젝트를 완료하면 다음을 수행할 수 있습니다.

  • HTML 태그 및 구조 이해
  • CSS 속성 및 선택자를 효과적으로 적용
  • JavaScript 구문, 변수 및 이벤트 리스너 활용
  • 파일 업로드를 처리하기 위해 JavaScript 에서 FileReader API 활용
  • JavaScript 의 Canvas API 를 사용하여 이미지 조작 구현

HTML 구조 배치

요구 사항:

  • HTML 태그 및 구조에 대한 지식.

기능:

  • 사용자가 이미지를 업로드하고 자르기 프로세스를 트리거할 수 있는 인터페이스를 설계합니다.

HTML 코드를 index.html에 포함합니다.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>HTML5 Crop Image</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <input type="file" name="file" id="post_file" />

    <button id="save_button">SAVE</button>
    <div id="label">
      <canvas id="get_image"></canvas>
      <p>
        <canvas id="cover_box"></canvas>
        <canvas id="edit_pic"></canvas>
      </p>
    </div>
    <p>
      <span id="show_edit"></span>
      <span id="show_pic"><img src="" /></span>
    </p>
    <script type="text/javascript" src="main.js"></script>
  </body>
</html>

위에 언급된 세 개의 <canvas> 태그는 이미지와 관련된 콘텐츠를 처리하는 데 사용됩니다. 자세한 처리는 후속 js (JavaScript) 코드에서 제공됩니다. id 가 show_edit 및 id 가 show_pic인 요소는 이미지 미리보기 및 최종 이미지 생성 결과를 보기 위한 것입니다.

✨ 솔루션 확인 및 연습

웹페이지 스타일 지정

요구 사항:

  • CSS 속성 및 선택자에 대한 숙지.

기능:

  • 인터페이스를 사용자 친화적이고 시각적으로 매력적으로 만들기 위해 HTML 요소의 스타일을 지정합니다.

CSS 를 style.css에 포함합니다.

body {
  background-color: #f6f6f6;
  margin: 0;
  padding: 20px;
  text-align: center;
}

#label {
  border: 1px solid #ccc;
  background-color: #fff;
  text-align: center;
  height: 300px;
  width: 300px;
  margin: 20px auto;
  position: relative;
}

#get_image {
  position: absolute;
}

#edit_pic {
  position: absolute;
  display: none;
  background: #000;
}

#cover_box {
  position: absolute;
  z-index: 9999;
  display: none;
  top: 0px;
  left: 0px;
}

#show_edit {
  margin: 0 auto;
  display: inline-block;
}

#show_pic {
  height: 100px;
  width: 100px;
  border: 2px solid #000;
  overflow: hidden;
  margin: 0 auto;
  display: inline-block;
}

canvas {
  position: absolute;
  top: 0;
  left: 0;
}

#save_button {
  padding: 8px 16px;
  background-color: #3498db;
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.2s;
}

#save_button:hover {
  background-color: #2980b9;
}

input[type="file"] {
  margin-bottom: 20px;
}
✨ 솔루션 확인 및 연습

변수 및 이벤트 리스너 초기화

요구 사항:

  • JavaScript 구문, 변수 및 이벤트 리스너에 대한 기본적인 이해.

기능:

  • 자르기 도구의 속성 및 구성을 초기화합니다. 업로드된 이미지를 처리하기 위해 이벤트 리스너를 추가합니다.

main.js에서 자르기 도구 속성을 초기화하고 이벤트 리스너를 설정하는 코드를 작성합니다.

var postFile = {
  init: function () {
    var t = this;
    t.regional = document.getElementById("label");
    t.getImage = document.getElementById("get_image");
    t.editPic = document.getElementById("edit_pic");
    t.editBox = document.getElementById("cover_box");
    t.px = 0; //background image x
    t.py = 0; //background image y
    t.sx = 15; //crop area x
    t.sy = 15; //crop area y
    t.sHeight = 150; //crop area height
    t.sWidth = 150; //crop area width
    document
      .getElementById("post_file")
      .addEventListener("change", t.handleFiles, false);
  }
};

우리의 모든 함수와 변수는 postFile 객체 내에 캡슐화되어 있습니다. 위에 언급된 init 함수는 주로 몇 가지 초기 값을 설정합니다.

t.px = 0;
t.py = 0;
t.sx = 15;
t.sy = 15;
t.sHeight = 100;
t.sWidth = 100;

변수 t.pxt.py는 실시간 미리보기 영역에서 배경 이미지의 좌표를 나타냅니다. t.sx, t.sy, t.sHeightt.sWidth는 이미지의 x, y 좌표와 너비, 높이를 각각 나타냅니다.

또한 document.getElementById를 통해 나중에 조작할 몇 가지 요소를 얻습니다.

document
  .getElementById("post_file")
  .addEventListener("change", t.handleFiles, false);

사용자가 업로드한 파일을 처리하기 위해 idpost_fileinput 폼의 change 이벤트를 리스닝합니다. 여기서 우리는 이것을 handleFiles 함수에 위임합니다. 따라서 다음으로 handleFiles 함수를 구현할 것입니다.

✨ 솔루션 확인 및 연습

이미지 업로드 및 표시 처리

요구 사항:

  • JavaScript 의 FileReader API 에 대한 기본적인 지식.

기능:

  • 사용자가 이미지를 업로드할 때 이미지가 올바르게 처리되고, 읽혀지고, 화면에 표시되도록 합니다.

업로드된 이미지를 처리하고 표시하는 함수로 main.js를 확장합니다.

  1. handleFiles 함수를 구현하려면

    여기서는 HTML5 File API 를 사용하고 있습니다. 먼저 new FileReader()를 호출하여 oFReader라는 FileReader 객체를 인스턴스화합니다. 그런 다음 readAsDataURL() 메서드를 호출하여 파일 내용을 읽고 base64 인코딩 형식으로 변환합니다.

    마지막으로, 파일이 완전히 읽혀지고 로드되면 postFile.paintImage(oFREvent.target.result)를 사용하여 읽은 이미지를 처리합니다. 간단히 말해서, 읽은 이미지 데이터를 브라우저에 다시 그리는 것입니다.

handleFiles: function () {
        var fileList = this.files[0];
        var oFReader = new FileReader();
        oFReader.readAsDataURL(fileList);
        oFReader.onload = function (oFREvent) {
            postFile.paintImage(oFREvent.target.result);
        };
},
  1. paintImage 함수를 구현하려면

    여기서 가장 중요한 단계는 canvas 를 사용하여 컨테이너의 크기에 따라 이미지를 그리는 것입니다. 이전 단계에서 File API 의 FileReader 를 사용하여 업로드하려는 이미지의 URL( oFREvent.target.result의 값) 을 이미 얻었습니다. 다음 단계는 canvas 를 사용하여 이 이미지를 그리는 것입니다. 먼저 getImage.getContext를 사용하여 <canvas id="get_image"></canvas>의 2d 컨텍스트를 얻습니다. 이는 간단히 이미지 내용으로 이해할 수 있습니다. 그 후, new Image()를 사용하여 <img> 요소를 생성하고 해당 src 속성 값을 설정합니다.

paintImage: function (url) {
        var t = this;
        var createCanvas = t.getImage.getContext("2d");
        var img = new Image();
        img.src = url;
        img.onload = function () {
            if (
                img.width < t.regional.offsetWidth &&
                img.height < t.regional.offsetHeight
            ) {
                t.imgWidth = img.width;
                t.imgHeight = img.height;
            } else {
                var pWidth = img.width / (img.height / t.regional.offsetHeight);
                var pHeight = img.height / (img.width / t.regional.offsetWidth);
                t.imgWidth = img.width > img.height ? t.regional.offsetWidth : pWidth;
                t.imgHeight =
                    img.height > img.width ? t.regional.offsetHeight : pHeight;
            }
            t.px = (t.regional.offsetWidth - t.imgWidth) / 2 + "px";
            t.py = (t.regional.offsetHeight - t.imgHeight) / 2 + "px";

            t.getImage.height = t.imgHeight;
            t.getImage.width = t.imgWidth;
            t.getImage.style.left = t.px;
            t.getImage.style.top = t.py;

            createCanvas.drawImage(img, 0, 0, t.imgWidth, t.imgHeight);
            t.imgUrl = t.getImage.toDataURL();
            t.cutImage();
            t.drag();
        };
},

img.onload 함수 내에서 우리의 주요 목표는 비율을 유지하면서 원래 크기로 이미지를 다시 그리는 것입니다. 이것이 if 조건이 있는 이유입니다. 궁극적으로, 우리는 코드 라인 createCanvas.drawImage(img,0,0,t.imgWidth,t.imgHeight);를 사용하여 실제로 이미지를 렌더링합니다.

✨ 솔루션 확인 및 연습

이미지 자르기 기능 구현

요구 사항:

  • 그림 그리기 및 이미지 조작을 위한 JavaScript 의 Canvas API 에 대한 이해.

기능:

  • 표시된 이미지에 자르기 영역을 추가하고 이 영역을 드래그 가능하게 만듭니다. 이를 통해 사용자는 자르기 원하는 영역을 유연하게 선택할 수 있습니다.

main.js에 관련 메서드를 추가합니다.

  1. cutImage 메서드 생성

    cutImage 메서드는 주로 두 가지 작업을 담당합니다. 하나는 마스크 레이어를 생성하는 것이고, 다른 하나는 CSS background 속성을 사용하여 선택한 자르기 영역의 실시간 미리보기를 제공하는 것입니다.

cutImage: function () {
        var t = this;

        t.editBox.height = t.imgHeight;
        t.editBox.width = t.imgWidth;
        t.editBox.style.display = "block";
        t.editBox.style.left = t.px;
        t.editBox.style.top = t.py;

        var cover = t.editBox.getContext("2d");
        cover.fillStyle = "rgba(0, 0, 0, 0.5)";
        cover.fillRect(0, 0, t.imgWidth, t.imgHeight);
        cover.clearRect(t.sx, t.sy, t.sHeight, t.sWidth);

        document.getElementById("show_edit").style.background =
            "url(" + t.imgUrl + ")" + -t.sx + "px " + -t.sy + "px no-repeat";
        document.getElementById("show_edit").style.height = t.sHeight + "px";
        document.getElementById("show_edit").style.width = t.sWidth + "px";
},
  1. drag 메서드 생성
drag: function () {
        var t = this;
        var draging = false;
        var startX = 0;
        var startY = 0;

        document.getElementById("cover_box").onmousemove = function (e) {
            var pageX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
            var pageY = e.pageY - (t.regional.offsetTop + this.offsetTop);

            if (
                pageX > t.sx &&
                pageX < t.sx + t.sWidth &&
                pageY > t.sy &&
                pageY < t.sy + t.sHeight
            ) {
                this.style.cursor = "move";

                this.onmousedown = function () {
                    draging = true;

                    t.ex = t.sx;
                    t.ey = t.sy;

                    startX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
                    startY = e.pageY - (t.regional.offsetTop + this.offsetTop);
                };
                window.onmouseup = function () {
                    draging = false;
                };

                if (draging) {
                    if (t.ex + (pageX - startX) < 0) {
                        t.sx = 0;
                    } else if (t.ex + (pageX - startX) + t.sWidth > t.imgWidth) {
                        t.sx = t.imgWidth - t.sWidth;
                    } else {
                        t.sx = t.ex + (pageX - startX);
                    }

                    if (t.ey + (pageY - startY) < 0) {
                        t.sy = 0;
                    } else if (t.ey + (pageY - startY) + t.sHeight > t.imgHeight) {
                        t.sy = t.imgHeight - t.sHeight;
                    } else {
                        t.sy = t.ey + (pageY - startY);
                    }

                    t.cutImage();
                }
            } else {
                this.style.cursor = "auto";
            }
        };
},

이 메서드를 이해하려면 다음 핵심 사항을 파악해야 합니다.

var pageX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
var pageY = e.pageY - (t.regional.offsetTop + this.offsetTop);

위의 두 줄의 코드를 통해 마우스와 배경 이미지 사이의 거리를 얻습니다. e.pageX는 마우스에서 브라우저의 왼쪽 가장자리까지의 거리를 나타내고, t.regional.offsetLeft + this.offsetLeft은 이미지에서 브라우저의 왼쪽 가장자리까지의 거리를 계산합니다. 마찬가지로, 상단 거리를 추론할 수 있습니다.

 if ( pageX > t.sx && pageX < t.sx + t.sWidth && pageY > t.sy && pageY < t.sy + t.sHeight )

마우스와 배경 이미지 사이의 거리를 이해했다면, 이것은 이해하기 쉬울 것입니다. 즉, 마우스가 이미지 영역 내에 있는지 여부를 결정합니다.

t.ex = t.sx;
t.ey = t.sy;

startX = e.pageX - (t.regional.offsetLeft + this.offsetLeft);
startY = e.pageY - (t.regional.offsetTop + this.offsetTop);

이 두 코드 조각은 강조할 가치가 있습니다. 처음 두 줄은 마지막 스크린샷의 좌표를 기록합니다 (또는 이전 스크린샷이 없으면 초기 좌표). 다음 두 줄은 마우스가 눌렸을 때의 좌표를 기록합니다. console.log()를 사용하여 이러한 값을 개별적으로 검사할 수 있습니다.

if (draging) {
  if (t.ex + (pageX - startX) < 0) {
    t.sx = 0;
  } else if (t.ex + (pageX - startX) + t.sWidth > t.imgWidth) {
    t.sx = t.imgWidth - t.sWidth;
  } else {
    t.sx = t.ex + (pageX - startX);
  }

  if (t.ey + (pageY - startY) < 0) {
    t.sy = 0;
  } else if (t.ey + (pageY - startY) + t.sHeight > t.imgHeight) {
    t.sy = t.imgHeight - t.sHeight;
  } else {
    t.sy = t.ey + (pageY - startY);
  }

  t.cutImage();
}

위의 코드는 본질적으로 다음과 같습니다. 드래그하는 동안에는 좌표 변경에 따라 t.sxt.sy의 값을 실시간으로 업데이트하고 cutImage 메서드를 호출하여 라이브 미리보기를 제공해야 합니다.

이동 중 자르기 영역의 좌표 = 마지막으로 기록된 위치 + (현재 마우스 위치 - 마우스가 눌렸을 때의 위치)

✨ 솔루션 확인 및 연습

잘린 이미지 저장

요구 사항:

  • canvas 를 사용하여 이미지 데이터를 추출하고 표시하는 방법에 대한 지식.

기능:

  • 원하는 이미지 영역을 자른 후, 사용자가 이 잘린 영역을 저장하고 결과를 화면에 표시할 수 있도록 합니다.

잘린 이미지 저장을 처리하도록 main.js의 초기화 함수를 개선합니다.

var postFile = {
  init: function () {
    // ...
    document.getElementById("save_button").onclick = function () {
      t.editPic.height = t.sHeight;
      t.editPic.width = t.sWidth;
      var ctx = t.editPic.getContext("2d");
      var images = new Image();
      images.src = t.imgUrl;

      images.onload = function () {
        ctx.drawImage(
          images,
          t.sx,
          t.sy,
          t.sHeight,
          t.sWidth,
          0,
          0,
          t.sHeight,
          t.sWidth
        );
        document.getElementById("show_pic").getElementsByTagName("img")[0].src =
          t.editPic.toDataURL();
      };
    };
  }
};
postFile.init();

paintImage 메서드를 구현하는 것과 유사하게, 먼저 저장 버튼에 대한 클릭 이벤트를 수신 대기합니다. 그런 다음 drawImage 메서드를 사용하여 선택한 이미지 영역을 렌더링합니다. 마지막으로, toDataURL 메서드를 사용하여 이미지를 base64 인코딩 형식으로 변환합니다. 이 값은 show_pic 아래의 imgsrc 속성에 할당됩니다. 이러한 방식으로 이미지 자르기 및 저장이 완료됩니다.

✨ 솔루션 확인 및 연습

도구 테스트

  • 웹 브라우저에서 index.html을 엽니다.
    open web
  • 이미지를 업로드하고 자르기 기능을 테스트합니다.
  • 페이지의 효과는 다음과 같습니다.
    image cropping tool demo
✨ 솔루션 확인 및 연습

요약

축하합니다! HTML5 와 JavaScript 를 사용하여 기본적인 이미지 자르기 도구를 구축했습니다. 이 기반을 바탕으로 더 발전된 기능이나 다른 이미지 조작 작업을 수행할 수 있습니다. 도구를 개선하거나 다른 웹 개발 프로젝트를 탐색하여 연습하십시오!