캔버스의 너비, 높이 속성은 pixel을 기준으로 하는 반면 CSS 너비, 높이 속성은 스타일이 적용되는 박스의 크기와 비례해서 적용됩니다. 따라서, 캔버스를 활용한 애플리케이션 개발 시 크기지정은 다른 요소들처럼 CSS를 사용할 수 없고 자바스크립트나 HTML을 활용하여 지정하여야 합니다. 오늘은 화면의 크기에 따라 캔버스의 크기를 달리하는 동적크기를 지정하는법을 살펴 보겠습니다.
목차
동적사이즈 지정하기
캔버스를 활용하여 그림판 애플리케이션을 만들 다 화면의 크기에 따라 동적으로 캔버스 크기를 조정하는 기능을 추가하기로 했습니다.
<!-- index.html -->
<body>
<canvas></canvas>
</body>
그냥 CSS를 활용하여 상대 단위를 적용하면 쉽게 구현될 줄 알고 아래와 같이 코드를 작성했는데요.
/* main.css */
body {
display: flex;
}
canvas {
height: 95vh;
width: 95%;
border: 4px solid #000;
margin: auto;
display: block;
}
.canvas-css {
height: 300px;
width: 300px;
}
근데, 캔버스의 크기를 CSS에서 지정하면 캔버스에 이미지를 그렸을 때 기준이 되는 좌측 상단에서 멀어질수록 이미지도 비례해서 커지는 에러가 발생하는 걸 발견했습니다.
그래서 아래와 같이 자바스크립트로 캔버스의 크기를 지정하고 화면의 크기가 변할 때마다 다시 지정하는 방식을 적용해 보았더니
/* index.js */
const canvas = document.querySelector('canvas')
const cv = canvas.getContext('2d')
canvas.height = window.innerHeight - 50
canvas.width = window.innerWidth - 50
window.addEventListener('resize', () => {
canvas.height = window.innerHeight - 50
canvas.width = window.innerWidth - 50
})
보이는 것처럼 이미지가 왜곡되는 에러가 사라지더군요.
※ 캔버스와 캔버스안에 선 초점 맞추기
추가로, 그림판 애플리케이션에서 캔버스와 그려지는 선의 초점을 맞추는 방법도 살펴보겠습니다.
보기를 위해 그림판 애플리케이션을 아래와 같이 작성
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- custom style -->
<link rel="stylesheet" href="main.css" />
<script defer type="module" src="index.js"></script>
</head>
<body>
<canvas height="500" width="500"></canvas>
<div class="tool-box">
<button class="btn-increase">+</button>
<button class="btn-decrease">-</button>
<p class="size">5</p>
<input type="color" class="color">
<button class="btn-delete">🗑️</button>
</div>
</body>
</html>
/* main.css */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
canvas {
border: solid 2px #000;
}
.tool-box {
width: 503.2px;
display: flex;
align-items: center;
justify-content: space-between;
border-left: solid 2px #000;
border-right: solid 2px #000;
border-bottom: solid 2px #000;
}
.tool-box p {
margin: 0;
flex-grow: 1;
text-align: center;
}
.tool-box button {
flex-grow: 1;
border: none;
}
.tool-box input {
flex-grow: 1;
border: none;
}
// index.js
const colorEl = document.querySelector('.color')
const canvasEl = document.querySelector('canvas')
const cv = canvasEl.getContext('2d')
const btnIncrease = document.querySelector('.btn-increase')
const btnDecrease = document.querySelector('.btn-decrease')
const btnDelete = document.querySelector('.btn-delete')
const sizeEl = document.querySelector('.size')
let color = ''
let x1
let y1
let isPressed = false
let size = 5
colorEl.addEventListener('change', (e) => {
color = e.target.value
})
btnIncrease.addEventListener('click', () => {
size += 5
if (size > 40) {
size = 40
}
sizeEl.innerText = `${size}`
})
btnDecrease.addEventListener('click', () => {
size -= 5
if (size < 5) {
size = 5
}
sizeEl.innerText = `${size}`
})
btnDelete.addEventListener('click', () => {
cv.clearRect(0, 0, canvasEl.width, canvasEl.height)
})
canvasEl.addEventListener('mousedown', (e) => {
isPressed = true
x1 = e.clientX
y1 = e.clientY
})
canvasEl.addEventListener('mousemove', (e) => {
if (isPressed) {
let x2 = e.clientX
let y2 = e.clientY
cv.beginPath()
cv.strokeStyle = color
cv.lineWidth = size + 5
cv.moveTo(x1, y1)
cv.lineTo(x2, y2)
cv.stroke()
cv.beginPath()
cv.arc(x2, y2, size, 0, Math.PI * 2);
cv.fillStyle = color
cv.fill();
x1 = x2
y1 = y2
}
})
canvasEl.addEventListener('mouseup', (e) => {
isPressed = false
})
선의 좌표는 아래 표시된 부분에서 지정되는 데, 문제점은 캔버스요소의 위치는 화면의 좌상단 기준이므로 캔버스 내에 그려지는 선의 좌표로 활용할 수 없습니다.
수정된 코드는 아래와 같은 데
// index.js
const colorEl = document.querySelector('.color')
const canvasEl = document.querySelector('canvas')
const cv = canvasEl.getContext('2d')
const btnIncrease = document.querySelector('.btn-increase')
const btnDecrease = document.querySelector('.btn-decrease')
const btnDelete = document.querySelector('.btn-delete')
const sizeEl = document.querySelector('.size')
let color = ''
let x1
let y1
let isPressed = false
let size = 5
colorEl.addEventListener('change', (e) => {
color = e.target.value
})
btnIncrease.addEventListener('click', () => {
size += 5
if (size > 40) {
size = 40
}
sizeEl.innerText = `${size}`
})
btnDecrease.addEventListener('click', () => {
size -= 5
if (size < 5) {
size = 5
}
sizeEl.innerText = `${size}`
})
btnDelete.addEventListener('click', () => {
cv.clearRect(0, 0, canvasEl.width, canvasEl.height)
})
canvasEl.addEventListener('mousedown', (e) => {
isPressed = true
x1 = e.clientX - e.target.getBoundingClientRect().x
y1 = e.clientY - e.target.getBoundingClientRect().y
})
canvasEl.addEventListener('mousemove', (e) => {
if (isPressed) {
let x2 = e.clientX - e.target.getBoundingClientRect().x
let y2 = e.clientY - e.target.getBoundingClientRect().y
cv.beginPath()
cv.strokeStyle = color
cv.lineWidth = size + 5
cv.moveTo(x1, y1)
cv.lineTo(x2, y2)
cv.stroke()
cv.beginPath()
cv.arc(x2, y2, size, 0, Math.PI * 2);
cv.fillStyle = color
cv.fill();
x1 = x2
y1 = y2
}
})
canvasEl.addEventListener('mouseup', (e) => {
isPressed = false
})
추가된 부분은 전체화면에서 요소가 시작하는 위치를 감하여 그려지는 선의 위치가 0에서 시작하도록 설정
이상으로 캔버스의 크기를 동적으로 지정하는 방법을 보았습니다.
참고
https://stackoverflow.com/questions/1664785/resize-html5-canvas-to-fit-window
https://stackoverflow.com/questions/26670810/why-we-cant-set-width-and-height-for-canvas-using-css
'프론트엔드 > HTML' 카테고리의 다른 글
캔버스 (0) | 2023.01.05 |
---|---|
전역속성 (global attribute) (0) | 2022.12.28 |
contenteditable (0) | 2022.12.28 |