밝을희 클태

<Canvas> 캐릭터 그리기 도라에몽 본문

사이드 프로젝트/도라에몽 잡기 [React]

<Canvas> 캐릭터 그리기 도라에몽

huipark 2024. 11. 6. 10:49

미니맵에 도라에몽의 위치를 표시할 때 PNG를 사용해 scale을 줄이니 너무 작아져 화질이 깨지는 문제가 생겨서 직접 그리기로 했다.

힘들다..

useEffect(() => {
		const canvas = canvasRef.current;
		const ctx = canvas.getContext("2d");

		const drawCanvas = () => {
			ctx.clearRect(0, 0, canvas.width, canvas.height);

			const PosX = posRef.x / 7;
			const PosY = posRef.y / 7;
			const size = doraemonSizeInMiniMapRef.current;

			// 얼굴
			ctx.beginPath();
			ctx.ellipse(size + PosX, size + PosY, size, size * 0.93, 0, 0, Math.PI * 2);
			ctx.fillStyle = "#60BBD5";
			ctx.fill();

			// 흰색 수염
			ctx.beginPath();
			ctx.ellipse(size + PosX, size + size * 0.2 + PosY, size * 0.9, size * 0.7, 0, 0, Math.PI * 2);
			ctx.fillStyle = "white";
			ctx.fill();

			/*
			~ 1 가운데 수염
			~ 3 윗 수염
			~ 아랫 수염
			*/
			// ctx.lineWidth = size * 0.01;
			ctx.lineWidth = size * 0.01;
			const beards = [
				[size * 0.6 + PosX, size * 1.2 + PosY, size + PosX - size, size * 1.2 + PosY],
				[size * 1.4 + PosX, size * 1.2 + PosY, size + PosX + size, size * 1.2 + PosY],
				[size * 0.6 + PosX, size * 1.1 + PosY, size + PosX - size * 0.95, size + PosY],
				[size * 1.4 + PosX, size * 1.1 + PosY, size + PosX + size * 0.95, size + PosY],
				[size * 0.6 + PosX, size * 1.3 + PosY, size + PosX - size * 0.95, size * 1.4 + PosY],
				[size * 1.4 + PosX, size * 1.3 + PosY, size + PosX + size * 0.95, size * 1.4 + PosY],
			];

			for (const beard of beards) {
				ctx.beginPath();
				ctx.moveTo(beard[0], beard[1]);
				ctx.lineTo(beard[2], beard[3]);
				ctx.stroke();
			}

			// 왼쪽 눈 -> 눈동자 -> 반사광 -> 오른쪽 ... 순
			const eyes = [
				[size * 0.75 + PosX, size * 0.5 + PosY, size / 4, size / 3],
				[size * 0.75 + size / 8 + PosX, size * 0.5 + PosY, size / 10, size / 8],
				[size * 0.75 + size / 8 + PosX, size * 0.5 + PosY, size / 20, size / 18, 0, 0, Math.PI * 2],
				[size * 0.75 + size / 2 + PosX, size * 0.5 + PosY, size / 4, size / 3],
				[
					size * 0.75 + size / 2.5 + PosX,
					size * 0.5 + PosY,
					size / 10,
					size / 8,
					0,
					0,
					Math.PI * 2,
				],
				[size * 0.75 + size / 2.5 + PosX, size * 0.5 + PosY, size / 20, size / 18],
			];

			eyes.forEach((eye, idx) => {
				ctx.beginPath();
				ctx.ellipse(eye[0], eye[1], eye[2], eye[3], 0, 0, Math.PI * 2);
				ctx.stroke();
				ctx.fillStyle = idx === 1 || idx === 4 ? "black" : "white";
				ctx.fill();
			});

			// 코
			ctx.beginPath();
			ctx.arc(size + PosX, size * 0.95 + PosY, size * 0.2, 0, Math.PI * 2);
			ctx.fillStyle = "red";
			ctx.fill();

			// 코 (반사광)
			ctx.beginPath();
			ctx.arc(size * 1.1 + PosX, size * 0.9 + PosY, size * 0.07, 0, Math.PI * 2);
			ctx.fillStyle = "white";
			ctx.fill();
		};

		const init = () => {
			drawCanvas();
			requestAnimationFrame(init);
		};

		requestAnimationFrame(init);
	}, []);

결과

실제 비율

실제 비율