Canvas API란?

Canvas API는 Javascript와 HTML을 이용해서 그래픽을 처리를 할 수 있게 도와줍니다. 이 API를 이용해서 애니메이션, 그림, 비디오등을 처리 할 수 있고, 이번에 소개해 드릴 2D 및 3D 그래픽을 그립니다.

Canvas API 기본적인 예시

Canvas API를 이용한 간단한 예시를 보여드리겠습니다.

<canvas id="scene"></canvas>
//html id가 scene인 값을 참조 합니다.
const canvas = document.getElementById("scene");

//렌더링이 될 수 있는 대상을 얻는 메소드를 얻습니다.
//HTMLCanvasElement.getContext() => 캔버스의 드로잉 컨텍스트를 반환 합니다. 지원이 되지 않는 다면
//null를 반환
const ctx = canvas.getContext("2d");

// 캔버스 전체를 green으로 채우기
ctx.fillStyle = "green";

//처음 부터 x,y, width, height을 주는 메소드 이므로
// 상단 좌즉 10, 10인 곳에 배치하고 높이와 넓이를 150으로 지정
ctx.fillRect(10, 10, 150, 100);

이렇게 하면 저희는 아래의 사진처럼 상단 좌측의 초록색의 150, 100의 contents를 얻을 수 있습니다. img_alt

3D 그래픽 만들기

<canvas id="scene"></canvas>
console.clear();

// Get the canvas element from the DOM
const canvas = document.querySelector("#scene");
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
// Store the 2D context
const ctx = canvas.getContext("2d");

if (window.devicePixelRatio > 1) {
  canvas.width = canvas.clientWidth * 2;
  canvas.height = canvas.clientHeight * 2;
  ctx.scale(2, 2);
}

/* ====================== */
/* ====== VARIABLES ===== */
/* ====================== */
let width = canvas.clientWidth; // Width of the canvas
let height = canvas.clientHeight; // Height of the canvas
let rotation = 0; // Rotation of the globe
let dots = []; // Every dots in an array

/* ====================== */
/* ====== CONSTANTS ===== */
/* ====================== */
/* Some of those constants may change if the user resizes their screen but I still strongly believe they belong to the Constants part of the variables */
const DOTS_AMOUNT = 1000; // Amount of dots on the screen
const DOT_RADIUS = 4; // Radius of the dots
let GLOBE_RADIUS = width * 0.7; // Radius of the globe
let GLOBE_CENTER_Z = -GLOBE_RADIUS; // Z value of the globe center
let PROJECTION_CENTER_X = width / 2; // X center of the canvas HTML
let PROJECTION_CENTER_Y = height / 2; // Y center of the canvas HTML
let FIELD_OF_VIEW = width * 0.8;

//canvas 스크린에 보이는 점들을 표시해 주는 함수
// y좌표 x좌표 z좌표 설정 후 에 sin값과 cos값을 이용해서 Dot위치를 설정해 준다.
class Dot {
  constructor(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;

    this.xProject = 0;
    this.yProject = 0;
    this.sizeProjection = 0;
  }
  // Do some math to project the 3D position into the 2D canvas
  project(sin, cos) {
    const rotX = cos * this.x + sin * (this.z - GLOBE_CENTER_Z);
    const rotZ =
      -sin * this.x + cos * (this.z - GLOBE_CENTER_Z) + GLOBE_CENTER_Z;
    this.sizeProjection = FIELD_OF_VIEW / (FIELD_OF_VIEW - rotZ);
    this.xProject = rotX * this.sizeProjection + PROJECTION_CENTER_X;
    this.yProject = this.y * this.sizeProjection + PROJECTION_CENTER_Y;
  }
  // Draw the dot on the canvas
  draw(sin, cos) {
    this.project(sin, cos);
    // ctx.fillRect(this.xProject - DOT_RADIUS, this.yProject - DOT_RADIUS, DOT_RADIUS * 2 * this.sizeProjection, DOT_RADIUS * 2 * this.sizeProjection);
    ctx.beginPath();
    ctx.arc(
      this.xProject,
      this.yProject,
      DOT_RADIUS * this.sizeProjection,
      0,
      Math.PI * 2
    );
    ctx.closePath();
    ctx.fill();
  }
}

//Dot 생성 후 Array로 저장한다
// 이부분의 저장이 실제 구 에서 보여지는 값
function createDots() {
  // Empty the array of dots
  dots.length = 0;

  // Create a new dot based on the amount needed
  for (let i = 0; i < DOTS_AMOUNT; i++) {
    const theta = Math.random() * 2 * Math.PI; // Random value between [0, 2PI]
    const phi = Math.acos(Math.random() * 2 - 1); // Random value between [-1, 1]

    // Calculate the [x, y, z] coordinates of the dot along the globe
    const x = GLOBE_RADIUS * Math.sin(phi) * Math.cos(theta);
    const y = GLOBE_RADIUS * Math.sin(phi) * Math.sin(theta);
    const z = GLOBE_RADIUS * Math.cos(phi) + GLOBE_CENTER_Z;
    dots.push(new Dot(x, y, z));
  }
}

/* ====================== */
/* ======== RENDER ====== */
/* ====================== */
function render(a) {
  // Clear the scene
  ctx.clearRect(0, 0, width, height);

  // Increase the globe rotation
  rotation = a * 0.0004;

  const sineRotation = Math.sin(rotation); // Sine of the rotation
  const cosineRotation = Math.cos(rotation); // Cosine of the rotation

  //Dot 찍기 시작
  // Loop through the dots array and draw every dot
  for (var i = 0; i < dots.length; i++) {
    dots[i].draw(sineRotation, cosineRotation);
  }

  window.requestAnimationFrame(render);
}

// Function called after the user resized its screen
function afterResize() {
  width = canvas.offsetWidth;
  height = canvas.offsetHeight;
  if (window.devicePixelRatio > 1) {
    canvas.width = canvas.clientWidth * 2;
    canvas.height = canvas.clientHeight * 2;
    ctx.scale(2, 2);
  } else {
    canvas.width = width;
    canvas.height = height;
  }
  GLOBE_RADIUS = width * 0.7;
  GLOBE_CENTER_Z = -GLOBE_RADIUS;
  PROJECTION_CENTER_X = width / 2;
  PROJECTION_CENTER_Y = height / 2;
  FIELD_OF_VIEW = width * 0.8;

  createDots(); // Reset all dots
}

// Variable used to store a timeout when user resized its screen
let resizeTimeout;
// Function called right after user resized its screen
function onResize() {
  // Clear the timeout variable
  resizeTimeout = window.clearTimeout(resizeTimeout);
  // Store a new timeout to avoid calling afterResize for every resize event
  //   resizeTimeout = window.setTimeout(afterResize, 500);
}
window.addEventListener("resize", onResize);

// Populate the dots array with random dots
createDots();

// Render the scene
// canvas에 렌더 되는 함수 (중요)
window.requestAnimationFrame(render);

위에 처럼 코드르 짠 후 렌더된 html을 보게 되면 아래 사진처럼 보이게 될 것이다(실제로는 회전함…) img_alt

주의

좌표값을 구할 때 일반적인 평면이 아닌 입체여서 좌표값이 3개로 나와해서 힘들 수 있다.

참조

https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer https://wsss.tistory.com/25 https://www.basedesign.com/blog/how-to-render-3d-in-2d-canvas

tkendi's profile image

tkendi

2021-06-03 18:00

Read more posts by this author