본문 바로가기

Frontend/HTML

Canvas - Dynamic Sizing

반응형

While the width and height of a canvas element are relevant to the pixel, the width and height of the CSS are relevant to the size of the element that the size is applied to. For this reason, it is not a good practice to set the size of a canvas element with CSS as we would do with normal elements, if not possible, and it is better to rely on the element itself or JavaScript. With that being said we will see how we can implement the dynamic sizing for a canvas and other tips regarding sizing of a canvas in this posting

List of Contents

Dynamic Sizing

As I was making a drawing application with Canvas, I decided to add a dynamic sizing function

<!-- index.html -->

<body>
  <canvas></canvas>
</body>

At first, I thought that I could just add the relative unit in the CSS and wrote the below code

/* main.css */

body {
  display: flex;  
}

canvas {
  height: 95vh;
  width: 95%;
  border: 4px solid #000;
  margin: auto;
  display: block;
}

.canvas-css {
  height: 300px;
  width: 300px;
}

Then, I gave the application a test and I realized that the more I draw away from the initial point the bigger the object gets breaking the image as shown below.

To solve this, I used JavaScript to dynamically set the size of the canvas by resizing the canvas every time the screen size changed using the window's inner height and inner width.

/* 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
})

Then, I gave another test, and as you can see the problem was solved.


※ Adjusting the Focus on Canvas

Additionally, let's see how we can adjust the focus of the object on a canvas.

 

I have created an example code as shown below for a demonstration.

<!-- 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
})

Initially, I used the coordinate of the canvas to set the drawing point. But the problem with this approach is that the starting point of the drawing object should be the top-left side of the element not the screen and the client X and Y are relative to the top-left side of the screen.

So I updated the code

// 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
})

The difference is to deduct the gap between the starting point of the element and the starting point of the screen so that the drawing object can start at the 0 positions on the canvas


In this writing, we have seen how we can set the size of a canvas dynamically.


References

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
반응형