一个 canvas 画图面试题

44 阅读2分钟

在画布上面画实心圆,然后沿8个方向移动,每个圆颜色随机,要做边界检测。如下:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta
			name="viewport"
			content="width=device-width, initial-scale=1.0"
		/>
		<title>canvas</title>
		<style>
			#canvas {
				border: 1px solid #f00;
				background: #eee;
			}
		</style>
	</head>
	<body>
		<canvas id="canvas"></canvas>

		<button id="btn">run</button>

		<script>
			const canvas = document.getElementById("canvas");
			const ctx = canvas.getContext("2d");
			const btn = document.getElementById("btn");

			const CANVAS_WIDTH = (canvas.width = 500);
			const CANVAS_HEIGHT = (canvas.height = 500);

			console.log("----CANVAS_WIDTH--: ", CANVAS_WIDTH);
			console.log("----CANVAS_HEIGHT--: ", CANVAS_HEIGHT);

			const ROOT_OF_TWO_OVER_TWO = Math.sqrt(2) / 2; // 根号2/2 约等于 0.707

			// step 在各个方向上面移动的距离都应该是圆半径的倍数
                        const STEP = 10;

			const DOT_RADIUS = 10;

			// 上 上右 右 。。。
			const DIRECTIONS = [1, 2, 3, 4, 5, 6, 7, 8];

			const getRandomDirection = () => {
				const index = Math.floor(Math.random() * DIRECTIONS.length);
				return DIRECTIONS[index];
			};

			const getRandomColor = () => {
				return `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${
					Math.random() * 255
				})`;
			};

			class Dot {
				#active = true;

				constructor(x, y, r = DOT_RADIUS, color = "#f00") {
					this.x = x;
					this.y = y;
					this.r = DOT_RADIUS;
				}

				move() {
					const direction = getRandomDirection();

					console.log("--this---move: ", this);

					switch (direction) {
						case 1:
							// up
							if (this.y - this.r <= 0) {
								// return;
								break;
							}
							this.y -= STEP;
							break;
						case 2:
							if (
								this.y - this.r <= 0 ||
								this.x + this.r >= canvas.width
							) {
								// return;
								break;
							}
							this.y -= STEP * ROOT_OF_TWO_OVER_TWO;
							this.x += STEP * ROOT_OF_TWO_OVER_TWO;
							break;
						case 3:
							//right
							if (this.x + this.r >= canvas.width) {
								// return;
								break;
							}
							this.x += STEP;
							break;
						case 4:
							if (
								this.y + this.r >= CANVAS_HEIGHT ||
								this.x + this.r >= canvas.width
							) {
								// return;
								break;
							}
							this.x += STEP * ROOT_OF_TWO_OVER_TWO;
							this.y += STEP * ROOT_OF_TWO_OVER_TWO;
							break;
						case 5:
							// down
							if (this.y + this.r >= CANVAS_HEIGHT) {
								// return;
								break;
							}
							this.y += STEP;
							break;
						case 6:
							if (
								this.y + this.r >= CANVAS_HEIGHT ||
								this.x - this.r <= 0
							) {
								// return;
								break;
							}
							this.y += STEP * ROOT_OF_TWO_OVER_TWO;
							this.x -= STEP * ROOT_OF_TWO_OVER_TWO;
							break;
						case 7:
							// left
							if (this.x - this.r <= 0) {
								// return;
								break;
							}
							this.x -= STEP;
							break;
						case 8:
							if (this.y - this.r <= 0 || this.x - this.r <= 0) {
								// return;
								break;
							}
							this.x -= STEP * ROOT_OF_TWO_OVER_TWO;
							this.y -= STEP * ROOT_OF_TWO_OVER_TWO;
							break;

						default:
							break;
					}

					// moveObj[direction]();

					this.drawDot();

					if (this.#active) {
						requestAnimationFrame(this.move.bind(this));
					}
				}
				drawDot() {
					console.log("--ctx--drawDot: ", ctx);
					console.log("--ctx--drawDot this: ", this);
					ctx.fillStyle = getRandomColor();
					ctx.beginPath();
					ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
					ctx.fill();
					console.log("--ctx---end--drawDot: ");
				}
			}

			const onClick = (e) => {
				console.log(e);
				const x = e.offsetX;
				const y = e.offsetY;
				// drawDot(x, y);
				const dot = new Dot(x, y);
				dot.move();

				// ctx.fillStyle = getRandomColor();
				// ctx.beginPath();
				// ctx.arc(150, 100, 10, 0, Math.PI * 2, false);
				// ctx.fill();
			};

			// btn.addEventListener("click", onClick);
			canvas.addEventListener("click", onClick);
		</script>
	</body>
</html>

实际写完后注意到如下问题: 1 因为我做前端 5年多,也较少手写 canvas 的题目,所以没有一下子写出来。

2 画布的宽高应该用 js 设置,不能用css 设置

3 点击事件的坐标 offsetX offsetY 是相对点击元素的位置。

4 对 dom 元素的各种距离 screenX, screenY, pageX, pageY, left top right bottom , 以及 鼠标点击事件对象里面 的各种距离需要 回头做个总结,画个图