Sunshine168 / react-konva-demo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

react-konva-demo

简介

在线例子 以create-react-app作为脚手架,通过react+redux+react-konva 写一个简单的五子棋游戏,分享一下react-konva的实际使用例子 (没有人机对战功能哟)

react-konva是一个react组件化的canvas库,来绘制五子棋 redux处理游戏中的数据流

思路介绍

把五子棋游戏分为两个部分,一个是设定部分,另外一部分就是游戏本身。 将五子棋游戏本身分解为三层,即分解为三个组件 底层是棋盘,包括棋盘颜色与网格的绘制 中间层是绘制棋子在棋盘上的状态的 最顶层是操作层负责落子以及绘制光标状态。 五子棋状态储存在一个二维数组中,通过判断值是否大于0判断是否已经落子

棋盘绘制

// src/components/Board.js
import React,{Component} from 'react';
import {Shape, Group, Rect} from 'react-konva';
export default class Board extends Component {
  constructor(args){
    super(...args)
    //绑定上下文
    this._grid = this._grid.bind(this);
  }
  //绘制网格的函数
  _grid(context){
     /*fill the board */
     context.beginPath();
     context.fillStyle = "#976B42"
     context.fill();
     context.closePath();
     let col= 0, row= 0,
     { width,height, gridSize } = this.props;
     col= Math.ceil(width/gridSize);
     row= Math.ceil(height/gridSize);
     //绘制列
     for(let i= 0; i<= col; i++) {
       context.beginPath();
         context.moveTo(gridSize*i,0);
         context.lineTo(gridSize*i,height);
         context.stroke();
          context.closePath();
     }
     //绘制行
      for(let j= 0; j<= row; j++){
        context.beginPath();
        context.moveTo(0,gridSize*j);
        context.lineTo(width,gridSize*j);
        context.stroke();
        context.closePath();
      }
  }
  render(){
    let { width,height} = this.props;
    console.log(this.props)
     return (
      <Group>
        <Rect
          fill = {"#fed386"}
          width= {width}
          height= {height}
          strokeWidth= {2}
          stroke={ 'black'}
        />
        <Shape
          x= {0}
          y= {0}
          width= {width}
          height= {height}
          strokeWidth= {2}
          sceneFunc = {this._grid}
          stroke={ 'black'}
        />
      </Group>
     )
  }
}

Rect是konva中绘制矩形的控件, Shape是绘制图形的控件即绘制自定义控件通过sceneFunc函数传入context进行自定义绘制

棋子绘制

import React ,{Component} from 'react';
import {Shape} from 'react-konva';
export default class ChessDisplay extends Component {
  constructor(props){
    super(props)
    this._drawChess = this._drawChess.bind(this);
  }
  _drawChess(context){
      const {chessStore,gridSize,cheeSize} = this.props;
      if(!chessStore.hasOwnProperty('length')){
           throw Error("must be a valid array");
      }
      //需要初始化数组,
      //type 1 白棋 2 黑棋
      for(let i = 0; i < chessStore.length; i++){
           for(let j = 0; j < chessStore[i].length; j++){
                if(chessStore[i][j] >= 1){
                  if(chessStore[i][j] === 1){
                    context.fillStyle = "white";
                  }
                  if(chessStore[i][j] === 2)
                  {
                    context.fillStyle = "black";
                  }
                   context.beginPath();
                   /*context.arc(x,y,r,sAngle,eAngle,counterclockwise);*/
                   context.arc(i*gridSize,j*gridSize,cheeSize,0,2*Math.PI);
                   context.fill();
                   context.closePath();
                }
           }
      }
   }
  render(){
       let {width, height}  = this.props;
    return (
      <Shape
        x= {0}
        y= {0}
        width= {width}
        height= {height}
        strokeWidth= {2}
        sceneFunc = {this._drawChess}
        stroke={ 'black'}
      />
    )
  }
}

落子组件

 import React, {Component} from 'react';
 import {Shape, Rect, Circle, Group} from 'react-konva';
 import PropTypes from 'prop-types'
 export default class HandleChess extends Component {
  static propTypes=({
  chess:PropTypes.array,
  addChess:PropTypes.func,
  endChess:PropTypes.func,
  turn:PropTypes.number
})
 constructor(props){
   super(props)
   this.__handleChess = this._handleChess.bind(this);
   this._pointer = this._pointer.bind(this);
   this.state = {
     pointerX:null,
     pointerY:null
   }
 }
 _handleChess(){
   let stage = this.props.getStage().getStage(),mousePos,x,y,pointX,pointY,chess,winner,
   {chessStore, addChess, endChess, turn, isBegin, setWinner}  = this.props;
   if(!isBegin){
     return
   }
   const gridSize = 50 ;
   mousePos= stage.getPointerPosition();
   x = mousePos.x,
   y = mousePos.y;
   pointX = mousePos.x > gridSize ? mousePos.x/gridSize :1 ;
   pointY = mousePos.y > gridSize ? mousePos.y/gridSize :1;
   pointX = Math.floor(mousePos.x % gridSize >= 25 ? ++pointX : pointX);
   pointY = Math.floor(mousePos.y % gridSize >= 25 ? ++pointY : pointY);
   /*add Chess*/
   if(chessStore[pointX]&&!chessStore[pointX][pointY]){
      chess = {
        x:pointX,
        y:pointY,
        type:turn,
      }
     if(pointX > 0 && pointY > 0){
       addChess(chess)
     }

     try{
       if(this._isWin(chessStore,chess)){
         if(chess.type === 1 ){
              winner = "白棋"
              setWinner(1)
         }
         if(chess.type ===2 ){
             winner = "黑棋"
             setWinner(2)
         }
         alert(`${winner}胜利,游戏结束`)
         endChess();

       }
     }catch(e){
        console.log(e)
     }

   }


 }
 //扫描游戏是否已经结束了
 _isWin(store,chess){
    let pointX = chess.x,
        pointY = chess.y,
        type   = chess.type,
        count  = 0 ;
        //扫描行
    for(let i = pointX - 5; i <= pointX + 5; i++){
       if(i<=0) {
         continue;
       }

        if(type === store[i][pointY]){
           count++;
         }
       if(count === 5){
          return 1
       }
       if(i + 1 == store.length){
            break;
       }
    }
    //清空count
    count = 0 ;

    //扫描列
    for(let j = pointY - 5; j<= pointY + 5; j++){
      if(j<=0) {
        continue;
      }else{
        if(type === store[pointX][j]){
          count++;
        }
      }
      if(count === 5){
         return 1
      }
      if(j + 1 == store[pointX].length){
           break;
      }
    }
    //清空count
    count = 0 ;

    //扫描右对称轴
    for(let i = pointX - 5, j = pointY - 5;i <= pointX + 5; i++,j++){
      if(i <= 0 || j<= 0) {
        continue;
      }else{
        if(type === store[i][j]){
          count++;
        }
      }
      if(count === 5){
         return 1
      }
      if(i + 1 == store.length || j + 1 == store[i].length){
           break;
      }
    }
    //清空count
    count = 0 ;
    //扫描左对称轴
    for(let i = pointX + 5, j = pointY +5; i>=pointX - 5; i--,j--){
      if(i + 1 >= store.length || j + 1 >= store[i].length) {
        continue;
      }else{
        if(type === store[i][j]){
          count++;
        }
      }
      if(count === 5){
         return 1
      }
      if(i <= 0 || j<= 0){
           break;
      }
 }
  return 0;
}
/*绘制一个跟随光标的棋子*/
_pointer(){
  let stage = this.props.getStage().getStage(),
  mousePos = stage.getPointerPosition();
  this.setState({
    pointerX: mousePos.x,
    pointerY: mousePos.y
  })
}
 /*父组件需要传入stage?*/
 render(){
    let winWidth= 800, winHeight= 800;
    const {turn, cheeSize} = this.props;
    let {pointerX, pointerY} =  this.state;
    return (
      <Group>
        {this.props.isBegin && pointerX && pointerY ?
          <Circle
            x = {pointerX}
            y = {pointerY}
            fill = {turn===1? "white" : "black"}
            radius = {20}
          />
          :null
        }
        <Rect
          x = {0}
          y = {0}
          height = {winHeight}
          width  = {winWidth}
          onClick = {this.__handleChess}
          onMousemove = {this._pointer}
        />
      </Group>
);
 }
}

About


Languages

Language:JavaScript 91.3%Language:HTML 6.6%Language:CSS 2.0%