import "./styles.css";
import useBall from "./hooks/useBall";
import useBoard from "./hooks/useBoard";
import React, { useEffect, useState } from "react";
const width = 500;
const height = 500;
const refreshInterval = 50;
//建立一个小球
const ballRadius = width / 30;
const ballX = width / 2 - ballRadius;
//建立一个挡板
const boardWidth = width / 6;
const boardHeight = 10;
const boardX = (width - boardWidth) / 2;
const boardY = height - 100;
export default function App() {
const [started, setSarted] = useState(false);
const [stateCode, setSateCode] = useState(0);
const [timer, setTimer] = useState(null);
const [context, setConText] = useState(null);
const { board, reset: resetBoard, drawMe: drawBoard, setBoard } = useBoard(
boardX,
boardY,
boardWidth,
boardHeight,
context
);
const {
ball,
move: moveBall,
drawMe: drawBall,
reset: resetBall,
setBall
} = useBall(ballX, 50, ballRadius, context);
console.log("board", board);
useEffect(() => {
const canvas = document.getElementById("canvas");
if (canvas) {
const ctx = canvas.getContext("2d");
setConText(ctx);
}
}, [stateCode]);
useEffect(() => {
if (context) startGame();
}, [context]);
/** 执行开始游戏 */
const handleStartGameBtnClick = () => {
setSateCode(1);
};
const clear = () => {
if (context) {
context.clearRect(0, 0, width, height);
}
};
const startGame = () => {
resetBall();
resetBoard();
refreshGameView();
const updateTimer = setInterval(refreshGameView, refreshInterval);
setTimer(updateTimer);
setSarted(true);
};
const refreshGameView = () => {
/** 重新整理游戏区域 */
//每次重新整理前都需要清除背景,不然小球和挡板上次的位置会被保留
clear();
const { x, y } = moveBall();
var ballX = x;
var ballY = y;
if (ballX < 0) {
setBall((prev) => ({ ...prev, x: 0, speedX: prev?.speedX * -1 }));
}
if (ballY < 0) {
setBall((prev) => ({ ...prev, y: 0, speedY: prev?.speedY * -1 }));
}
if (ballX + 2 * ball.radius > width) {
setBall((prev) => ({
...prev,
x: width - 2 * ball?.radius,
speedY: prev?.speedX * -1
}));
}
const ballBottomY = y + 2 * ball.radius;
const ballCenterX = x + ball.radius;
if (ballBottomY >= board.y) {
if (ballCenterX >= board.x && ballCenterX <= board.x + board.width) {
//反弹
setBall((prev) => ({
...prev,
speedY: prev?.speedY * -1
}));
} else {
//游戏结束
if (timer != null) {
clearInterval(timer);
setSateCode(-1);
}
}
}
//画小球和挡板
// drawBall();
// drawBoard();
};
const handleMouseMove = (event) => {
/** 处理滑鼠的移动事件,移动滑鼠的同时移动挡板 */
const x = getClientOffset(event)?.x;
//将挡板的水平中心位置移到x处
let boardX = x - board?.width / 2;
if (boardX < 0) {
boardX = 0;
}
if (boardX + board?.width > width) {
boardX = width - board?.width;
}
setBoard({ ...board, x: boardX });
};
/** 取得位置 */
const getClientOffset = (event) => {
const canvas = document.getElementById("canvas");
let rect = canvas.getBoundingClientRect();
const point = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
return point;
};
/** 游戏结束 */
const renderGameOverView = () => (
<div>
<p>游戏结束</p>
<button className="start-game-btn" onClick={handleStartGameBtnClick}>
开始游戏
</button>
</div>
);
/** 未开始 */
const renderStartView = () => (
<button className="start-game-btn" onClick={handleStartGameBtnClick}>
开始游戏
</button>
);
/** 游戏中 */
const renderGameView = () => (
<canvas
id="canvas"
onMouseMove={handleMouseMove}
width={width}
height={height}
/>
);
const stateCodeHtml = {
"-1": renderGameOverView() /** 游戏结束 */,
0: renderStartView() /** 游戏开始 */,
1: renderGameView() /** 游戏画面 */
};
return (
<div className="App">
<div id="container">{stateCodeHtml[stateCode]}</div>
</div>
);
}
<<: 给别人前先包装:套件、汇入、存取修饰词 Packages, imports and Visibility modifiers
前言 这篇文章会进行到更多的资料操作 将会处理 Indexing Values 在标签值的处理很重...
(一)介绍 当我们需要对资料进行大量递增、删、改、查操作时,我们就会使用到资料库,而最广泛被应用的就...
由於查询部分的篇幅相较於前几者较多,因此将查询的部分独立出来写 另外这边写的只有一些基础的操作,像是...
有些时候需要看外文网页,而外文里可能有许多单字,或是看外文的速度没有那麽快,阅读起来会很辛苦。这时如...
今天要来看的是JavaScript 错误 - throw、try 和 catch: 1.try 语句...