DOM/Canvas

1-5. 이미지 사용하기 - Canvas API | MDN

AGAL 2021. 3. 8. 10:02
반응형

 

지금까지 우리는 Canvas를 가지고 도형을 만들고 그 도형에 스타일을 적용해봤습니다. 이미지를 사용하는 기능은 <canvas>의 가장 흥미로운 기능 중 하나입니다. 이 기능은 게임의 그래픽 배경이나 혹은 동적 사진 합성을 위해 사용할 수 있습니다. PNG, GIF, JPEG 등 브라우저에서 지원되는 형식 형태라면 어떠한 외부 이미지라도 사용할 수 있습니다. 같은 페이지 소스에서 다른 Canvas 요소로 만들어진 이미지 또한 사용할 수 있습니다!

 

이미지를 캔버스로 불러오는 것은 기본적으로 두 단계를 필요로 합니다.

  1. HTMLImageElement 개체를 참조하거나 다른 캔버스 요소를 소스로 사용합니다. URL을 제공하여 이미지를 사용할 수 있습니다.

  2. drawImage() 기능을 사용하여 캔버스에 나타난 이미지 위에 그림을 그립니다.

 

이를 수행하는 방법을 살펴 보겠습니다.

 

 

이미지 불러 오기

canvas API는 아래의 데이터 타입을 이미지 소스로 사용할 수 있습니다.

  • HTMLImageElement : <img> 요소와 Image() 생성자를 통해 생성된 이미지입니다.

  • SVGImageElement : <image> 요소를 사용하여 삽입된 이미지입니다.

  • HTMLVideoElement : HTML <video> 요소를 이미지 소스로 사용하여 비디오의 현재 프레임을 이미지로 불러옵니다.

  • HTMLCanvasElement : 다른  <canvas> 요소를 이미지 소스로 사용합니다.

 

이렇게 얻은 소스는 CanvasImageSource를 사용하여 불러 올 수 있습니다.

 

캔버스에서 사용할 이미지를 가져 오는 방법에는 여러 가지가 있습니다.

 

같은 페이지의 이미지 사용하기

우리는 다음을 사용하여 같은 페이지에 있는 캔버스 나 이미지를 참고할 수 있습니다.

 

다른 도메인의 이미지 사용하기

<img> 요소의 crossorigin 속성 (HTMLImageElement.crossOrigin 속성에 반영됨)을 사용하면 drawImage() 호출에 사용할 이미지를 다른 도메인에서 로드 할 수 있는 권한을 요청할 수 있습니다. 호스팅 도메인이 이미지에 대한 cross-domain 접근을 허용하면 이미지를 오염시키지 않고 캔버스에서 사용할 수 있습니다. 그렇게 하지 않고 이미지를 사용하면 캔버스가 더러워집니다.

 

다른 캔버스 요소 사용하기

일반 이미지와 마찬가지로 document.getElementsByTagName() 또는 document.getElementById() 메서드를 사용하여 다른 캔버스 요소에 액세스 합니다. 대상 캔버스에서 사용하기 전에 원본 캔버스에 무언가를 그렸는지 확인하십시오.

더 실용적인 용도 중 하나는 다른 더 큰 캔버스의 축소판 그림으로 두 번째 캔버스 요소를 사용하는 것입니다.

 

처음부터 이미지 만들기

또 다른 옵션은 HTMLImageElement 스크립트에서 새 객체를 만드는 것입니다. 이를 위해 Image() 생성자를 사용하면 편리합니다.

var img = new Image();   // Create new img element
img.src = 'myImage.png'; // Set source path

이 스크립트가 실행되면 이미지가 로드되기 시작합니다.

 

이미지 로드가 완료되기 전에 drawImage()를 호출하려고 하면 아무 작업도 수행되지 않습니다 (또는 이전 브라우저에서는 예외가 발생할 수도 있음). 따라서 이미지가 로드되기 전에 이를 시도하지 않도록 load 이벤트를 사용해야 합니다.

var img = new Image();   // Create new img element
img.addEventListener('load', function() {
  // execute drawImage statements here
}, false);
img.src = 'myImage.png'; // Set source path

하나의 외부 이미지만 사용하는 경우 이 방법이 좋은 방법이 될 수 있지만 두 개 이상의 이미지를 추적해야하는 경우에는 좀 더 현명한 방법을 사용해야 합니다. 이미지 사전 로드 전략(image pre-loading tactics)은 본 자습서에서 다루지 않지만 염두에 두어야 합니다.

 

데이터를 사용하여 이미지 불러 오기 데이터를 통해 이미지 삽입 : URL

이미지를 포함하는 또 다른 방법은 data : url을 사용하는 것 입니다. Data URL을 사용하면 코드에서 인코딩된 Base64로 문자열로 이미지를 완전히 정의할 수 있습니다.

var img = new Image();   // Create new img element
img.src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';

Data URL의 한 가지 장점은 서버로의 또 다른 왕복 없이 결과 이미지를 즉시 사용할 수 있다는 것입니다. 또 다른 잠재적 이점은 CSS , JavaScript , HTML 및 이미지를 하나의 파일에 캡슐화하여 다른 위치로 쉽게 이동할 수 있다는 것입니다.

 

이 방법의 일부 단점은 이미지가 캐시되지 않고 더 큰 이미지의 경우 인코딩된 URL이 상당히 길어질 수 있다는 것입니다.

 

비디오 프레임 사용하기

<video> 요소에서 제공하는 비디오의 프레임을 사용할 수도 있습니다 (비디오가 보이지 않는 경우에도). 예를 들어 <video> ID가 "myvideo"인 요소가 있는 경우 다음을 수행 할 수 있습니다.

function getMyVideo() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    return document.getElementById('myvideo');
  }
}

이것은 비디오에 대한 HTMLVideoElement 객체를 반환합니다.이 객체는 이전에 설명한 바와 같이 CanvasImageSource로 사용할 수 있는 객체 중 하나입니다.

 

 

이미지 그리기

원본 이미지 객체를 참조한 후에는 drawImage() 메서드를 사용하여 캔버스에 렌더링 할 수 있습니다. 나중에 확인할 수 있듯이 drawImage() 메서드는 오버로드되어 여러 변형이 있습니다. 가장 기본적인 형태는 다음과 같습니다.

  • drawImage(image, x, y) : 좌표(x, y)에서 이미지 매개 변수로 지정된 캔버스 이미지 소스를 그립니다.

 

SVG 이미지는 루트 <svg> 요소에 너비와 높이를 지정해야합니다.

 

예제 : 기본 선 그래프

다음 예에서는 작은 선 그래프의 배경으로 외부 이미지를 사용합니다. 배경을 사용하면 배경을 생성하는 코드가 필요하지 않기 때문에 스크립트를 상당히 작게 만들 수 있습니다. 이 예제에서는 하나의 이미지만 사용하고 있으므로 이미지 개체의 load 이벤트 핸들러를 사용하여 drawing 구문을 실행합니다. 이 drawImage() 메서드는 캔버스의 왼쪽 상단 모서리인 좌표 (0, 0)에 배경을 배치합니다.

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  var img = new Image();
  img.onload = function() {
    ctx.drawImage(img, 0, 0);
    ctx.beginPath();
    ctx.moveTo(30, 96);
    ctx.lineTo(70, 66);
    ctx.lineTo(103, 76);
    ctx.lineTo(170, 15);
    ctx.stroke();
  };
  img.src = 'https://mdn.mozillademos.org/files/5395/backdrop.png';
}

 

 

 

비례 크기 조정

drawImage() 방법의 두 번째 변형(variant)에서는 두 개의 새로운 파라미터를 추가하고 크기를 조정한 이미지를 캔버스에 배치할 수 있습니다.

  • drawImage(image, x, y, width, height) : 너비 및 높이 매개변수가 추가되어 캔버스에 이미지를 그릴 때 크기를 조정할 수 있습니다.

 

예제 : 이미지를 타일처럼 배치

이 예에서는 이미지를 배경화면으로 사용하고 캔버스에서 여러 번 반복합니다. 이 작업은 조정된 이미지를 반복하고 다른 위치에 배치하여 수행됩니다. 아래 코드에서 첫 번째 for문은 행을 반복합니다. 두 번째 for문은 열을 반복됩니다. 이미지는 원래 크기의 3분의 1(50x38픽셀)로 조정됩니다.

이미지를 확대하면 이미지가 흐릿해지거나 너무 많이 축소하면 깨질 질 수 있습니다. 읽기 쉬운 상태로 유지해야하는 텍스트가 있는 경우 크기 조정을 수행하지 않는 것이 가장 좋습니다.

 

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  var img = new Image();
  img.onload = function() {
    for (var i = 0; i < 4; i++) {
      for (var j = 0; j < 3; j++) {
        ctx.drawImage(img, j * 50, i * 38, 50, 38);
      }
    }
  };
  img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
}

 

 

이미지 자르기

drawImage() 메서드의 세 번째 및 마지막 변형(variant)에는 이미지 소스 외에 8개의 파라미터가 있습니다. 소스 이미지의 한 부분을 잘라낸 다음 캔버스에 크기를 조정하고 그릴 수 있습니다.

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) : 이 함수는 원본 이미지의 왼쪽 상단 모서리 위치가 (sx, sy)이고 폭과 높이가 (sWidth, sHeight)인 영역을 가져와서 캔버스의 (dx, dy) 위치에 배치하고 (dWidth, dHeight) 값으로 크기를 조정합니다.

 

 

이 기능을 제대로 이해하려면 오른쪽 이미지를 보는 것이 도움이 될 수 있습니다. 처음 4개의 파라미터는 원본 이미지에서 슬라이스의 위치와 크기를 정의합니다. 마지막 4개의 파라미터는 대상 캔버스에 이미지를 그릴 직사각형을 정의합니다.

 

슬라이싱은 컴포지션을 만들 때 유용한 도구가 될 수 있습니다. 단일 이미지 파일에 모든 요소를 ​​포함하고 이 방법을 사용하여 전체 도면을 합성 할 수 있습니다. 예를 들어 차트를 만들고 싶다면 필요한 모든 텍스트가 포함된 PNG 이미지를 하나의 파일에 포함하고 데이터에 따라 차트의 배율을 상당히 쉽게 변경할 수 있습니다. 또 다른 장점은 모든 이미지를 개별적으로 로드할 필요가 없으므로 로드 성능을 향상시킬 수 있다는 것입니다.

 

 

 

예제 : 이미지 프레임 넣기

이 예에서는 이전 예에서와 동일한 코뿔소를 사용하지만 머리를 잘라서 사진 프레임으로 합성합니다. 사진 프레임 이미지는 그림자가 포함 된 24비트 PNG입니다. 24비트 PNG 이미지에는 GIF 및 8 비트 PNG 이미지와 달리 전체 8비트 알파 채널이 포함되어 있으므로 매트 색상에 대한 걱정없이 모든 배경에 배치할 수 있습니다.

<html>
 <body onload="draw();">
   <canvas id="canvas" width="150" height="150"></canvas>
   <div style="display:none;">
     <img id="source" src="https://mdn.mozillademos.org/files/5397/rhino.jpg" width="300" height="227">
     <img id="frame" src="https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png" width="132" height="150">
   </div>
 </body>
</html>

 

이번에는 이미지를 로드하기 위해 다른 방식을 취했습니다. 새로운 HTML mageElement 객체를 만들어 로드하는 대신 HTML 소스에 직접 img 태그로 포함시키고 이미지를 불러왔습니다. 이러한 이미지에 대해 CSS 속성 디스플레이를 none으로 설정하면 이미지가 출력에서 숨겨집니다.

 

function draw() {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');

  // Draw slice
  ctx.drawImage(document.getElementById('source'),
                33, 71, 104, 124, 21, 20, 87, 104);

  // Draw frame
  ctx.drawImage(document.getElementById('frame'), 0, 0);
}

 

스크립트 자체는 매우 간단합니다. 각각 <img>에는 ID 속성이 할당되어 있으므로  document.getElementById()를 사용하여 쉽게 선택할 수 있습니다. 그런 다음을 drawImage()을 사용하여 첫 번째 이미지에서 코뿔소를 잘라내어 캔버스에 크기를 조정한 다음 두 번째 drawImage()를 사용하여 프레임을 그립니다.

 

 

아트 갤러리 예제

이 장의 마지막 예에서는 작은 아트 갤러리를 만들 것입니다. 갤러리는 여러 이미지가 포함된 테이블로 구성됩니다. 페이지가 로드되면 각 이미지에 대해 <canvas> 요소가 삽입되고 그 주위에 프레임이 그려집니다.

이 경우 모든 이미지는 이미지 주위에 그려진 프레임과 마찬가지로 고정 된 너비와 높이를 고정됩니다. 이미지의 너비와 높이를 사용하여 프레임 주위에 완벽하게 맞도록 스크립트를 향상시킬 수 있습니다.

아래 코드를 설명하면, Document.images를 반복하고 그에 따라 새 캔버스 요소를 추가합니다. Node.insertBefore()는 새 노드(캔버스 요소)를 삽입하기 전에 요소(이미지)의 부모 노드(테이블 셀)의 메소드입니다.

<html>
 <body onload="draw();">
     <table>
      <tr>
        <td><img src="https://mdn.mozillademos.org/files/5399/gallery_1.jpg"></td>
        <td><img src="https://mdn.mozillademos.org/files/5401/gallery_2.jpg"></td>
        <td><img src="https://mdn.mozillademos.org/files/5403/gallery_3.jpg"></td>
        <td><img src="https://mdn.mozillademos.org/files/5405/gallery_4.jpg"></td>
      </tr>
      <tr>
        <td><img src="https://mdn.mozillademos.org/files/5407/gallery_5.jpg"></td>
        <td><img src="https://mdn.mozillademos.org/files/5409/gallery_6.jpg"></td>
        <td><img src="https://mdn.mozillademos.org/files/5411/gallery_7.jpg"></td>
        <td><img src="https://mdn.mozillademos.org/files/5413/gallery_8.jpg"></td>
      </tr>
     </table>
     <img id="frame" src="https://mdn.mozillademos.org/files/242/Canvas_picture_frame.png" width="132" height="150">
 </body>
</html>

다음은 멋지게 보이게하는 CSS입니다.

body {
  background: 0 -100px repeat-x url(https://mdn.mozillademos.org/files/5415/bg_gallery.png) #4F191A;
  margin: 10px;
}

img {
  display: none;
}

table {
  margin: 0 auto;
}

td {
  padding: 15px;
}

모든 것을 하나로 묶는 것은 프레임 이미지를 그리는 JavaScript입니다.

function draw() {

  // Loop through all images
  for (var i = 0; i < document.images.length; i++) {

    // Don't add a canvas for the frame image
    if (document.images[i].getAttribute('id') != 'frame') {

      // Create canvas element
      canvas = document.createElement('canvas');
      canvas.setAttribute('width', 132);
      canvas.setAttribute('height', 150);

      // Insert before the image
      document.images[i].parentNode.insertBefore(canvas,document.images[i]);

      ctx = canvas.getContext('2d');

      // Draw image to canvas
      ctx.drawImage(document.images[i], 15, 20);

      // Add frame
      ctx.drawImage(document.getElementById('frame'), 0, 0);
    }
  }
}

 

 

이미지 비율 습성 (스케일링 동작) 제어하기

앞서 언급했듯이 이미지 크기 조정은 크기 조정 프로세스로 인해 흐릿하거나 고르지 않은 아티팩트를 초래할 수 있습니다. drawing 컨텍스트의 imageSmoothingEnabled 속성을 사용하여 컨텍스트 내에서 이미지 크기를 조정할 때 이미지 다듬기 알고리즘 사용을 제어 할 수 있습니다. 기본적으로 이 값은 true이며, 크기가 조정될 때 이미지가 부드럽게 처리됩니다. 다음과 같이 이 기능을 비활성화 할 수 있습니다.

ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
반응형