react新手教程井字棋游戏

react井字棋1.0

js部分

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import './index.css';

// 单个格子
function Square (props) {
    const lightLIst = props.winlist
    const num = props.num
    const [light, setLight] = useState(false)
    useEffect(() => {
        if (!lightLIst) return setLight(false)
        for (const key in lightLIst) {
            if (lightLIst[key] === num) return setLight(true)
        }
    }, [lightLIst, num])
    const cName = 'light'
    return (
        <button className={`square ${light ? cName : ''}`} onClick={props.onClick}>
            {props.value}
        </button>
    )
}

// 生成九宫格的组件
class Board extends React.Component {
    renderSquare(i, y, x) {
        return <Square
            key={`${i}${y}`}
            winlist={this.props.winlist}
            num={i}
            value={this.props.squares[i]}
            onClick={() => this.props.onClick(i, y, x)}
        />
    }
    BoardList () {
        const row = []
        for (const arr of this.props.list) {
            row.push(<div key={`${arr[0][1]}${arr[0][2]}`}>
                {arr.map(item => {
                    return this.renderSquare(item[0], item[1], item[2])
                })}
            </div>)
        }
        return row
    }
    render() {
        return (
            <div>
                {this.BoardList()}
            </div>
        )
    }
}

// 游戏组件
class Game extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            list: [
                [
                    [0, 3, 1],
                    [1, 3, 2],
                    [2, 3, 3]
                ],
                [
                    [3, 2, 1],
                    [4, 2, 2],
                    [5, 2, 3]
                ],
                [
                    [6, 1, 1],
                    [7, 1, 2],
                    [8, 1, 3]
                ]
            ],
            history: [
                {
                    squares: Array(9).fill(null),
                    xIndex: 0,
                    yIndex: 0,
                    boldNum: 0
                }
            ],
            stepNumber:0,
            xIsNext: true,
        }
    }
    handleClick(i, y, x) {
        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,
                yIndex: y,
                xIndex: x,
                boldNum: history.length
            }]),
            stepNumber: history.length,
            xIsNext: !this.state.xIsNext,
        })
    }
    reverseHistory() {
        const history = this.state.history.slice()
        const newArr = []
        for (const key in history) {
            newArr.unshift(history[key])
        }
        this.setState({
            history: newArr
        })
    }
    jumpTo(move) {
        this.setState({
            stepNumber: move,
            xIsNext: (move % 2) === 0
        })
    }
    render() {
        const history = this.state.history
        const current = history[this.state.stepNumber]
        const winList = calculateWinner(current.squares)
        let winStatus
        winStatus = winList ? winList[0] : 0
        const winner = current.squares[winStatus]
        const lastStep = current.squares.filter((item) => {
            return item === null
        })

        const moves = history.map((step, move) => {
            const desc = step.boldNum ?
                `Go to move #${step.boldNum}${winList}(x:${step.xIndex},y:${step.yIndex})`:
                'Go to game start'
            return (
                <li key={`${step.boldNum}`}
                    className={[step.boldNum === this.state.stepNumber ? 'bold' : '']}>
                    <button onClick={() => this.jumpTo(move)}>{desc}</button>
                </li>
            )
        })

        let status
        if (winList) {
            status = 'Winner:' + winner
        } else {
            if (!winList && lastStep.length < 1) {
                status = '平局'
            } else {
                status = 'Next player:' + (this.state.xIsNext ? 'X' : "0")
            }
        }
        return (
            <div className="game">
                <div className="game-board">
                    <Board
                        list={this.state.list}
                        winlist={winList}
                        squares={current.squares}
                        onClick={(i, x, y) => this.handleClick(i, x, y)}
                    />
                </div>
                <div className="game-info">
                    <div>{status}</div>
                    <ol>{moves}</ol>
                </div>
                <div>
                    <button onClick={() => this.reverseHistory()}>
                        改变排序
                    </button>
                </div>
            </div>
        )
    }
}

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

// 页面的根元素
ReactDOM.render(
    <Game />,
    document.getElementById('root')
);

// 规定井字棋胜负的方法
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 lines[i];
        }
    }
    return null;
}

css部分

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

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

.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;
     34px;
}

.square:focus {
    outline: none;
}

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

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

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

.bold{
    font-weight: bold;
}

.light{
    background: chartreuse!important;
}



react井字棋2.0

js部分

主页面
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { CanvasBox } from './canvas'

// 单个格子
function Square (props) {
    const lightList = props.winlist

    const num = props.num
    const [light, setLight] = useState(false)

    // 做判断是否处在胜利的三颗棋子中,是则高亮
    useEffect(() => {
        if (!lightList) return setLight(false)
        for (const key in lightList) {
            if (lightList[key] === num) return setLight(true)
        }
    }, [lightList, num])

    const str = props.value
    const winKey = props.winKey
    const canvasSquare = React.createRef()
    // 设置画布数据
    const setCanvasFn = (canvasSquare, str, lightList, winKey) => {
        const cbr = canvasSquare.current
        if (cbr.getContext){
            const cb = cbr.getContext('2d')
            // 清空画布
            cbr.height = cbr.height
            // 显示字体
            if (str) {
                cb.beginPath()
                cb.fillStyle = 'black'
                cb.font = '48px sans-serif'
                if (str === 'O') cb.fillText(str, 16,52, 60)
                if (str === 'X') cb.fillText(str, 20,52, 60)
                cb.closePath()
            }
            //  产生竖线
            const h = () => {
                cb.moveTo(0, 34)
                cb.lineTo(68,34)
                cb.lineWidth = 8
                cb.stroke()
            }
            //  产生横线
            const w = () => {
                cb.moveTo(34, 0)
                cb.lineTo(34,68)
                cb.lineWidth = 8
                cb.stroke()
            }
            //  产生左斜线
            const l = () => {
                cb.moveTo(0, 0)
                cb.lineTo(68,68)
                cb.lineWidth = 8
                cb.stroke()
            }
            //  产生右斜线
            const r = () => {
                cb.moveTo(68, 0)
                cb.lineTo(0,68)
                cb.lineWidth = 8
                cb.stroke()
            }
            // 游戏出现胜利结果后,使用条件判断哪个格子该产生什么线
            if (lightList) {
                for (const key in lightList) {
                    if (lightList[key] === num) {
                        switch (winKey) {
                            case  0:
                            case  1:
                            case  2:
                                h()
                                break
                            case  3:
                            case  4:
                            case  5:
                                w()
                                break
                            case  6:
                                l()
                                break
                            case  7:
                                r()
                                break
                            default:
                                return ''
                        }
                    }
                }
            }
        }
    }
    useEffect(() => {
        setCanvasFn(canvasSquare, str, lightList, winKey)
    }, [str, lightList, canvasSquare, winKey])

    const cName = 'light'
    return (
        <canvas ref={canvasSquare} className={`square ${light ? cName : ''}`} width="68px" height="68px" onClick={props.onClick}>
            {props.value}
        </canvas>
    )
}

// 生成九宫格的组件
class Board extends React.Component {
    renderSquare(i, y, x) {
        return <Square
            key={`${i}${y}`}
            winKey={this.props.winKey}
            winlist={this.props.winlist}
            num={i}
            value={this.props.squares[i]}
            onClick={() => this.props.onClick(i, y, x)}
        />
    }
    BoardList () {
        const row = []
        for (const arr of this.props.list) {
            row.push(<div key={`${arr[0][1]}${arr[0][2]}`}>
                {arr.map(item => {
                    return this.renderSquare(item[0], item[1], item[2])
                })}
            </div>)
        }
        return row
    }
    render() {
        return (
            <div>
                {this.BoardList()}
            </div>
        )
    }
}

// 游戏组件
class Game extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            list: [
                [
                    [0, 3, 1],
                    [1, 3, 2],
                    [2, 3, 3]
                ],
                [
                    [3, 2, 1],
                    [4, 2, 2],
                    [5, 2, 3]
                ],
                [
                    [6, 1, 1],
                    [7, 1, 2],
                    [8, 1, 3]
                ]
            ],
            history: [
                {
                    squares: Array(9).fill(null),
                    xIndex: 0,
                    yIndex: 0,
                    boldNum: 0
                }
            ],
            stepNumber:0,
            xIsNext: true,
        }
    }
    handleClick(i, y, x) {
        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) && calculateWinner(squares).arr) || squares[i]) return
        squares[i] = this.state.xIsNext ? 'X' : 'O'
        this.setState({
            history: history.concat([{
                squares: squares,
                yIndex: y,
                xIndex: x,
                boldNum: history.length
            }]),
            stepNumber: history.length,
            xIsNext: !this.state.xIsNext,
        })
    }
    reverseHistory() {
        const history = this.state.history.slice()
        const newArr = []
        for (const key in history) {
            newArr.unshift(history[key])
        }
        this.setState({
            history: newArr
        })
    }
    jumpTo(move) {
        this.setState({
            stepNumber: move,
            xIsNext: (move % 2) === 0
        })
    }
    render() {
        const history = this.state.history
        const current = history[this.state.stepNumber]
        const winKey = calculateWinner(current.squares) && calculateWinner(current.squares).key
        const winList = calculateWinner(current.squares) && calculateWinner(current.squares).arr
        let winStatus
        winStatus = winList ? winList[0] : 0
        const winner = current.squares[winStatus]
        const lastStep = current.squares.filter((item) => {
            return item === null
        })

        const moves = history.map((step, move) => {
            const desc = step.boldNum ?
                `Go to move #${step.boldNum}${winList}(x:${step.xIndex},y:${step.yIndex})`:
                'Go to game start'
            return (
                <li key={`${step.boldNum}`}
                    className={[step.boldNum === this.state.stepNumber ? 'bold' : '']}>
                    <button onClick={() => this.jumpTo(move)}>{desc}</button>
                </li>
            )
        })

        let status
        if (winList) {
            status = 'Winner:' + winner
        } else {
            if (!winList && lastStep.length < 1) {
                status = '平局'
            } else {
                status = 'Next player:' + (this.state.xIsNext ? 'X' : "0")
            }
        }
        return (
            <div className="game">
                <div className="game-board">
                    <Board
                        list={this.state.list}
                        winKey={winKey}
                        winlist={winList}
                        squares={current.squares}
                        onClick={(i, x, y) => this.handleClick(i, x, y)}
                    />
                </div>
                <div className="game-info">
                    <div>{status}</div>
                    <ol>{moves}</ol>
                </div>
                <div className="game-info">
                    <button onClick={() => this.reverseHistory()}>
                        改变排序
                    </button>
                </div>
                <div className="game-info">
                    <CanvasBox/>
                </div>
            </div>
        )
    }
}

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

// 页面的根元素
ReactDOM.render(
    <Game />,
    document.getElementById('root')
);

// 规定井字棋胜负的方法
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]) {
            const data = {
                arr: lines[i],
                key: i
            }
            return data
        }
    }
    return null;
}

次要组件(可删除)
import React, {useEffect} from 'react'

export function CanvasBox() {
    const canvasBox = React.createRef()
    useEffect(() => {
        const canvas = canvasBox.current
        if (canvas.getContext) {
            const ctx = canvas.getContext('2d')
            ctx.fillStyle = '#ff0000'
            ctx.fillRect(33,0,66,33)
            ctx.moveTo(33,0)
            ctx.lineTo(33,99)
            ctx.stroke()
            ctx.moveTo(66,0)
            ctx.lineTo(66,99)
            ctx.stroke()
            ctx.moveTo(0,33)
            ctx.lineTo(99,33)
            ctx.stroke()
            ctx.moveTo(0,66)
            ctx.lineTo(99,66)
            ctx.stroke()
            ctx.fillStyle='#ff0000'
            ctx.beginPath()
            ctx.arc(49.5,49.5,14,0,Math.PI*2,true)
            ctx.closePath()
            ctx.fill()
            ctx.beginPath()
            ctx.fillStyle='white'
            ctx.font='24px'
            ctx.fillText('⚪',42.5,52.5)
            ctx.closePath()
        }
    })
    return(
        <div>
            <canvas ref={canvasBox} id='canvas-box' width='99px' height='99px' style={{ border: '1px solid #c3c3c3' }}>
                您的浏览器不支持canvas元素
            </canvas>
            <canvas className='square' width='33px' height='33px' >
                您的浏览器不支持canvas元素
            </canvas>
        </div>
    )
}

css部分

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

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

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

.status {
    margin-bottom: 10px;
}

.square {
    background: #fff;
    border: 1px solid #999;
    float: left;
    height: 34px;
    box-sizing: border-box;
    padding: 0;
     34px;
}

.square:focus {
    outline: none;
}

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

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

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

.bold{
    font-weight: bold;
}

.light{
    background: chartreuse!important;
}

原文地址:https://www.cnblogs.com/ww-523-99/p/14004026.html