<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>雪球跳跃闯关游戏</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&display=swap');
html {
font-size: 2.8vh;
}
@media screen and (orientation: portrait) {
html {
font-size: 2.8vw;
}
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
color: #000038;
font-family: 'Archivo Black', 'Arial Black', sans-serif;
font-size: 1.5rem;
perspective: 100rem;
transform-style: preserve-3d;
background: linear-gradient(white, #919ea3 400%);
overflow: hidden;
}
.snow-game__level-marker {
position: absolute;
z-index: 1;
top: 1.1rem;
left: 1rem;
}
.snow-game__button {
background: white;
border: 1px solid grey;
border-radius: 1rem;
padding: 0.5rem 1rem;
text-transform: uppercase;
font-family: 'Archivo Black', 'Arial Black', sans-serif;
font-size: 1.5rem;
cursor: pointer;
}
.snow-game__fullscreen-button {
position: absolute;
z-index: 1;
top: 0.5rem;
left: 4rem;
}
.snow-game__wrapper {
position: relative;
width: 30rem;
height: 35rem;
perspective: 100rem;
}
.snow-game__level {
position: absolute;
left: 0;
top: calc(var(--time) * var(--cell) * -1);
display: flex;
flex-wrap: wrap;
height: 100%;
transform: rotate3d(1, 0, 0, 45deg) translateZ(
calc(var(--time) * -0.5 * var(--cell))
)
translateY(calc(var(--time) * -0.1 * var(--cell)));
transform-style: preserve-3d;
transition: top 0.3s linear, transform 0.3s linear;
}
.snow-game__cell {
position: relative;
width: var(--cell);
height: var(--cell);
}
.snow-game__cell--rock {
transform: rotate3d(1, 0, 0, -45deg);
transform-origin: bottom;
}
.snow-game__cell--rock:before {
content: '';
position: absolute;
bottom: 0;
left: 10%;
width: 80%;
height: 60%;
border-radius: 3rem 3rem 1rem 1rem;
background: radial-gradient(circle at 30% 80%, white -200%, #919ea3);
box-shadow: 0 0.5rem 1rem 0 rgba(145, 158, 163, 0.5);
}
.snow-game__cell--rock:nth-child(2n):before {
border-radius: 3rem 1rem 1rem 1rem;
}
.snow-game__cell--rock:nth-child(3n):before {
border-radius: 1rem 5rem 1rem 1rem;
}
.snow-game__cell--rock:nth-child(4n):before {
height: 50%;
}
.snow-game__cell--rock:nth-child(5n):before {
height: 70%;
}
.snow-game__cell--lava:before {
content: '';
position: absolute;
top: 40%;
left: 0;
width: 100%;
height: 60%;
background: linear-gradient(180deg, orange, #ff7d66);
background-size: 100% 200%;
animation: lava;
animation-duration: 2s;
animation-iteration-count: infinite;
}
@keyframes lava {
0%,
100% {
background-position: 0 0;
}
50% {
background-position: 0 100%;
}
}
.snow-game__cell--end {
background-image: linear-gradient(45deg, #000038 25%, transparent 25%),
linear-gradient(-45deg, #000038 25%, transparent 25%), linear-gradient(
45deg,
transparent 75%,
#000038 75%
), linear-gradient(-45deg, transparent 75%, #000038 75%);
background-size: var(--cell) var(--cell);
background-position: 0 0, 0 calc(var(--cell) / 2),
calc(var(--cell) / 2) calc(var(--cell) / -2), calc(var(--cell) / -2) 0px;
}
.snow-game__player {
position: absolute;
top: calc(
var(--top) * var(--cell) + (var(--cell) * 0.5) - var(--lifes) * 1rem
);
left: calc(
var(--left) * var(--cell) + (var(--cell) * 0.5) - var(--lifes) * 1rem
);
width: calc(var(--lifes) * 2rem);
height: calc(var(--lifes) * 2rem);
border-radius: 50%;
background: radial-gradient(circle at 40% 70%, white -200%, #eb80b1);
transform: rotate3d(1, 0, 0, -45deg);
transform-origin: bottom;
transition: top 0.3s linear, left 0.5s ease, width 0.5s ease, height
0.5s ease, transform 0.3s cubic-bezier(0.17, 0.67, 0.54, 1), box-shadow
0.3s cubic-bezier(0.17, 0.67, 0.54, 1);
box-shadow: 0 0.5rem 1rem 0 rgba(145, 158, 163, 0.5);
}
.snow-game__player--jump {
transform: rotate3d(1, 0, 0, -45deg) translateZ(var(--cell)) translateY(calc(var(
--cell
) * -1));
box-shadow: 0 var(--cell) 1rem 0 rgba(145, 158, 163, 0.5);
}
.snow-game__start,
.snow-game__score {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 1rem;
background-color: rgba(255, 255, 255, 0.6);
cursor: default;
}
.snow-game__start--hidden,
.snow-game__score--hidden {
display: none;
}
.snow-game__title {
font-size: 10vw;
}
.snow-game__title--highlighted {
color: #eb80b1;
}
.snow-game__tutorial {
font-size: 2rem;
margin-bottom: 1rem;
text-align: center;
}
.snow-game__tutorial-mobile {
display: none;
}
.snow-game__score {
font-size: 10vw;
text-transform: uppercase;
}
.snow-game__click-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
@media screen and (max-width: 767px) {
.snow-game__tutorial {
font-size: 1.5rem;
}
}
</style>
</head>
<body>
<div id="snow-game-level-marker" class="snow-game__level-marker"></div>
<div id="snow-game" class="snow-game__wrapper">
<div id="snow-game-level" class="snow-game__level"></div>
<div class="snow-game__snowball"></div>
</div>
<div id="click-layer" class="snow-game__click-layer"></div>
<div id="start" class="snow-game__start">
<div class="snow-game__title">
PINK <span class="snow-game__title--highlighted">SNOW</span>
</div>
<div class="snow-game__tutorial">
Use ⇦ ⇨ to move, ⇧ to jump
</div>
<button class="snow-game__start-button snow-game__button">Play</button>
</div>
<button
id="fullscreen"
class="snow-game__fullscreen-button snow-game__button"
>
Fullscreen
</button>
<div id="score" class="snow-game__score snow-game__score--hidden"></div>
<script>
const snowGame = document.getElementById('snow-game');
const snowGameLevel = document.getElementById('snow-game-level');
const snowGameLevelMarker = document.getElementById(
'snow-game-level-marker'
);
const clickLayer = document.getElementById('click-layer');
const startScreen = document.getElementById('start');
const scoreScreen = document.getElementById('score');
const fullscreen = document.getElementById('fullscreen');
const rhythm = 300;
let width = 30;
let lifes = 2;
let z = 0;
let position = [];
let time = -1;
let currentLevel = 0;
let score = 0;
let rocks = [];
const levels = [
[
'--o--',
'--',
'--',
'',
'-',
'--',
'-',
'--',
'--',
'--x-x',
'--',
'',
'xx',
'--',
'xxxxx',
'--',
'xxx--',
'--',
'--',
'xxxxx',
'--xx-',
'x--xx',
'',
'xx',
'--xx-',
'--',
'-',
'--',
'--',
'xxxxx',
'--xx-',
'x--xx',
'',
'xxxx',
'--xxx',
'-xxl-',
'xxxx-',
'xx',
'x--ll',
'--',
'll',
'xx--x',
'--',
'xxx--',
'll--',
'--xxx',
'--',
'-xxxx',
'xlxx',
'eeeee',
],
[
'--o--',
'--',
'xx--x',
'll-x',
'x--xx',
'-x',
'--xxx',
'--llx',
'--xxx',
'x',
'x--x-',
'--x-x',
'eeeee',
],
[
'--o--',
'--',
'xx--x',
'll-x',
'x--xx',
'-x',
'xxxxx',
'--llx',
'--xxx',
'--',
'',
'll',
'x--ll',
'-x',
'xxlll',
'--',
'--',
'x-llx',
'x--x-',
'--l-x',
'eeeee',
],
[
'--o--',
'',
'll',
'x--ll',
'--xxx',
'x-x',
'xxl--',
'--',
'--',
'x--',
'--',
'x',
'--xxx',
'-xxxx',
'xx',
'xxl--',
'',
'--',
'--',
'llll-',
'x--',
'--',
'-x-x-',
'x-x-x',
'-x-x-',
'-x-x-',
'x-x-x',
'-x-x-',
'-x-x-',
'x-x-x',
'-x-x-',
'--',
'--',
'--',
'-x-xl',
'x-xlx',
'-x-x-',
'-x-x-',
'x-x-x',
'lx-x-',
'lx-x-',
'xlx-x',
'lx-x-',
'lll',
'--',
'--',
'xx-',
'lx-x-',
'xlxl',
'x-x-x',
'll',
'eeeee',
],
[
'--o--',
'--',
'll',
'--', '',
'--l--',
'l--l-',
'-l--l',
'--l--',
'-l',
'eeeee',
],
[
'l-o-x',
'-l-x-',
'--l--',
'lx-l-',
'xl--l',
'--l-x',
'--',
'--',
'xxxxx',
'--',
'xxxxx',
'--',
'--',
'lllll',
'--',
'lllll',
'--',
'eeeee',
],
[
'--o--',
'--',
'-----',
'xxxxx',
'-xxxx',
'x--xx',
'--xxx',
'llll-',
'x-xxx',
'llll-',
'x-xxx',
'-xx--',
'x-xxx',
'-xx--',
'x-xxx',
'-xx--',
'eeeee',
],
[
'xxoxx',
'll-ll',
'xx-xx',
'll-ll',
'xx-xx',
'll-ll',
'xx-xx',
'll-ll',
'xx-xx',
'll-ll',
'xx-xx',
'll-ll',
'xx-xx',
'll-ll',
'xx-xx',
'll-ll',
'xx-xx',
'll-ll',
'eeeee',
],
];
let intervalId;
const player = document.createElement('div');
player.classList.add('snow-game__player');
const positionPlayer = (x, y) => {
position = [x, y];
setPlayerData();
};
const setPlayerData = () => {
player.style = `--top: ${position[1]}; --left: ${position[0]}; --lifes: ${lifes}`;
};
const buildLevel = () => {
clearInterval(intervalId);
if (currentLevel === levels.length) {
scoreScreen.classList.remove('snow-game__score--hidden');
scoreScreen.innerHTML = `Score: ${score}`;
return;
}
snowGameLevel.innerHTML = '';
lifes = 2;
time = -1;
rocks = [];
snowGameLevelMarker.innerHTML = currentLevel;
const level = levels[currentLevel];
document.body.style = `--cell: ${width / level[0].length}rem`;
level.forEach((row, rowIndex) => {
const cells = row.split('');
cells.forEach((cell, cellIndex) => {
const newDiv = document.createElement('div');
newDiv.classList.add('snow-game__cell');
if (cell === 'o') {
positionPlayer(cellIndex, rowIndex);
} else if (cell === 'x') {
rocks.push([cellIndex, rowIndex, 'rock']);
newDiv.classList.add('snow-game__cell--rock');
} else if (cell === 'l') {
rocks.push([cellIndex, rowIndex, 'lava']);
newDiv.classList.add('snow-game__cell--lava');
} else if (cell === 'e') {
newDiv.classList.add('snow-game__cell--end');
}
snowGameLevel.appendChild(newDiv);
});
snowGameLevel.appendChild(player);
});
intervalId = setInterval(() => {
time++;
snowGame.style = `--time: ${time};`;
positionPlayer(position[0], position[1] + 1);
const collisions = rocks.filter(
(rock) => rock[0] === position[0] && rock[1] === position[1] - 1
);
if (lifes <= 0) {
buildLevel();
} else if (position[1] === levels[currentLevel].length) {
currentLevel = currentLevel + 1;
score = score + lifes;
buildLevel();
} else if (collisions.length && z === 0) {
if (collisions[0][2] === 'rock') {
lifes = lifes - 1;
} else if (collisions[0][2] === 'lava') {
lifes = lifes - 2;
}
setPlayerData();
}
}, rhythm);
};
buildLevel();
const jump = () => {
if (z === 0) {
z = 1;
player.classList.add('snow-game__player--jump');
setTimeout(() => {
setTimeout(() => (z = 0), rhythm);
player.classList.remove('snow-game__player--jump');
}, rhythm);
}
};
window.addEventListener('keydown', (event) => {
if (event.key === 'ArrowRight') {
positionPlayer(
Math.min(position[0] + 1, levels[currentLevel][0].length - 1),
position[1]
);
} else if (event.key === 'ArrowLeft') {
positionPlayer(Math.max(position[0] - 1, 0), position[1]);
} else if (event.key === 'ArrowUp') {
jump();
}
});
fullscreen.addEventListener('click', () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
} else if (document.exitFullscreen) {
document.exitFullscreen();
}
});
startScreen.addEventListener('click', () => {
startScreen.classList.add('snow-game__start--hidden');
});
clickLayer.addEventListener('click', (event) => {
if (event.clientY < window.innerHeight / 3) {
jump();
} else if (event.clientX < window.innerWidth / 3) {
positionPlayer(Math.max(position[0] - 1, 0), position[1]);
} else if (event.clientX > (window.innerWidth * 2) / 3) {
positionPlayer(
Math.min(position[0] + 1, levels[currentLevel][0].length - 1),
position[1]
);
}
});
</script>
</body>
</html>