본문 바로가기

프론트엔드/HTML

캔버스 동적 크기 지정

반응형

캔버스의 너비, 높이 속성은 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

 

Resize HTML5 canvas to fit window

How can I automatically scale the HTML5 <canvas> element to fit the page? For example, I can get a <div> to scale by setting the height and width properties to 100%, but a <canvas&g...

stackoverflow.com

https://stackoverflow.com/questions/26670810/why-we-cant-set-width-and-height-for-canvas-using-css

 

why we can't set width and height for canvas using css

Why the canvas appears as scaled one, when the height and width is specified in css instead of DOM? Html: <canvas></canvas> Css: canvas{ width:100px; height:100px...

stackoverflow.com

 

728x90
반응형

'프론트엔드 > HTML' 카테고리의 다른 글

캔버스  (0) 2023.01.05
전역속성 (global attribute)  (0) 2022.12.28
contenteditable  (0) 2022.12.28