퍼즐로 건너뛰기
어느 날 어린 시절의 작은 퍼즐 장난감에 대한 기억이 떠올랐습니다. 슬라이더 퍼즐은 15개의 정사각형 타일을 프레임에 놓고 4 x 4 셀 배열로 하나의 여유 공간을 남겨두는 것이었습니다. 각 타일과 프레임의 가장자리에 있는 일련의 능선과 홈을 통해 타일이 프레임에 타일을 고정하는 동안 서로 미끄러져 지나갈 수 있습니다. 주어진 시간에 여유 공간에 인접한 모든 타일은 해당 공간으로 이동할 수 있으며, 그렇지 않으면 타일이 이동할 수 없습니다. 타일을 여유 공간으로 이동하면 타일이 있던 자리에 새로운 여유 공간이 남고 다른 타일이 해당 새 공간으로 이동할 수 있습니다. 이러한 방식으로 타일을 반복적으로 밀어 타일을 미리 정해진 순서로 배열하는 것이 아이디어입니다.
분명히 이것은 "15 퍼즐"이라고 불리며 1870년대부터 존재해 왔습니다. 웹을 검색하면 다양한 프로그래밍 언어로 작성된 여러 가지 레크리에이션이 반환되며 실제로 https://dev.to/artydev/let-us-code-a-sliding-puzzle-9n을 포함하여 여기 dev.to에 여러 기사가 있습니다. , https://dev.to/xzanderzone/making-a-slider-puzzle-in-java-script-83m 및 https://dev.to/claurcia/slide-puzzle-5c55 모두 JavaScript 및 https:/로 제공됩니다. /dev.to/mfbmina/building-a-sliding-puzzle-with-go-3bnj Go. 또한 JavaScript를 배우는 사람들에게 좋은 시작 과제로도 제시됩니다.
하지만 내 관심을 불러일으킨 것은 프로그래밍 언어를 전혀 사용하지 않고 웹에서 다시 만들 수 있어야 한다는 생각이었습니다! 즉, 순수한 HTML과 CSS만을 사용하여 구현한 것입니다. 그래서 아래에 소개합니다. 제가 타협해야 했던 한 가지 점은 제공된 10개의 게임이 미리 섞인 시작 위치를 고정했다는 것입니다.
이를 위해 미리 정해진 순서는 완성된 사진을 보여주는 것입니다.
이 구현의 기본 원칙은 각 타일이 프레임 내 위치에 대한 상태 기록을 유지한다는 것입니다. HTML과 CSS에서 상태를 변경하고 유지하는 방법은 많지 않지만 가장 일반적인 것은 '체크박스 해킹'이며 이 구현에서는 이를 많이 활용합니다. 체크박스 해킹에 익숙하지 않은 분들을 위해
따라서 각 타일에는 각각 4개의 라디오 버튼이 있는 한 쌍의 라디오 버튼 그룹이 있습니다. 이러한 그룹 중 하나는 X축의 타일 위치를 유지하고 다른 그룹은 Y축의 타일 위치를 유지합니다. (또는 원하는 경우 각각 수평 위치와 수직 위치.) 처음에는 15개의 타일에 각각 라디오 버튼을 통해 서로 다른 X 및 Y 좌표 조합이 지정되어 각 타일이 프레임에서 서로 다른 셀을 차지합니다.
타일은 처음에 프레임의 왼쪽 상단 셀에 배치된 다음 변환 변환을 적용하여 라디오 버튼의 상태를 측정하는 CSS를 통해 프레임 내에서 이동됩니다.
/* "X" refers to the X-axis cell positions, "Y" to the Y-axis cell positions. * 0, 1, 2, 3 refers to the position on that axis, * 0 = left and top, 3 = right and bottom respectively. */ .tile:has(.X0:checked~.Y0:checked) { transform: translate(0%, 0%); } .tile:has(.X0:checked~.Y1:checked) { transform: translate(0%, 100%); } .tile:has(.X0:checked~.Y2:checked) { transform: translate(0%, 200%); } .tile:has(.X0:checked~.Y3:checked) { transform: translate(0%, 300%); } .tile:has(.X1:checked~.Y0:checked) { transform: translate(100%, 0%); } /* and so on for the remainder of the sixteen combinations */
그러면 타일에는 8개의 라디오 버튼에 해당하는 8개의 레이블 요소도 포함됩니다. 각 레이블은 서로 겹쳐지는 절대 위치에 있으며 각 레이블은 타일을 완전히 채웁니다. 레이블은 투명하며 처음에는 모든 레이블에 포인터 이벤트:없음을 설정하여 클릭 및 탭에 응답하지 않도록 설정됩니다.
다음 단계는 CSS 선택기가 빈 셀의 위치를 식별하는 것입니다. 이는 제거에 의해 수행되며 X, Y 좌표가 15개 타일 중 라디오 버튼 그룹 쌍으로 표시되지 않는 셀입니다.
예를 들어 다음과 일치하는 경우:
.frame:not(:has(.tile .X0:checked~.Y0:checked)) { .... }
그러면 빈 셀은 현재 왼쪽 상단 셀에 있어야 합니다. 16개의 셀 각각에 대해 이 과정을 반복하면 정확히 하나의 셀이 일치합니다.
이 작업이 완료되면 빈 셀에 인접한 셀을 식별할 수 있습니다. 빈 셀이 모서리에 있으면 해당 셀로 이동할 수 있는 타일은 정확히 두 개입니다. 그렇지 않고 빈 셀이 프레임 측면 중 하나에 있으면 셀로 이동할 수 있는 타일은 세 개입니다. 그렇지 않으면 빈 셀입니다. 셀은 4개의 중간 셀 중 하나여야 하며, 해당 셀로 이동할 수 있는 4개의 타일이 있습니다. 각 타일에 대해 타일의 8개 라벨 중 정확히 하나가 타일을 빈 셀로 이동하는 데 필요한 올바른 라디오 버튼을 활성화합니다. 해당 레이블은 포인터 이벤트 값을 다시 자동으로 설정하여 활성화됩니다. 예를 들어:
/* Top, left corner */ .frame:not(:has(.tile .X0:checked ~ .Y0:checked)) { :is( .tile:has(.X0:checked ~ .Y1:checked) label.Y0, .tile:has(.X1:checked ~ .Y0:checked) label.X0 ) { pointer-events: auto; } } /* right most cell of row two */ .frame:not(:has(.tile .X1:checked ~ .Y3:checked)) { :is( .tile:has(.X1:checked ~ .Y2:checked) label.Y3, .tile:has(.X0:checked ~ .Y3:checked) label.X1, .tile:has(.X2:checked ~ .Y3:checked) label.X1 ) { pointer-events: auto; } } /* second cell from left on row three */ .frame:not(:has(.tile .X2:checked ~ .Y1:checked)) { :is( .tile:has(.X2:checked ~ .Y0:checked) label.Y1, .tile:has(.X2:checked ~ .Y2:checked) label.Y1, .tile:has(.X1:checked ~ .Y1:checked) label.X2, .tile:has(.X3:checked ~ .Y1:checked) label.X2 ) { pointer-events: auto; } }
The last step of the game is to identify when the puzzle is solved. This is simply a case of checking that the 15 tiles all have their expected X and Y axis radio buttons set to their "solved" position.
/* Each tile is assigned a letter "a" to "o". * The puzzle is solved when the tiles are in alphabetical order * reading left to right and top to bottom */ .frame:has(.a .X0:checked ~ .Y0:checked):has(.b .X1:checked ~ .Y0:checked):has( .c .X2:checked ~ .Y0:checked ):has(.d .X3:checked ~ .Y0:checked):has(.e .X0:checked ~ .Y1:checked):has( .f .X1:checked ~ .Y1:checked ):has(.g .X2:checked ~ .Y1:checked):has(.h .X3:checked ~ .Y1:checked):has( .i .X0:checked ~ .Y2:checked ):has(.j .X1:checked ~ .Y2:checked):has(.k .X2:checked ~ .Y2:checked):has( .l .X3:checked ~ .Y2:checked ):has(.m .X0:checked ~ .Y3:checked):has(.n .X1:checked ~ .Y3:checked):has( .o .X2:checked ~ .Y3:checked ) ~ .options .success { display: block; }
The rest is cosmetic. The sliding is done with a simple transition of the transform described above
.tile { transition: 0.5s transform; @media (prefers-reduced-motion) { transition: none; } }
and each tile shows a portion of the game's image using background-size and background-position
.tile { background-size: 400%; } #board1 .tile { background-image: url("https://alohci.net/image/dev.to/slidergame/mullermarc-k7bQqdUf954-unsplash.webp"); } .a { background-position: 0% 0%; } .b { background-position: 33.333% 0%; } .c { background-position: 66.667% 0%; } .d { background-position: 100% 0%; } .e { background-position: 0% 33.333%; } /* and so on for the remaining tiles */
and there's a single set of radio buttons to choose which of the ten games to play.
To play the game, simply click or tap on the tile you want to slide to the empty cell.
I've also provided a "barebones" mode to show the tile letters and radio buttons which might help with the understanding of how the HTML and CSS works.
So here's the completed puzzle game. Please let me know any feedback you have.
위 내용은 HTML 및 CSS의 '퍼즐' 슬라이더 게임의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!