PINK ICE 是一款基于 HTML、CSS 和 JavaScript 开发的响应式滑块解谜游戏,支持方向键或触屏操作,适配移动与桌面端,拥有多级挑战关卡与炫酷动画效果。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>粉色冰块闯关游戏</title>
<style>
@import url("https://fonts.geekzu.org/css2?family=Archivo+Black&display=swap");
html {
font-size: 3vh;
}
@media (orientation: portrait) {
html {
font-size: 2.9vw;
}
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
color: #fff;
background-color: #000038;
overflow: hidden;
font-family: "Archivo Black", sans-serif;
font-weight: 700;
font-size: 1.5rem;
}
.level {
position: absolute;
top: 0.75rem;
left: 1rem;
}
@media (min-height: 500px), (orientation: portrait) {
.level {
top: 0.5rem;
}
}
.refresh,
.fullscreen {
position: absolute;
padding: 0.2rem 0.6rem;
border: 1px solid #fff;
font-weight: 500;
background-color: #000038;
cursor: pointer;
text-transform: uppercase;
}
.refresh:hover,
.fullscreen:hover {
color: #000038;
background-color: #fff;
}
@media (min-height: 500px), (orientation: portrait) {
.refresh,
.fullscreen {
font-size: 1rem;
}
}
.refresh {
top: 0.5rem;
right: 1rem;
}
.fullscreen {
top: 0.5rem;
left: 4rem;
}
.controls {
position: absolute;
top: 0;
left: 0;
display: flex;
flex-wrap: wrap;
width: 100%;
height: 100%;
}
.controls__arrow {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.controls__arrow:after {
content: "";
width: 0;
height: 0;
margin: 1rem;
opacity: 0.3;
}
.controls__arrow--top,
.controls__arrow--bottom {
width: 100%;
height: 40vh;
}
.controls__arrow--left,
.controls__arrow--right {
flex-direction: column;
width: 50%;
height: 20vh;
}
.controls__arrow--top,
.controls__arrow--left {
align-items: flex-start;
}
.controls__arrow--bottom,
.controls__arrow--right {
align-items: flex-end;
}
.controls__arrow--top:after {
border-left: 1rem solid transparent;
border-right: 1rem solid transparent;
border-bottom: 1rem solid #fff;
}
.controls__arrow--left:after {
border-top: 1rem solid transparent;
border-right: 1rem solid #fff;
border-bottom: 1rem solid transparent;
}
.controls__arrow--right:after {
border-top: 1rem solid transparent;
border-left: 1rem solid #fff;
border-bottom: 1rem solid transparent;
}
.controls__arrow--bottom:after {
border-left: 1rem solid transparent;
border-right: 1rem solid transparent;
border-top: 1rem solid #fff;
}
.timer {
position: absolute;
top: 55%;
font-size: 3rem;
}
.title-screen {
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 56, 0.8);
}
.title-screen--hidden {
display: none;
}
.title-screen__title {
position: relative;
font-size: 12vw;
color: #fff;
}
.title-screen__word {
color: #eb80b1;
}
.title-screen__button {
position: relative;
border: 1px solid #fff;
color: #fff;
padding: 0.3rem 0.6rem;
background-color: transparent;
font-size: 3vw;
font-family: "Archivo Black", sans-serif;
text-transform: uppercase;
cursor: pointer;
}
.title-screen__button:hover {
color: #000038;
background-color: #fff;
}
@media (orientation: portrait) {
.title-screen__title {
font-size: 18vw;
}
.title-screen__button {
font-size: 4.5vw;
}
}
.game {
position: relative;
display: flex;
flex-wrap: wrap;
width: 29.03rem;
}
.game--tutorial:before {
position: absolute;
bottom: -4rem;
content: "使用键盘方向键(推荐)或点击箭头键开始游戏";
color: #fff;
font-size: 1.2rem;
font-weight: 500;
text-align: center;
text-transform: uppercase;
}
@media (max-width: 767px) {
.game--tutorial:before {
display: none;
}
}
.game--win:after,
.game--lose:after,
.game--final-win:after {
position: absolute;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
color: #fff;
background-color: rgba(0, 0, 56, 0.5);
font-size: 2rem;
text-transform: uppercase;
}
.game--win:after {
content: "You win! Next level";
}
.game--lose:after {
content: "You lose! Try again!";
}
.game--final-win:after {
content: "Game completed!";
}
.game__player {
position: absolute;
width: var(--cell);
height: var(--cell);
background-color: #eb80b1;
top: calc(var(--positionTop) * var(--cell));
left: calc(var(--positionLeft) * var(--cell));
transition-property: top, left;
transition-duration: 0.2s;
transition-timing-function: ease;
}
@media (max-width: 767px) {
.game__player {
transition-duration: 0.1s;
}
}
.game__cell {
width: var(--cell);
height: var(--cell);
background-color: #fff;
}
.game__cell--rock {
background: #000038;
}
.game__cell--lava {
background: linear-gradient(45deg, orange, #ff7d66 150%);
}
.game__cell--stop {
position: relative;
background: linear-gradient(45deg, #78d6c6 -50%, #fff 100%);
}
.game__cell--end {
background: #80c0a1;
}
</style>
</head>
<body>
<div id="game" class="game"></div>
<div id="controls" class="controls">
<div id="top" class="controls__arrow controls__arrow--top"></div>
<div id="left" class="controls__arrow controls__arrow--left"></div>
<div id="right" class="controls__arrow controls__arrow--right"></div>
<div id="bottom" class="controls__arrow controls__arrow--bottom"></div>
</div>
<div id="level-text" class="level">0</div>
<div id="refresh" class="refresh">重新开始</div>
<div id="timer" class="timer"></div>
<div id="title-screen" class="title-screen">
<div class="title-screen__title">
粉色 <span class="title-screen__word">冰块</span>
</div>
<button id="title-screen-button" class="title-screen__button">
开始游戏
</button>
</div>
<div id="fullscreen" class="fullscreen">全屏</div>
<script>
const game = document.getElementById("game");
const levelText = document.getElementById("level-text");
const refresh = document.getElementById("refresh");
const fullscreen = document.getElementById("fullscreen");
const topArrow = document.getElementById("top");
const leftArrow = document.getElementById("left");
const rightArrow = document.getElementById("right");
const bottomArrow = document.getElementById("bottom");
const timer = document.getElementById("timer");
const titleScreenButton = document.getElementById("title-screen-button");
const player = document.createElement("div");
player.classList.add("game__player");
let level = 0;
let width = 29;
const puzzles = [
["ox---", "---xe"],
["ox---", "---xe", "--xxx"],
["o--xx", "x--xe", "--x--", "-----", "-----"],
["e-xx-", "x--xx", "--xo-", "-x---", "-----"],
["xx--e", "---xx", "--xxx", "----x", "-x-ox"],
["ox--e", "-x-xx", "---xx", "--xxx", "x---x"],
["ox--x", "-x-xe", "---x-", "x----", "x---x"],
["ox---", "---xe", "--xxx", "---x-", "x----"],
["ox--x", "-----", "x--x-", "x--e-", "x---x"],
["ox---", "---x-", "x----", "---xx", "xexxx"],
["o-x--", "--x--", "x----", "x--ex", "x--xx"],
["ox--x", "---x-", "--e--", "x-x--", "x---x"],
["---x---", "-x----e", "--x----", "x------", "o-----x"],
["ox----x---", "--------xe", "-----x---x"],
[
"xxo---x---",
"--------x-",
"-----x----",
"--x-------",
"---------e",
"-x---x----",
],
["xollx---", "l-----xe"],
[
"xx----x--o",
"------x---",
"--------xx",
"--x----x--",
"---ll----e",
"xx-xx---x-",
],
[
"xx----x--o",
"------x---",
"---x----xx",
"--x----x--",
"--x-xx----",
"---x----xx",
"--x-------",
"----xlll--",
"xx-xxlllxe",
],
[
"xx----x--o",
"------x--x",
"-lxlx-----",
"-llx--x-xx",
"-lx-------",
"-llx----xx",
"-lxx---x--",
"----x-----",
"xxe-x---x-",
],
[
"xx----x---",
"------x--x",
"--x-x-----",
"x--x--x-xx",
"l-xx------",
"l--xlll--x",
"l-xxlxlx--",
"l---xe--x-",
"xx------xo",
],
[
"xx----x---",
"---l--x--x",
"---l------",
"x--x--x-x-",
"-----x--x-",
"--ex-x----",
"--x-----x-",
"----------",
"xx-------o",
],
[
"xx----x---",
"---l--x--x",
"---l----l-",
"------x-x-",
"-----x--x-",
"--lx-x----",
"--x-----x-",
"-l---xx---",
"ex-------o",
],
["xx----xxll", "---x----lx", "-lxx-lx--o", "-x---lx---", "--elxxx--x"],
[
"xx----xxll",
"ll-x----lx",
"ll-l-xx-lx",
"ll-l-ll-lx",
"---x----lx",
"-lxx-xl--o",
"-xlllex---",
"------x--x",
],
[
"xx----llll",
"------exlx",
"-l-l-xxxlx",
"-l-l-ll-xx",
"---x-----x",
"-lxx-xl--o",
"-xll-lxx--",
"------x--x",
],
[
"xx--------",
"--llx--xl-",
"-x----xlx-",
"-l---x-xx-",
"-l-x------",
"---x---x-x",
"-lxx-xl--o",
"exll-lxx--",
"x--------x",
],
["o---s---", "--------", "---xe--s", "--------"],
[
"o--xxx----",
"---xxx-x--",
"-------e--",
"-----x-s--",
"---xx----x",
"x------xxx",
],
[
"o-------s-",
"-x-xxx-x--",
"---------e",
"-----x----",
"---xx----x",
"x------xxx",
],
[
"o---------",
"---xxx-x--",
"-s-s--slse",
"-----x----",
"---xx----x",
"x-----xxxx",
],
[
"o---------",
"------s-s-",
"-s-s------",
"-------ss-",
"---s-ss---",
"-s-----e--",
],
[
"o---------",
"------s-s-",
"-s-s------",
"--------s-",
"----s--s--",
"----s---s-",
"---s-ss---",
"-s-----e--",
],
[
"o------e--",
"--lll-s-s-",
"-s-s------",
"---llll-s-",
"----s--s--",
"--lls---ss",
"---s-ssll-",
"-s--------",
],
[
"o-xxx---x-",
"--lll-s-s-",
"-s-s---x--",
"xx-llll-s-",
"----sxxx--",
"--lls---ss",
"---sxxxll-",
"-s-s---ex-",
],
[
"--xxx---x-",
"--lll-s-s-",
"-s-s---xll",
"-l-llx--s-",
"---lsxxx--",
"--lls---ss",
"---sxxxll-",
"xs-s---exo",
],
[
"-s--x---x-",
"e--ll-s-s-",
"x--s---xll",
"-l-l-xlll-",
"----sxxx--",
"-l-ls---ss",
"--s-xxxll-",
"xs-s----lo",
],
[
"es--l-----",
"xxx---s---",
"ol-ls---ss",
"--s-xx-ll-",
"xs-s----l-",
"x--s---xl-",
"-l-l-xlll-",
"----s-----",
],
["-x-e---xxx", "-x--xlsllx", "s------x--", "-xs-xl-l--", "---------o"],
[
"lllls--x--",
"sxxx-s-x--",
"-x--slell-",
"-x--xlx-l-",
"s--------s",
"-xsxxx-ls-",
"-xs-xlxx--",
"-xs-xlol--",
"--slll---x",
],
[
"s-e-s-x-s-",
"x--lx-slx-",
"sss-ls--ss",
"x-x-x-x-x-",
"-sl-s-l-s-",
"-x--llo-ss",
"-xs-xlxx--",
"s--s--s--s",
"x--s--x--s",
],
[
"x-l-l-lxe-",
"------xlx-",
"-l--l--l--",
"-lx-x--xx-",
"-x--xx--l-",
"----ll----",
"x--x--x--l",
"-l-l---l-l",
"o--xl---xl",
],
[
"ss-ss-s--s",
"o--s----s-",
"-s--s--s--",
"--ss--s---",
"-s--ss--s-",
"-------s-e",
"s--s--s--s",
"----s----s",
"s--s--s--s",
],
[
"s-sss-s-ss",
"---s--ss-s",
"-----s-s--",
"s-ss--s---",
"-s--s---s-",
"-------s--",
"s--s--s--s",
"-s--s-s--s",
"s--soe----",
],
[
"s-s---s--s",
"o--s--ss-s",
"-s---s-s--",
"s-ss--s---",
"-s------s-",
"--s-s--s--",
"s--s--s--s",
"-s--s-s--s",
"s--s----e-",
],
[
"-s--sll--s",
"---sllss-s",
"-s---s----",
"s-ss--s-ll",
"-s-ll--s-l",
"--s-s--s--",
"sl-s--s--s",
"-s--s-s--s",
"s--s-eloll",
],
[
"-s-------s",
"x--sllss-s",
"lsllls-xx-",
"l-ss--s-ll",
"-sxx-----l",
"-xxll--s--",
"---sl-s--s",
"--x-l-s--s",
"--ls-xl-ll",
"-sl--ex--o",
],
[
"os-xx------s--",
"x--sllssxx---x",
"lsllls-xx-s-xx",
"l-ss--s-ll-xx-",
"-s-l--xx---lxx",
"-x-ll--s--xx-l",
"-x---sl-s--ll-",
"---xx-l-s--s-e",
],
[
"o---e-------------",
"xxxx-xxxxxxxxxxxx-",
"----------------x-",
"-xxx-xxxxxxxxxx-x-",
"------------------",
"xxxx------------xx",
],
[
"xxxx-xxxxxxxxxxxxx",
"e-----------------",
"xxxx-xxxxxxxx-xxx-",
"o-----------------",
"xxxx-xxxxxxxx-xxx-",
"----------------x-",
"-xxx-xxxxxxxx-x-x-",
"------------------",
"xxxx------------xx",
"xxxx-xxxxxxxx-xxxx",
"xxxx----------xxxx",
],
[
"xxxx----------xxxx",
"xxxx-xxxx-xxx-xxxx",
"------------------",
"xxxx-xxxx-xxx-xxx-",
"o-----------------",
"xxxx-xxxx-xxx-x-x-",
"----------xxx-x-x-",
"-xxx-xxxx-xxx-x-x-",
"------------------",
"xxxx------------xx",
"xxxx-xxxx-xxx-xxxx",
"e-------------xxxx",
"xxxx------xxxxxxxx",
],
[
"xxxx----xx----xxxx",
"xxox-xx-xx-xx-xxxx",
"e----------xx-----",
"xx-x-xxxxx-xx-xxx-",
"------------------",
"-x-x-xx-xx-xx-x-x-",
"-----------xx-x-x-",
"-x-x-xx-xx-xx-x-x-",
"------------------",
"xx--------------x-",
"xxxx-xxxxx-xx-xxx-",
"--------------xxx-",
"xxxx--------------",
],
[
"xxxx----xx----xxxx",
"xxox-xx-xx-xx-xxxx",
"e----------xx-----",
"-x-x-xx-xx-xx-x-x-",
"-------------s----",
"xx--------------x-",
"xxxx-xxxxx-xx-xxx-",
"--------------xxx-",
"xxxx--------------",
],
["oe"],
];
let initialPosition = [];
let position = [];
let end = [];
let rocks = [];
var start = Date.now();
const buildLevel = () => {
if (level === 0) {
game.classList.add("game--tutorial");
} else {
game.classList.remove("game--tutorial");
}
document.body.style = `--cell: ${width / puzzles[level][0].length}rem`;
rocks = [];
puzzles[level].forEach((row, rowIndex) => {
const cells = row.split("");
cells.forEach((cell, cellIndex) => {
const newDiv = document.createElement("div");
newDiv.classList.add("game__cell");
if (cell === "o") {
initialPosition = [cellIndex, rowIndex];
position = [cellIndex, rowIndex];
} else if (cell === "x") {
rocks.push([cellIndex, rowIndex, "rock"]);
newDiv.classList.add("game__cell--rock");
} else if (cell === "l") {
rocks.push([cellIndex, rowIndex, "lava"]);
newDiv.classList.add("game__cell--lava");
} else if (cell === "s") {
rocks.push([cellIndex, rowIndex, "stop"]);
newDiv.classList.add("game__cell--stop");
} else if (cell === "e") {
end = [cellIndex, rowIndex];
newDiv.classList.add("game__cell--end");
}
game.appendChild(newDiv);
});
});
game.appendChild(player);
};
const positionPlayer = () => {
player.style = `--positionLeft: ${position[0]}; --positionTop: ${position[1]};`;
};
const removeEvent = (e) => {
e.preventDefault();
e.stopPropagation();
};
const winLevel = () => {
game.classList.add("game--win");
document.addEventListener("keydown", removeEvent);
removeArrowsEvents();
setTimeout(() => {
game.innerHTML = "";
game.classList.remove("game--win");
level = level + 1;
levelText.innerHTML = level;
buildLevel();
positionPlayer();
document.removeEventListener("keydown", removeEvent);
registerArrows();
}, 500);
};
const loseLevel = () => {
game.classList.add("game--lose");
document.addEventListener("keydown", removeEvent);
removeArrowsEvents();
setTimeout(() => {
game.classList.remove("game--lose");
position = [...initialPosition];
positionPlayer();
document.removeEventListener("keydown", removeEvent);
registerArrows();
}, 500);
};
const nextLevel = () => {
if (position[0] === end[0] && position[1] === end[1]) {
if (level === puzzles.length - 1) {
const s = (Date.now() - start) / 1000;
timer.innerHTML = `${parseInt(s / 60 / 60)}h ${parseInt(
(s / 60) % 60
)}m ${parseInt(s % 60)}s`;
game.classList.add("game--final-win");
} else {
winLevel();
}
}
};
const movePlayer = (axis, perpendicularAxis, movingForward) => {
const relevantRocks = rocks
.filter((rock) => rock[axis] === position[axis])
.filter((rock) =>
movingForward
? rock[perpendicularAxis] > position[perpendicularAxis]
: rock[perpendicularAxis] < position[perpendicularAxis]
);
if (relevantRocks.length) {
const minmax = movingForward
? Math.min(...relevantRocks.map((rock) => rock[perpendicularAxis]))
: Math.max(...relevantRocks.map((rock) => rock[perpendicularAxis]));
const relevantRock = relevantRocks.filter(
(rock) => rock[perpendicularAxis] === minmax
)[0];
if (relevantRock[2] === "rock") {
position[perpendicularAxis] = movingForward
? minmax - 1
: minmax + 1;
} else {
position[perpendicularAxis] = minmax;
if (relevantRock[2] === "lava") {
loseLevel();
}
}
} else {
const maxCells =
axis === 1
? puzzles[level][position[1]].length
: puzzles[level].length;
position[perpendicularAxis] = movingForward ? maxCells - 1 : 0;
}
positionPlayer();
nextLevel();
};
const topFunction = () => movePlayer(0, 1, false);
const leftFunction = () => movePlayer(1, 0, false);
const rightFunction = () => movePlayer(1, 0, true);
const bottomFunction = () => movePlayer(0, 1, true);
const registerArrows = () => {
topArrow.addEventListener("click", topFunction);
leftArrow.addEventListener("click", leftFunction);
rightArrow.addEventListener("click", rightFunction);
bottomArrow.addEventListener("click", bottomFunction);
};
const removeArrowsEvents = () => {
topArrow.removeEventListener("click", topFunction);
leftArrow.removeEventListener("click", leftFunction);
rightArrow.removeEventListener("click", rightFunction);
bottomArrow.removeEventListener("click", bottomFunction);
};
buildLevel();
positionPlayer();
registerArrows();
window.addEventListener("keydown", (event) => {
if (event.key === "ArrowRight") {
rightFunction();
} else if (event.key === "ArrowLeft") {
leftFunction();
} else if (event.key === "ArrowDown") {
bottomFunction();
} else if (event.key === "ArrowUp") {
topFunction();
}
});
refresh.addEventListener("click", () => {
position = [...initialPosition];
positionPlayer();
});
fullscreen.addEventListener("click", () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
} else if (document.exitFullscreen) {
document.exitFullscreen();
}
});
titleScreenButton.addEventListener("click", () => {
document
.getElementById("title-screen")
.classList.add("title-screen--hidden");
});
</script>
</body>
</html>
暂无评论
要发表评论,您必须先 登录