Canvas 심화, 선을 잇고 정답 구분하기

So Vidi·2024년 11월 14일

JavaScript

목록 보기
31/31

본격적으로 Canvas 를 통한 교육자료를 개발하기 시작하면서, Canvas 와 그에따른 핵심 기능들을 비틀어 가며 개발하고, 알게되는게 많아져 정리하게 되었다.

기본적으로 동적인 효과는 모두 animating 함수 안으로 들어가 줘야 한다 일례로 이벤트리스너로 마우스의 움직임을 감지하여 라인을 그리는 함수의 잘못된 버전과 맞는 버전을 설명,

틀린 버전

function Canvas() {
  this.x = tCanvas.width;
  this.y = tCanvas.height;
  this.w = tCanvas.width;
  this.h = tCanvas.width;
  this.pos = {};
  this.c = `rgb(${ranNum(255)}, ${ranNum(255)}, ${ranNum(255)})`
  this.dx = ranNum(100);

 
  tCanvas.addEventListener("touchmove", this.lining.bind(this));
}

Canvas.prototype.lining = function(e) {
  ctx.lineWidth = "20px";
  ctx.strokeStyle = this.c;
  ctx.moveTo(0, 0);
  ctx.lineTo(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
  ctx.stroke();
}

new Canvas();

위는 이벤트리스닝이 일어남에 따라 선을 그리는 애니메이팅이 일어난다. 정적인 타입이고 애니메이팅이 아니지만 보이기에 애니메이션처럼 보이므로 많이 착각하게 되는 요소.

맞는 버전

const liner = [];
const aniDraw = () => {
  ctx.clearRect(0, 0, tCanvas.width, tCanvas.height);

  liner.forEach((item, key) => {
    item.lining();
  });

  requestAnimationFrame(aniDraw);
};
aniDraw();

function Canvas() {
  this.x = tCanvas.width;
  this.y = tCanvas.height;
  this.w = tCanvas.width;
  this.h = tCanvas.width;
  this.pos = {};
  this.c = `rgb(${ranNum(255)}, ${ranNum(255)}, ${ranNum(255)})`
  this.dx = ranNum(100);

 
  tCanvas.addEventListener("touchmove", this.moving.bind(this));
}

Canvas.prototype.lining = function(e) {
  ctx.lineWidth = "20px";
  ctx.strokeStyle = this.c;
  ctx.moveTo(0, 0);
  ctx.lineTo(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
  ctx.stroke();
}

Canvas.prototype.moving = function(e) {
  this.pos.x = e.targetTouches[0].clientX;
  this.pos.y = e.targetTouches[0].clientY;
}

liner.push(new Canvas());

맞는 코드는 이런식으로 선을 긋는 자체가 선이 있던 없던 일단 애니메이팅 함수에서 선언으로 작동되어져야 한다.

Canvas 자체를 생성자 함수로 지정하고 선언하여 그 안에 일어나는 마우스와 터치 이벤트 리스너를 통해 선을 긋고, 화면상에 위치한 X, Y 값을 계산하여 마우스의 위치와, 화면 뒤에 있는 HTML 요소의 X, Y 위치를 감지한다.

특정 이벤트의 특정좌표 추적 및 근처실행 감지

const liner = [];
const aniDraw = () => {
  ctx.clearRect(0, 0, tCanvas.width, tCanvas.height);

  liner.forEach((item, key) => {
    item.draw();
  });

  requestAnimationFrame(aniDraw);
};
aniDraw();

// Canvas 라는 생성자함수가 있고 마우스위치를 실시간으로 pos 라는 변수로 적용하고 있다고 가정,
function Canvas() {
  this.x = tCanvas.width;
  this.y = tCanvas.height;
  this.w = tCanvas.width;
  this.h = tCanvas.width;
  this.pos = {};
  this.c = `rgb(${ranNum(255)}, ${ranNum(255)}, ${ranNum(255)})`
  this.dx = ranNum(100);

  
  Canvas.addEventlistener("mousedown", this.mDown.bind(this));
  Canvas.addEventlistener("touchstart", this.tStart.bind(this));
  Canvas.addEventlistener("mousedown", this.dotBoundaryCheck.bind(this));
}

Canvas.prototype.tStart= function(e) {
  this.pos.x = e.offsetX;
  this.pos.y = e.offsetY;
}

Canvas.prototype.mDown = function(e) {
  this.pos.x = e.targetTouches[0].clientX;
  this.pos.y = e.targetTouches[0].clientY;
}

// Canvas 에 이벤트리스너 등록 및 바인드,

// 혹은 마우스 다운 및 터치시작 프로토타입 함수안에 걸려있는경우 별도설정 불필요

// 점의 영역을 클릭했는지 체크
Canvas.prototype.dotBoundaryCheck = function(type) {
  if (!questionDots) {
    return;
  }
  // res 를 boolean 으로 사용하여 다른 이벤트에 if 조건문으로 달아놓으면 아래의 좌표추적에서 true 값이 나왔을때에만 작동됨
  let res = false;
  // 이건 나중에 감지할 범위의 크기가 된다.
  const dotRadius = 80;
  let searchDots = null;
  if (type === "question") {
    searchDots = questionDots;
  } else {
    searchDots = answerDots;
  }
  searchDots.each((idx, qsDot) => {
	  // 해당 이벤트를 감지할 객체의 좌표위치 계산, 해당 위치는 어차피 Canvas 에 의해 가려져 Canvas 에 좌표클릭이 되겠지만 객체의 x ,y좌표는 추적되므로 감지가 된다.
    const qsDotRect = qsDot.getBoundingClientRect();
    let zoom = $("#wrap").css("zoom");
    // 객체의 가운데위치 계산, 혹시 zoom 을 사용했다면 zoom 값 곱하기 필요.
    // 수평좌표 + 객체길이 / 2, 수직좌표 + 객체높이 / 2 를 통해 해당 좌표에서의 중앙위치 계산
    const qsDotCenterX = (qsDotRect.x + qsDotRect.width / 2) * zoom;
    const qsDotCenterY = (qsDotRect.y + qsDotRect.height / 2) * zoom;
    // pos 값은 마우스 클릭 이벤트 일어나면 canvas 의 pos 값 갱신되도록 prototype 함수 지정해놔야 함
    const isWithinX = this.isWithinRange(this.pos.x, qsDotCenterX, dotRadius);
    const isWithinY = this.isWithinRange(this.pos.y, qsDotCenterY, dotRadius);
    if (isWithinX && isWithinY) {
    // 본격적으로 이곳에서 정지좌표 qsDotRect 안에 특정 이벤트가 일어난 pos 가 일정거리 이상 일치할시 작동할 기능들이 됨
    // pos 는 canvas 생성자 함수 this.pos 라는 값으로 계속 실시간 업데이트 해야됨
      res = qsDot;
    }
  });
  return res;
};

// 영역안에 있는지 확인 true, false
// 매개변수 순서 각각 마우스좌표, 객체좌표, 범위
Canvas.prototype.isWithinRange = function(coord, center, radius) {
  return coord >= center - radius && coord <= center + radius;
};

위 설명이 모두 들어간 가장 기초적인 선 긋기 함수 형태이며, 자세한 형태는 훨씬 복잡하게 로직이 만들어져 있다. 그건 비밀이지롱

profile
먹을거 좋아하는데 마른 개발자

0개의 댓글