Until now we have created our own shapes and applied styles to them. One of the more exciting features of <canvas>
is the ability to use images. These can be used to do dynamic photo compositing or as backdrops of graphs, for sprites in games, and so forth. External images can be used in any format supported by the browser, such as PNG, GIF, or JPEG. You can even use the image produced by other canvas elements on the same page as the source!
캔버스에 이미지를 가져오는 방법에는 아래의 두가지 절차가 필요합니다:
HTMLImageElement
오브젝트로부터 가져오거나, 다른 캔버스를 소스로 하여 가져오기. URL로부터 이미지를 가져오는것도 가능합니다.drawImage()
함수를 이용하여 이미지 드로잉.
그럼 이제 이걸 어떻게 처리하는지 알아보도록 하겠습니다.
그리기 할 이미지 가져오기
아래의 타입들은 캔버스의 이미지 데이터로 활용 가능한 데이터들입니다.
HTMLImageElement
Image()
생성자를 통해 생성된 이미지 혹은,
<img>
엘리먼트입니다.HTMLVideoElement
- HTML
<video>
엘리먼트에서 프레임을 가져와서 이미지의 소스로 사용할 수 있습니다. HTMLCanvasElement
- 다른
<canvas>
엘리먼트를 이미지의 소스로 사용할 수 있습니다.
These sources are collectively referred to by the type CanvasImageSource
.
캔버스에서 사용할 이미지를 가져오는 방법에는 몇 가지 방법이 있습니다.
같은 페이지 내의 이미지 사용하기
아래의 방법들 중 하나를 사용해 페이지 내의 이미지 엘리먼트를 가져올 수 있습니다.:
document.images
의 이미지 목록document.getElementsByTagName()
메소드- 얻어오고자 하는 이미지의 ID 를 알고있다면
document.getElementById()
메소드를 사용할 수도 있습니다.
다른 도메인의 이미지 사용하기
Using the crossorigin
attribute of an <img>
element (reflected by the HTMLImageElement.crossOrigin
property), you can request permission to load an image from another domain for use in your call to drawImage()
. If the hosting domain permits cross-domain access to the image, the image can be used in your canvas without tainting it; otherwise using the image will taint the canvas.
다른 캔버스 엘리먼트 사용하기
Just as with normal images, we access other canvas elements using either the document.getElementsByTagName()
or document.getElementById()
method. Be sure you've drawn something to the source canvas before using it in your target canvas.
One of the more practical uses of this would be to use a second canvas element as a thumbnail view of the other larger canvas.
이미지를 직접 생성하기
또 다른 방법은, 스크립트를 통해 직접 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() {
// 여기서 drawImage 를 수행합니다.
}, false);
img.src = 'myImage.png'; // Set source path
만약 외부 이미지를 딱 하나 사용하는 경우에 위의 예제는 올바른 접근방법입니다. 하지만 그 갯수가 하나 이상이 될 경우에는 뭔가 다른 방법을 사용하는게 좀 더 깔금하게 처리할 수 있습니다. 하지만 그 방법은 이 글의 범위를 벗어나므로 image pre-loading 전략(tactics)에 대해서 직접 찾아 보시기 바랍니다.
data: 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 파일에 이미지를 내장시킴으로써 포터블 버전을 만드는것에 용이합니다.
하지만 단점도 존재합니다. 이렇게 생성된 이미지들은 캐싱 되지 않습니다. 또한 이렇게 Base64로 인코딩된 URL의 크기는 원본 이미지보다도 길어질 수 있습니다.
비디오의 프레임 사용하기
You can also use frames from a video being presented by a <video>
element (even if the video is not visible). For example, if you have a <video>
element with the ID "myvideo", you can do this:
function getMyVideo() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
return document.getElementById('myvideo');
}
}
This returns the HTMLVideoElement
object for the video, which, as covered earlier, is one of the objects that can be used as aCanvasImageSource
.
이미지 드로잉
Once we have a reference to our source image object we can use the drawImage()
method to render it to the canvas. As we will see later thedrawImage()
method is overloaded and has several variants. In its most basic form it looks like this:
drawImage(image, x, y)
- Draws the
CanvasImageSource
specified by theimage
parameter at the coordinates (x
,y
).
SVG 이미지들은 반드시 루트 <svg> 엘리먼트에 width와 height 값을 지정해주어야 합니다.
Example: A simple line graph
In the following example, we will use an external image as the backdrop for a small line graph. Using backdrops can make your script considerably smaller because we can avoid the need for code to generate the background. In this example, we're only using one image, so I use the image object's load
event handler to execute the drawing statements. The drawImage()
method places the backdrop at the coordinate (0, 0), which is the top-left corner of the canvas.
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';
}
결과물은 아래와 같이 출력됩니다:
Screenshot | Live sample |
---|---|
스케일링
The second variant of the drawImage()
method adds two new parameters and lets us place scaled images on the canvas.
drawImage(image, x, y, width, height)
- This adds the
width
andheight
parameters, which indicate the size to which to scale the image when drawing it onto the canvas.
Example: Tiling an image
In this example, we'll use an image as a wallpaper and repeat it several times on the canvas. This is done simply by looping and placing the scaled images at different positions. In the code below, the first for
loop iterates over the rows. The second for
loop iterates over the columns. The image is scaled to one third of its original size, which is 50x38 pixels.
Note: Images can become blurry when scaling up or grainy if they're scaled down too much. Scaling is probably best not done if you've got some text in it which needs to remain legible.
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';
}
The resulting canvas looks like this:
Screenshot | Live sample |
---|---|
슬라이싱
The third and last variant of the drawImage()
method has eight parameters in addition to the image source. It lets us cut out a section of the source image, then scale and draw it on our canvas.
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
- Given an
image
, this function takes the area of the source image specified by the rectangle whose top-left corner is (sx
,sy
) and whose width and height aresWidth
andsHeight
and draws it into the canvas, placing it on the canvas at (dx
,dy
) and scaling it to the size specified bydWidth
anddHeight
.
To really understand what this does, it may help to look at the image to the right. The first four parameters define the location and size of the slice on the source image. The last four parameters define the rectangle into which to draw the image on the destination canvas.
Slicing can be a useful tool when you want to make compositions. You could have all elements in a single image file and use this method to composite a complete drawing. For instance, if you want to make a chart you could have a PNG image containing all the necessary text in a single file and depending on your data could change the scale of your chart fairly easily. Another advantage is that you don't need to load every image individually, which can improve load performance.
Example: Framing an image
In this example, we'll use the same rhino as in the previous example, but we'll slice out its head and composite it into a picture frame. The picture frame image is a 24-bit PNG which includes a drop shadow. Because 24-bit PNG images include a full 8-bit alpha channel, unlike GIF and 8-bit PNG images, it can be placed onto any background without worrying about a matte color.
<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>
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);
}
We took a different approach to loading the images this time. Instead of loading them by creating new HTMLImageElement
objects, we included them as <img>
tags directly in our HTML source and retrieved the images from those. The images are hidden from output by setting the CSS property display
to none for those images.
Screenshot | Live sample |
---|---|
The script itself is very simple. Each <img>
is assigned an ID attribute, which makes them easy to select using document.getElementById()
. We then simply use drawImage()
to slice the rhino out of the first image and scale him onto the canvas, then draw the frame on top using a second drawImage()
call.
아트 갤러리 예제
In the final example of this chapter, we'll build a little art gallery. The gallery consists of a table containing several images. When the page is loaded, a <canvas>
element is inserted for each image and a frame is drawn around it.
In this case, every image has a fixed width and height, as does the frame that's drawn around them. You could enhance the script so that it uses the image's width and height to make the frame fit perfectly around it.
The code below should be self-explanatory. We loop through the document.images
container and add new canvas elements accordingly. Probably the only thing to note, for those not so familiar with the DOM, is the use of the Node.insertBefore
method. insertBefore()
is a method of the parent node (a table cell) of the element (the image) before which we want to insert our new node (the canvas element).
<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>
And here's some CSS to make things look nice:
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;
}
Tying it all together is the JavaScript to draw our framed images:
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);
}
}
}
이미지 스케일링 동작 지정하기
As mentioned previously, scaling images can result in fuzzy or blocky artifacts due to the scaling process. You can use the drawing context's imageSmoothingEnabled
property to control the use of image smoothing algorithms when scaling images within your context. By default, this is true
, meaning images will be smoothed when scaled. You can disable this feature like this:
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;