중국 체스판 그리기 - CSS 의사 요소 사용

중국 체스판 그리기 - CSS 의사 요소 사용

체스와 비교:

체스판

체스판을 그리는 것은 약간 더 어렵습니다.

cn-체스

주된 이유는 체스 이동이 그리드에 있고 체스 이동이 교차점에 있기 때문입니다. 따라서 체스의 레이아웃 background-color: #fffbackground-color: #aaa홀수 및 짝수 그리드를 사용하고 표시할 수 있습니다. 이것은 CSS에서도 제공합니다. nth-child()이 의사 클래스는 작업을 완료하는 데 편리하고 효율적일 수 있지만 중국 체스의 처리는 비교적 번거롭습니다.

그러나 몇 가지 검색 후 체스판을 그리는 비교적 간단한 방법이 있음을 발견했습니다. 즉 before, after이 두 의사 요소를 통해 최종 결과는 다음과 같습니다.

res1

빈 체스판은 다음과 같습니다.

res2

구체적인 구현은 다음과 같습니다.

  • 상위 구성 요소는 바둑판 배열을 관리합니다.

    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>;
    };
    
  • 자식 구성 요소 렌더 그리드

    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;
    

JS에서 CSS를 사용하기 때문에 코드 양이 더 길어 보입니다.

구체적인 논리적 분석은 다음과 같다.

상위 컴포넌트에서 90개의 그리드가 생성되고(체스는 90에 배치될 수 있음) , 조각의 데이터가 하위 컴포넌트로 전달됩니다.

자식 구성 요소 beforeafter두 개의 유사 선택기를 통해 수직 및 수평 경계를 생성합니다. 세부 정보는 다음과 같습니다.

세부 사항

border-top여기서 , border-bottom, border-right, 를 사용 border-left하는 것의 차이는 그리 크지 않습니다. 하나는 수평이고 다른 하나는 수직인 한, 그 차이는 위치 조정에 불과하며 마지막으로 숨겨야 하는 rowNum과 colNum입니다. 조건부 컨트롤 없이 렌더링된 모든before 체스판은 다음과 같습니다.after

모든 렌더링

여기서 숨길 필요가 있는 것은 체스판의 맨 오른쪽에 있는 것과 border-top체스판의 맨 아래에 있는 것 border-right입니다. 물론 초강과 한경계 사이의 공간이 여전히 필요하기 때문에 초강과 한경계도 확보col = 0 해야 한다.col = 8border-right

여기서 1차원 배열을 사용하는데 조건의 제어가 조금 더 번거롭겠지만 2차원 배열 row이라면 col와 로 직접 비교할 수 있다.

일반적으로 이번에 우연히 발견한 유사 요소의 사용은 고정된 사고에서 벗어나는 것이기도 합니다. 나중에 다른 유사한 요구 사항이 있으면 의사 요소를 완전히 전달할 수 있습니다.첫 번째, 이벤트 핸들러의 처리가 훨씬 더 편리할 것이고(refs가 캔버스를 사용하기 위해 필요함), 두 번째로 훨씬 적은 코드를 작성할 것입니다. (캔버스 그리기는 사실 상당히 번거롭다고 들었습니다. ), 셋째, 모바일 단말 지원(이미지 커팅 사용에 비해 캔버스 모바일 단말은 그다지 편리하지 않고 비교할 수 없음)도 훨씬 좋습니다.

그건 그렇고 이동단말기의 효과를 사진으로 몇장 첨부하겠습니다.물론 그리드의 크기, 패딩, 여백의 조정이 필요하지만 이것도 2~3줄의 코드를 수정하는 문제이기도 합니다. CSS 또는 상수.

모바일1

모바일2

태블릿1

태블릿2

완성된 사진 추가:

완성된

Mizi 그리드는 SVG용으로 제작되었습니다.

추천

출처blog.csdn.net/weixin_42938619/article/details/124014521