Drawing Chinese Chess Board - Using CSS Pseudo Elements

Drawing Chinese Chess Board - Using CSS Pseudo Elements

Compared to chess:

chess board

Drawing a chess board is slightly more difficult:

cn-chess

The main reason is that chess moves are in the grid, and chess moves are at intersections. Therefore, the layout of chess can use background-color: #fffand background-color: #aaamark odd and even grids - this is also provided by CSS. nth-child()This pseudo class can be convenient and efficient to complete the task, but the processing of Chinese chess is relatively more troublesome.

However, after some searching, I found that there is a relatively simple way to draw a chessboard, that is, through beforeand afterthese two pseudo elements, the final result is as follows:

res1

The blank chessboard is as follows:

res2

The specific implementation is as follows:

  • The parent component manages the checkerboard array

    import {
          
           useState } from "react";
    import Jiang from "../../types/pieces/Jiang";
    import Piece from "../piece/Piece";
    import styled from "styled-components";
    
    const ChessboardUIWrapper = styled.div`
      border: 2px solid ${
            
            BASE_BLACK};
      margin: 0 auto;
      padding: 2em;
      width: calc(${
            
            TILE_SIZE} * ${
            
            NUM_OF_COLS});
      position: relative;
    
      @media (max-device-width: 1024px) {
        padding: 0.5em;
      }
    
      @media (max-width: 768px) {
        padding: 0.8em;
        width: 80%;
      }
    
      @media (max-device-width: 1024px) and (orientation: landscape) {
        max-width: calc(${
            
            NUM_OF_COLS} * ${
            
            TILE_SIZE_MOBILE_LANDSCAPE});
      }
    `;
    
    const ChessPiecesUI = styled.div`
      display: grid;
      grid-template-columns: repeat(${
            
            NUM_OF_COLS}, ${
            
            TILE_SIZE});
    
      @media (max-width: 768px) {
        grid-template-columns: repeat(${
            
            NUM_OF_COLS}, ${
            
            TILE_SIZE_MOBILE});
      }
    
      @media (max-device-width: 1024px) and (orientation: landscape) {
        grid-template-columns: repeat(
          ${
            
            NUM_OF_COLS},
          ${
            
            TILE_SIZE_MOBILE_LANDSCAPE}
        );
      }
    `;
    
    const ChessboardUI = () => {
          
          
      const chessboard = new Chessboard();
    
      // 9 * 10
      const [board, setBoard] = useState(Array(90).fill(""));
    
      const renderPieces = () => {
          
          
        return (
          <ChessPiecesUI>
            {
          
          board.map((piece, idx) => (
              <Piece key={
          
          idx} idx={
          
          idx} piece={
          
          piece} />
            ))}
          </ChessPiecesUI>
        );
      };
    
      return <ChessboardUIWrapper>{
          
          renderPieces()}</ChessboardUIWrapper>;
    };
    
  • child component render grid

    import {
          
           FC } from 'react';
    import ChessPiece from '../../types/pieces/ChessPiece';
    import blankPiece from '../../assets/imgs/blank.svg';
    
    import styled from 'styled-components';
    
    interface TileProps {
          
          
    idx: number;
    piece?: ChessPiece;
    }
    
    const Piece: FC<TileProps> = ({
           
            idx, piece }) => {
          
          
    let hasBoderBtm = Math.floor(idx / 9) !== 9 && Math.floor(idx / 9) !== 4;
    // keep borders for river
    if ((idx % 9 === 0 || idx % 9 === 8) && Math.floor(idx / 9) !== 9) {
          
          
        hasBoderBtm = true;
    }
    const hasBorderRight = idx % 9 !== 8;
    
    const image = piece ? piece.getImages : blankPiece;
    const alt = piece ? piece.getTypes.toString() : 'blank piece';
    
    return (
        <ChessPieceUI hasBefore={
          
          hasBoderBtm} hasAfter={
          
          hasBorderRight}>
        <Img src={
          
          image} alt={
          
          alt} className="chess-piece__img" />
        </ChessPieceUI>
    );
    };
    
    const before = `
    &:before {
    content: '';
    position: absolute;
    left: calc(50% - 1px);
    top: 50%;
    border-right: 1px solid black;
    height: 100%;
    }`;
    
    const after = `
    &:after {
    content: '';
    position: absolute;
    left: 50%;
    top: calc(50% - 1px);
    border-top: 1px solid black;
    width: 100%;
    }`;
    
    const ChessPieceUI = styled.div<{
          
           hasBefore: boolean; hasAfter: boolean }>`
    position: relative;
    
    ${
            
            ({
             
              hasBefore }) => hasBefore && before}
    
    ${
            
            ({
             
              hasAfter }) => hasAfter && after}
    `;
    
    const Img = styled.img`
    max-width: 100%;
    position: relative;
    z-index: 1;
    `;
    
    export default Piece;
    

Because of the use of CSS in JS, the amount of code looks longer.

The specific logical analysis is as follows:

90 grids are generated in the parent component (the chess can be placed at 90), and then the data of the piece is passed to the child component.

The child component beforewill aftergenerate vertical and horizontal borders through and two pseudo selectors, the details are as follows:

detail

The difference between using border-top, border-bottom, border-right, here border-leftis not very big, as long as one is horizontal and the other is vertical, the difference is nothing more than the adjustment of the position, and finally the rowNum and colNum that need to be hidden. Without any conditional controls, beforeand afterall rendered chessboards are as follows:

all rendered

What I need to hide here is the one on the far right of the border-topchessboard and the one on the bottom of the chessboard border-right. Of course, because the space between the Chu River and Han borders is still needed, the Chu River and Han borders alsocol = 0 need to be reserved.col = 8border-right

I use a one-dimensional array here, and the control of the condition will be a little more troublesome. If it is a two-dimensional array, it can rowbe colcompared directly by and .

In general, this use of pseudo elements accidentally discovered this time is also a break from fixed thinking. Later, if there are other similar requirements, you can completely pass the pseudo elements. First, the processing of the event handler will be much more convenient (refs are required to use canvas), and secondly, it will also write a lot less code (I heard that canvas drawing is actually quite troublesome. ), and thirdly, the support for the mobile terminal (compared to the use of image cutting, the canvas mobile terminal is not very convenient and cannot be compared) is also much better.

By the way, I attach a few pictures of the effect of the mobile terminal. Of course, the size of the grid, padding and margin need to be adjusted, but this is just a matter of modifying two or three lines of code in CSS or constant.

mobile1

mobile2

tablet1

tablet2

Add a completed picture:

finished

The Mizi grid is made for SVG.

Guess you like

Origin blog.csdn.net/weixin_42938619/article/details/124014521