[TIL]24-12-17

김슬아·2024년 12월 17일

도형을 포문으로 여러개 그릴 때 beginPath() 가 없으면

이렇게 스트로크가 끊기지않고 이어그리기처럼 나오게 된다.

 context.save();
 context.translate(x, y);
 context.rotate(-angle);
//beginPath로 포문이 돌때마다 스트로크를 끊어준다.
context.beginPath();
context.arc(-w * 0.5, h / -2, radius * 0.2, 0, Math.PI * 2);
context.scale(1, 1);
context.stroke();
context.restore();

약 3시간의 뻘짓끝에 강의에서 배운 삼각법을 활용한
바이러스 세포같기도 한 모양을 만들어보았다.
처음엔 random 함수를 남발하며 무작위성으로 표현되는 도형들을 만들어보다가,
결국은 내가 제어할 수 있는 능력을 갖추는 게 우선이라고 생각이되서
random 함수를 사용하지 않고 내가 표현하고 싶은 그림을 그대로 만들어내는데에 집중했다.

그 과정에서 선에 그라데이션도 넣어보고,,
반복되는 비슷한 코드들을 함수로 모듈화하여 재사용성을 높이기도 하였다.

x = cx + r * Math.sin(angle);
y = cy + r * Math.cos(angle);
x2 = cx + r2 * Math.sin(angle);
y2 = cy + r2 * Math.cos(angle);
x3 = cx + (r / 5.5) * Math.sin(angle + 0.4);
y3 = cy + (r / 5.5) * Math.cos(angle + 0.4);
x4 = cx + (r / 1.03) * Math.sin(angle);
y4 = cy + (r / 1.03) * Math.cos(angle);

이건 모듈화 전 각 도형들의 위치 계산식이였다.
딱봐도 반복되는 코드가 많아 지저분해보인다.

function trigonometry(centerX, centerY, radius, angleOffset) {
  return {
    x: centerX + radius * Math.sin(angleOffset),
    y: centerY + radius * Math.cos(angleOffset),
  };
}
pos1 = trigonometry(cx, cy, r, angle);
pos2 = trigonometry(cx, cy, r2, angle);
pos3 = trigonometry(cx, cy, r / 5.5, angle + 0.4);
pos4 = trigonometry(cx, cy, r / 1.03, angle);

trigonometry 라는 함수로 계산식을 묶어 재사용하였더니 깔끔해졌다.
그리고 함수를 gpt 가 만들어줬는데, 이건 객체 방식이라고 한다.

객체 반환의 장점
1.여러 값을 명확하고 가독성 있게 반환할 수 있습니다.
2.구조 분해 할당으로 간편하게 값을 꺼낼 수 있습니다.
3.반환 값에 순서에 의존하지 않음으로 유연합니다.
4.필요 시 새로운 데이터를 쉽게 추가할 수 있습니다.
5.코드가 명확하고 유지보수하기 쉬워집니다.

음 , 이런 장점이 있군!

밑은 전체 구현 코드이다.

import canvasSketch from "canvas-sketch";
import * as canvasUtil from "canvas-sketch-util";

const { math, random } = canvasUtil;

const settings = {
  dimensions: [1024, 1024],
};
//이 두개에 대한 라이브러리가 이미 있다고 함=canvas-sketch-util

// const degToRad = (deg) => {
//   return deg * (Math.PI / 180);
// };

// const random = (min, max) => {
//   return Math.random() * (max - min) + min;
// };

const sketch = () => {
  return ({ context, width, height }) => {
    //sine 곡선 그리는 함수
    function drawSineWave(amplitude, frequency, offsetX, offsetY) {
      context.beginPath();
      context.lineWidth = 0.008;
      for (let y = 0; y <= height / 2; y++) {
        const x = amplitude * Math.sin(frequency * y) + offsetX;
        if (y === 0) {
          context.moveTo(x, y + offsetY);
        } else {
          context.lineTo(x, y + offsetY);
        }

        context.stroke();
      }
    }
    context.fillStyle = "white";
    context.fillRect(0, 0, width, height);

    //그라데이션
    const grad = context.createLinearGradient(0, 20, 0, 0);
    grad.addColorStop(0, "gray");
    grad.addColorStop(1, "black");

    const cx = width * 0.5;
    const cy = height * 0.5;
    const w = width * 0.01;
    const h = height * 0.1;
    let pos1;
    let pos2;
    let pos3;
    let pos4;
    context.fillStyle = grad;
    // context.fillStyle = "black";
    //리맵 전 상태 저장
    // context.save();

    function trigonometry(centerX, centerY, radius, angleOffset) {
      return {
        x: centerX + radius * Math.sin(angleOffset),
        y: centerY + radius * Math.cos(angleOffset),
      };
    }

    const num = 10;
    const radius = width * 0.2;
    const r = width * 0.3;
    const r2 = width * 0.49;
    for (let i = 0; i < num; i++) {
      const slice = math.degToRad(360 / num);
      const angle = slice * i;
      pos1 = trigonometry(cx, cy, r, angle);
      pos2 = trigonometry(cx, cy, r2, angle);
      pos3 = trigonometry(cx, cy, r / 5.5, angle + 0.4);
      pos4 = trigonometry(cx, cy, r / 1.03, angle);
      // save restore 로 rotate 기준 초기화

      // 이걸로 초기화 안해주면 roate 도 이전값에 영향을 받는듯함
      context.save();
      //회면 좌표를 넓이 높이 기준 가운데로 옮김. remap 과 같다고 생각하면 됨
      context.translate(pos2.x, pos2.y);
      context.rotate(-angle);
      //w,h 에 대한 스케일 값 변경
      context.scale(0.1, 4);
      context.beginPath();
      context.fillRect(-w * 0.5, h / -2, w, h);
      context.restore();

      context.save();
      context.translate(pos1.x, pos1.y);
      context.rotate(-angle);
      //beginPath로 포문이 돌때마다 스트로크를 끊어준다.
      context.beginPath();
      context.arc(-w * 0.5, h / -2, radius * 0.2, 0, Math.PI * 1);
      context.scale(1, 1);
      context.stroke();
      context.restore();

      context.save();
      //회면 좌표를 넓이 높이 기준 가운데로 옮김. remap 과 같다고 생각하면 됨
      context.translate(pos3.x, pos3.y);
      context.rotate(-angle);
      //w,h 에 대한 스케일 값 변경
      context.scale(1, 1);
      context.beginPath();
      context.arc(0, 0, radius, 0.8, Math.PI * 0.5);
      context.stroke();
      context.restore();

      const amplitude = 30; // 진폭
      const frequency = 0.05; // 주파수
      context.save();
      context.translate(pos4.x, pos4.y);
      context.rotate(-angle);
      drawSineWave(amplitude, frequency, 0, 0);
      context.restore();
    }

    // //remap 하기 전으로 복구
    // context.restore();
  };
};

canvasSketch(sketch, settings);
profile
개발자/디자이너 둘다 잘하고싶은 코린이

0개의 댓글