중국 체스판 그리기 - CSS 의사 요소 사용
체스와 비교:
체스판을 그리는 것은 약간 더 어렵습니다.
주된 이유는 체스 이동이 그리드에 있고 체스 이동이 교차점에 있기 때문입니다. 따라서 체스의 레이아웃 background-color: #fff
은 background-color: #aaa
홀수 및 짝수 그리드를 사용하고 표시할 수 있습니다. 이것은 CSS에서도 제공합니다. nth-child()
이 의사 클래스는 작업을 완료하는 데 편리하고 효율적일 수 있지만 중국 체스의 처리는 비교적 번거롭습니다.
그러나 몇 가지 검색 후 체스판을 그리는 비교적 간단한 방법이 있음을 발견했습니다. 즉 before
, after
이 두 의사 요소를 통해 최종 결과는 다음과 같습니다.
빈 체스판은 다음과 같습니다.
구체적인 구현은 다음과 같습니다.
-
상위 구성 요소는 바둑판 배열을 관리합니다.
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에 배치될 수 있음) , 조각의 데이터가 하위 컴포넌트로 전달됩니다.
자식 구성 요소 before
는 after
두 개의 유사 선택기를 통해 수직 및 수평 경계를 생성합니다. 세부 정보는 다음과 같습니다.
border-top
여기서 , border-bottom
, border-right
, 를 사용 border-left
하는 것의 차이는 그리 크지 않습니다. 하나는 수평이고 다른 하나는 수직인 한, 그 차이는 위치 조정에 불과하며 마지막으로 숨겨야 하는 rowNum과 colNum입니다. 조건부 컨트롤 없이 렌더링된 모든before
체스판은 다음과 같습니다.after
여기서 숨길 필요가 있는 것은 체스판의 맨 오른쪽에 있는 것과 border-top
체스판의 맨 아래에 있는 것 border-right
입니다. 물론 초강과 한경계 사이의 공간이 여전히 필요하기 때문에 초강과 한경계도 확보col = 0
해야 한다.col = 8
border-right
여기서 1차원 배열을 사용하는데 조건의 제어가 조금 더 번거롭겠지만 2차원 배열 row
이라면 col
와 로 직접 비교할 수 있다.
일반적으로 이번에 우연히 발견한 유사 요소의 사용은 고정된 사고에서 벗어나는 것이기도 합니다. 나중에 다른 유사한 요구 사항이 있으면 의사 요소를 완전히 전달할 수 있습니다.첫 번째, 이벤트 핸들러의 처리가 훨씬 더 편리할 것이고(refs가 캔버스를 사용하기 위해 필요함), 두 번째로 훨씬 적은 코드를 작성할 것입니다. (캔버스 그리기는 사실 상당히 번거롭다고 들었습니다. ), 셋째, 모바일 단말 지원(이미지 커팅 사용에 비해 캔버스 모바일 단말은 그다지 편리하지 않고 비교할 수 없음)도 훨씬 좋습니다.
그건 그렇고 이동단말기의 효과를 사진으로 몇장 첨부하겠습니다.물론 그리드의 크기, 패딩, 여백의 조정이 필요하지만 이것도 2~3줄의 코드를 수정하는 문제이기도 합니다. CSS 또는 상수.
완성된 사진 추가:
Mizi 그리드는 SVG용으로 제작되었습니다.