componentDidMount() {
// 监听键盘按下事件
document.onkeydown = this.onkeydown;
// 每隔200ms前进一步
setInterval(this.onMove, this.state.speed);
}
onkeydown = (e) => {
// 根据按下的键盘键更新direction
switch (e.keyCode) {
case 37:
this.setState({ direction: “LEFT” });
break;
case 38:
this.setState({ direction: “UP” });
break;
case 39:
this.setState({ direction: “RIGHT” });
break;
case 40:
this.setState({ direction: “DOWN” });
break;
default:
this.setState({ direction: “RIGHT” });
}
}
onMove = () => {
// 根据direction指示的方向前进
let newSnakeDots = […this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
switch (this.state.direction) {
case “UP”:
header = [header[0], header[1] - 2];
break;
case “DOWN”:
header = [header[0], header[1] + 2];
break;
case “LEFT”:
header = [header[0] - 2, header[1]];
break;
case “RIGHT”:
header = [header[0] + 2, header[1]];
break;
default:
header = [header[0] + 2, header[1]];
}
// 删去第一个小黑块,并在direction方向上新增新小黑块,更新snakeDots
newSnakeDots.shift();
newSnakeDots.push(header);
this.setState({ snakeDots: newSnakeDots })
}
效果:
当触碰边界或者撞击到自身时,游戏结束,弹窗提示,分数为贪吃蛇的长度-2(初始长度为2,其余均是吃到食物后变长的,吃一个食物长长1格)
componentDidUpdate() {
this.checkIfBordered();
}
checkIfBordered = () => {
let newSnakeDots = […this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
if (header[0] < 0 || header[0] > 98 || header[1] < 0 || header[1] > 98) {
alert(触碰边界,游戏结束,你的得分是:${newSnakeDots.length - 2}
);
this.setState(initState);
}
}
由于我们规定,贪吃蛇触碰到边界时算游戏结束,因此,食物不能出现在边界上,否则,一吃到食物就代表触壁结束游戏(可以改进为:贪吃蛇每次前进1%,当食物被触碰比例达50%时可以当作贪吃蛇吃掉该食物,马上掉头避免触壁,以后再改吧~)
因此,食物出现的范围在top:2%~96%,left的范围也是如此
const getRandomFood = ()=>{
// 食物出现的范围是:[2, 96] 推算过程:[0,95)->[2, 97)->[1, 48.5]->[1,48]->[2,96]
let max = 95;
let min = 2;
let x = Math.floor((Math.random() * max + min) / 2) * 2;
let y = Math.floor((Math.random() * max + min) / 2) * 2;
return [x,y]
}
const initState = {
snakeDots: [
[0, 0],
[2, 0]
],
direction: “RIGHT”,
speed: 200,
food:getRandomFood() // 随机生成食物的位置
}
食物的展示:
import { Food } from ‘./components/Food’;
render() {
return (
);
}
import React from ‘react’
export const Food = ({food}) => {
let foodStyle = {
left:${food[0]}%
,
top:${food[1]}%
}
return (
)
}
食物的样式:
.food {
height: 2%;
width: 2%;
background-color: red;
border: 1px solid #fff;
position: absolute;
}
贪吃蛇的头(head)和食物重叠时,代表吃到
贪吃蛇变长,食物的位置变成新的head,重新放置食物
componentDidUpdate() {
// 每次更新都判断一下
this.checkIfBordered();
this.checkIfEated();
}
checkIfEated = ()=>{
let newSnakeDots = […this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
// head与食物重叠时,代表吃到食物
if(header[0]==this.state.food[0] && header[1]==this.state.food[1]){
console.log(“吃到了”)
newSnakeDots.unshift([]);
this.setState({snakeDots:newSnakeDots});
this.setState({food:getRandomFood()});
}
}
效果:
完整代码:
新建文件夹component存放组件:
App.js
import React, { Component } from ‘react’
import { Snake } from ‘./components/Snake’;
import { Food } from ‘./components/Food’;
// 随机获取食物位置
const getRandomFood = ()=>{
// [2, 96]: [0,95)->[2, 97)->[1, 48.5]->[1,48]->[2,96]
let max = 95;
let min = 2;
let x = Math.floor((Math.random() * max + min) / 2) * 2;
let y = Math.floor((Math.random() * max + min) / 2) * 2;
console.log(“food-position”,x,y)
return [x,y]
}
// 另存state以便重新初始化
const initState = {
snakeDots: [
[0, 0],
[2, 0]
],
direction: “RIGHT”,
speed: 200,
food:getRandomFood()
}
class App extends Component {
state = initState;
// 监听与计时器
componentDidMount() {
document.onkeydown = this.onkeydown;
setInterval(this.onMove, this.state.speed);
}
// 更新时检测是否出界或吃到食物
componentDidUpdate() {
this.checkIfBordered();
this.checkIfEated();
}
// 判断键盘按键,更新direction
onkeydown = (e) => {
switch (e.keyCode) {
case 37:
this.setState({ direction: “LEFT” });
break;
case 38:
this.setState({ direction: “UP” });
break;
case 39:
this.setState({ direction: “RIGHT” });
break;
case 40:
this.setState({ direction: “DOWN” });
break;
default:
this.setState({ direction: “RIGHT” });
}
}
// 根据direction移动
onMove = () => {
let newSnakeDots = […this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
switch (this.state.direction) {
case “UP”:
header = [header[0], header[1] - 2];
break;
case “DOWN”:
header = [header[0], header[1] + 2];
break;
case “LEFT”:
header = [header[0] - 2, header[1]];
break;
case “RIGHT”:
header = [header[0] + 2, header[1]];
break;
default:
header = [header[0] + 2, header[1]];
}
newSnakeDots.shift();
newSnakeDots.push(header);
this.setState({ snakeDots: newSnakeDots })
// console.log(this.state.snakeDots)
}
// 判断是否出界
checkIfBordered = () => {
let newSnakeDots = […this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
if (header[0] < 0 || header[0] > 98 || header[1] < 0 || header[1] > 98) {
alert(触碰边界,游戏结束,你的得分是:${newSnakeDots.length - 2}
);
this.setState(initState);
}
}
// 判断是否吃到食物
checkIfEated = ()=>{
let newSnakeDots = […this.state.snakeDots];
let header = newSnakeDots[newSnakeDots.length - 1];
if(header[0]==this.state.food[0] && header[1]==this.state.food[1]){
console.log(“吃到了”)
newSnakeDots.unshift([]);
this.setState({snakeDots:newSnakeDots});
this.setState({food:getRandomFood()});
}
}
render() {
return (
);
}
}
export default App;
Snake.js:
import React from ‘react’
最后
其实前端开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
这里再分享一个复习的路线:(以下体系的复习资料是我从各路大佬收集整理好的)
《前端开发四大模块核心知识笔记》
最后,说个题外话,我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。