IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React.js三连棋完整版 -> 正文阅读

[JavaScript知识库]React.js三连棋完整版

React.js三连棋完整版

前言

最近学习微前端,正好接触到了React.js,索性就系统完整地学习一遍。和当初学习Vue.js相同,先将官网的教程看一遍,在结合项目完整地做一个系统出来,从而达到融会贯通地目的。

三连棋完整功能代码

大家看了React.js的官网教程的话,就会发现和其他教程不同,React.js第一节课就是教会我们如何写一个游戏——三连棋,目的是让我接触和感受React.js中的组件、状态等一些概念,同时不失趣味性。官网已经有了绝大部分的游戏代码,但是也留下了几个小任务让我们去自主完成,从而检验我们的学习情况。话不多说,上完整代码:
在这里插入图片描述
index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

function Square(props) {
  // 任务2:在历史记录列表中加粗显示当前选择的项目
  return (
    <button className={'square' + (props.isActive?' active':'') + (props.highlight?' highlight':'')} onClick={props.onClick}>
      {props.value}
    </button>
  )
}

class Board extends React.Component {
  renderSquare(i) {
    return (
      <Square
        highlight={this.props.highlight[0] === i || this.props.highlight[1] === i || this.props.highlight[2] === i}
        key={i.toString()}
        isActive={this.props.isActive === i}
        value={this.props.squares[i]} 
        onClick={()=>this.props.onClick(i)} 
      />
    )
  }

  // 任务3:使用两个循环来渲染出棋盘的格子,而不是在代码里写死(hardcode)
  render() {
    let boardRow = []
    for(let i = 0;i < 3;i++){
      let square = []
      for(let j = 0;j < 3;j++){
        square.push(this.renderSquare(i*3 + j))
      }
      boardRow.push(
        <div key={i} className="board-row">{square}</div>
      )
    }
    return (
      <div>
        {boardRow}
      </div>
    );
  }
}

class Game extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      history:[{
        // 当前棋盘所有棋子状态
        squares:Array(9).fill(null),
        // 当前棋子的落点坐标
        position:null,
      }],
      // 当前棋子步数
      stepNumber:0,
      // 下一步是否是X
      xIsNext:true,
      // 当前记录按钮按照升序acs排列,默认升序
      acs:true
    }
  }
  // 棋盘落子事件,只需要管数据,渲染的事情交给render()
  handleClick(i) {
    const history = this.state.history.slice(0,this.state.stepNumber + 1);
    const current = history[history.length - 1];
    const squares = current.squares.slice();
    // 如果已经有获胜者或者棋子,则没有任何反应
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] =this.state.xIsNext ? 'X':'O';
    // 更新最新的棋盘落点集合、当前步数、下一步棋手
    this.setState({
      history: history.concat([{
        squares: squares,
        position:i
      }]),
      stepNumber:history.length,
      xIsNext: !this.state.xIsNext,
    });
  }
  // 修改当前步数、下一步棋手,渲染的事情交给render()
  jumpTo(step){
    this.setState({
      stepNumber: step,
      xIsNext: (step % 2) === 0,
    })
  }
  changeSort(acs){
    this.setState({
      acs:!acs
    })
  }
  // (1)根据当前步数渲染棋盘current.squares
  // (3)下一步棋手(或者已经胜利的棋手)status 
  // (3)根据所有的历史记录渲染历史记录按钮和当时的落子位置
  render() {
    const history = this.state.history
    // (1)
    const current = history[this.state.stepNumber]
    const winnerInfo = calculateWinner(current.squares)
    let acs = this.state.acs
    let sort = []
    // 任务4:添加一个可以升序或降序显示历史记录的按钮
    sort.push(
        <button key={acs} onClick={()=>{this.changeSort(acs)}}>{acs?'升序':'降序'}</button>
    )



    // (3)
    const moves = history.map((step,move)=>{
      // 是否降序
      if(!acs){
        move = history.length - 1 - move
      }
      
      const desc = move ? 'Go to move #' + move : 'Go to game start'
      let position = history[move].position
      position = calculatePosition(position)
      // 任务1:在游戏历史记录列表显示每一步棋的坐标,格式为 (列号, 行号)
      const positionDesc = move ? `position: (${position[0]},${position[1]})` : ''  
      return (
        <li key={move}>
          <button onClick={()=>{this.jumpTo(move)}}>{desc}</button>
          <span>{positionDesc}</span>
        </li>
      )
    })

    let isActive = current.position
    // 任务5:每当有人获胜时,高亮显示连成一线的 3 颗棋子
    let info = [null,null,null]
    // (2)
    let status
    if(winnerInfo){
      status = 'Winner: ' + winnerInfo.winner
      info = winnerInfo.info
    }
    else{
      // 任务6:当无人获胜时,显示一个平局的消息
      if(this.state.stepNumber === 9){
        status = 'Draw!'
      }
      else{
        status = 'Next player: ' + (this.state.xIsNext?'X':'O');
      }
    }

    return (
      <div className="game">
        <div className="game-board">
          <Board
            highlight={info}
            isActive={isActive}
            squares={current.squares}
            onClick={(i) => this.handleClick(i)}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <div>{sort}</div>
          <ol>{moves}</ol>
        </div>
      </div>
    );
  }
}

// ========================================

ReactDOM.render(
  <Game />,
  document.getElementById('root')
);

// 计算当前落子坐标
function calculatePosition(index) {
  const positions = [
    [1,1],[1,2],[1,3],
    [2,1],[2,2],[2,3],
    [3,1],[3,2],[3,3],
  ]
  return positions[index]
}

// 判断是否有玩家胜出,并且返回该玩家
function calculateWinner(squares) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      // return squares[a];
      return {
        winner: squares[a],
        info: lines[i]
      }
    }
  }
  return null;
}

index.css:

body {
  font: 14px 'Century Gothic', Futura, sans-serif;
  margin: 20px;
}

ol,
ul {
  padding-left: 30px;
}

.game-info button {
  background: aqua;
  padding: 10px;
  border: 0;
  margin: 10px;
}

.board-row:after {
  clear: both;
  content: '';
  display: table;
}

.status {
  margin-bottom: 10px;
}

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px;
}

.square:focus {
  outline: none;
}

.active {
  border: 3px solid #000;
  line-height: 27px;
}
.highlight {
  background: aqua;
}

.kbd-navigation .square:focus {
  background: #ddd;
}

.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}

在这里插入图片描述

建议大家在学习后自己完成一遍这几个任务,同时也可以参考对照我的文章,相互学习进步。如果能够对我的代码提些宝贵的意见的话,那我必将不胜荣幸。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-25 12:26:58  更:2021-10-25 12:27:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 9:10:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码