본문 바로가기
DEV/JS30

[JS30] day 8 - Fun with HTML5 Canvas

by newjp 2021. 12. 12.

Canvas를 사용하기 위해서는 html에서

<canvas id="draw" width="800" height="800"></canvas> 를 선언해 준 후

JS에서 기본 세팅을 시작한다

JS 베이스 세팅

// 캔버스 대지를 가져오고, 앞으로 context는 2d임을 canvas에 명시
  const canvas = document.querySelector('#draw');
  const ctx = canvas.getContext('2d');

  // 기본 캔버스 사이즈를 window 크기 전체로 늘려 줌
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;

// Base-setting
  ctx.strokeStyle = '#BADA55';
  ctx.lineJoin = 'round';
  ctx.lineCap = 'round';
  ctx.lineWidth = 100;

canvas에 대한 접근은 canvas.getContext 로 접근이 가능하고 canvas는 2d와 3d를 제공한다. 매번 getContext로 접근하기 번거로우므로 ctx(canvas context)라는 변수에 할당하고 접근 해줌

context는 다양한 속성이 있는데, 이 강의에서는 아래의 요소들을 사용 👇

  • strokeStyle (선 색, RGB, hexcode, RGBA, 색상 이름)
  • lineJoin (선과 선을 이을 때 마무리 스타일을 지정, round, butt, squere)
  • lineCap (선 끝의 마무리 스타일을 지정 round , butt, squere)

선 그리기

ctx.beginPath(); // 선을 그리기 위해 호출
ctx.moveTo(lastX, lastY); // 선의 시작 점
ctx.lineTo(e.offsetX, e.offsetY); // 선의 마지막 지점
ctx.stroke(); // 그리기(호출 필수)
  • e.offset 은 마우스의 위치를 반환한다. event를 mousemove에 걸어주었기 때문에 mousemove event의 offset 속성 즉 현재 마우스의 위치를 계속 가지고 있음
let direction = true;

function draw(e) {
	if (!isDrawing) return; // 마우스 클릭 여부에 따라 그릴지 말지
	ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
	
	ctx.beginPath();
	ctx.moveTo(lastX, lastY);
	ctx.lineTo(e.offsetX, e.offsetY);
	ctx.stroke();
	
	[lastX, lastY] = [e.offsetX, e.offsetY];
	// 끝점을 시작점으로 바꿔줌
	
	hue++;
	if (hue >= 360) {
	  hue = 0;
	}
	if (ctx.lineWidth >= 100 || ctx.lineWidth <= 1) {
	  direction = !direction;
	}
	
	if (direction) {
	  ctx.lineWidth++;
	} else {
	  ctx.lineWidth--;
	}
}
  • hue 선에 다양한 스타일을 넣어주기 위해 hue값을 지정하고 hue++로 계속 올라가게 해 놓음(무지개 색 선이 나옴) 360의 값이 최대이므로 최대 값을 넘으면 다시 0부터 시작하게 함 (설정하지 않아도 hue값은 계속 루프되지만 lineWidth와 연동할 것 이기 때문)
  • lineWidth lineWidth를 hue값과 연동하여 선의 굵기도 조절되게 함 hue가 계속 늘어나면 선의 두께가 무한정 두꺼워지므로 2개의 if문으로 100이상 늘어나면(if 1) 다시 서서히 줄어들도록 함(if 2)

선 그리기 & 캔버스 초기화

// 오른쪽 마우스 클릭 시 나오는 메뉴 막기
canvas.addEventListener('contextmenu', (e) => {e.preventDefault(); return false});

// 마우스를 클릭하면 drawing을 시작하게 하는 함수에
// 왼, 오른 마우스를 구분하는 if를 넣어줌
canvas.addEventListener('mousedown', (e) => {
  if (e.which === 1) {
    isDrawing = true;
    [lastX, lastY] = [e.offsetX, e.offsetY];
  } else if (e.which === 3) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  }
});

canvas.addEventListener('mouseup', () => isDrawing = false);
canvas.addEventListener('mouseout', () => isDrawing = false);
  • which는 마우스의 왼쪽 오른쪽에 해당하는 값을 반환해준다. 마우스 왼쪽은 1, 스크롤은 2, 마우스 오른쪽은 3의 값을 가짐
  • 그려지는 순서
    마우스 왼쪽 클릭 → mousedown함수 실행 → isDrawing 을 true로 바꿔 mousemove event의 draw함수를 실행 → 마우스를 떼거나(mouseup), 마우스가 화면 밖으로 나가면(mouseout) isDrawing이 false가 되어 draw함수에서 선 그리기를 멈춤
<body>
<canvas id="draw" width="800" height="800"></canvas>
<script>
  // 캔버스 대지를 가져오고, 앞으로 context는 2d임을 canvas에 명시
  const canvas = document.querySelector('#draw');
  const ctx = canvas.getContext('2d');
  // 기본 캔버스 사이즈를 window 크기 전체로 늘려 줌
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;

  // Base-setting
  ctx.strokeStyle = '#BADA55';
  ctx.lineJoin = 'round';
  ctx.lineCap = 'round';
  ctx.lineWidth = 100;

  // drawing flag
  let isDrawing = false;

  // line 시작과 끝을 지정해 줄 변수
  let lastX = 0;
  let lastY = 0;
  let hue = 0;
  let direction = true;

  function draw(e) {
    if (!isDrawing) return; // isDraw가 true가 아닐때는 실행X
    console.log(e);
    ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
    // ctx.lineWidth = hue;
    ctx.beginPath(); // 시작
    ctx.moveTo(lastX, lastY); // 끝
    ctx.lineTo(e.offsetX, e.offsetY);
    ctx.stroke(); // 그리기
    [lastX, lastY] = [e.offsetX, e.offsetY]; // 끝점을 시작점으로 바꿔줌

    hue++;
    if (hue >= 360) {
      hue = 0;
    }
    if (ctx.lineWidth >= 100 || ctx.lineWidth <= 1) {
      direction = !direction;
    }

    if (direction) {
      ctx.lineWidth++;
    } else {
      ctx.lineWidth--;
    }
  }
  // 마우스를 움직이는 것을 감지
  canvas.addEventListener('mousemove', draw);
  canvas.addEventListener('mousedown', (e) => {
    if (e.which === 1) {
      isDrawing = true;
      [lastX, lastY] = [e.offsetX, e.offsetY];
    } else if (e.which === 3) {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
    }
  });
  canvas.addEventListener('mouseup', () => isDrawing = false);
  canvas.addEventListener('mouseout', () => isDrawing = false);
  canvas.addEventListener('contextmenu', (e) => {e.preventDefault(); return false});
  
</script>

<style>
  html, body {
    margin: 0;
  }
</style>

</body>