도형을 포문으로 여러개 그릴 때 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);