Арбузный молоток
by GoldenBear87744 lines10.8 KB
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Арбузный молоток</title>
<style>
*{
box-sizing:border-box;
user-select:none;
-webkit-user-select:none;
-webkit-tap-highlight-color:transparent;
}
html,body{
margin:0;
width:100%;
height:100%;
overflow:hidden;
touch-action:none;
}
body{
background:#d8c8a8;
}
#game{
position:relative;
width:100vw;
height:100vh;
overflow:hidden;
background:
radial-gradient(
circle at center,
#f2e8cf 0%,
#dfcfaa 70%,
#c8b487 100%
);
}
#score{
position:absolute;
top:16px;
left:50%;
transform:translateX(-50%);
z-index:100;
padding:9px 18px;
border-radius:15px;
background:rgba(255,255,255,.8);
color:#332b1e;
font-size:23px;
font-weight:900;
box-shadow:0 4px 15px rgba(0,0,0,.16);
}
#newWatermelon{
position:absolute;
top:65px;
left:50%;
transform:translateX(-50%);
z-index:100;
padding:9px 17px;
border:0;
border-radius:13px;
background:rgba(255,255,255,.8);
color:#332b1e;
font-size:15px;
font-weight:bold;
}
#watermelonShadow{
position:absolute;
width:280px;
height:55px;
left:50%;
top:58%;
transform:translate(-50%,-50%);
border-radius:50%;
background:rgba(0,0,0,.18);
filter:blur(15px);
}
#watermelon{
position:absolute;
width:min(62vw,330px);
height:min(62vw,330px);
left:50%;
top:50%;
transform:translate(-50%,-50%);
object-fit:contain;
z-index:10;
filter:
drop-shadow(0 12px 8px rgba(0,0,0,.28));
transition:
transform .08s,
opacity .12s;
}
#hammer{
position:absolute;
width:155px;
height:110px;
left:24%;
top:75%;
object-fit:contain;
z-index:30;
touch-action:none;
cursor:grab;
filter:
drop-shadow(0 8px 6px rgba(0,0,0,.3));
transform:
translate(-50%,-50%)
rotate(-25deg);
}
#hammer.smash{
transform:
translate(-50%,-50%)
rotate(35deg)
scale(1.08);
}
#pieces{
position:absolute;
inset:0;
z-index:20;
pointer-events:none;
}
.piece{
position:absolute;
width:58px;
height:58px;
object-fit:contain;
pointer-events:none;
filter:
drop-shadow(0 5px 4px rgba(0,0,0,.25));
}
#hint{
position:absolute;
bottom:22px;
left:50%;
transform:translateX(-50%);
z-index:100;
padding:10px 15px;
border-radius:13px;
background:rgba(255,255,255,.75);
color:#332b1e;
font-size:15px;
font-weight:bold;
white-space:nowrap;
}
#flash{
position:absolute;
inset:0;
background:white;
opacity:0;
z-index:80;
pointer-events:none;
}
#flash.active{
animation:flash .13s;
}
@keyframes flash{
0%{
opacity:.45;
}
100%{
opacity:0;
}
}
</style>
</head>
<body>
<div id="game">
<div id="score">
Разбито: 0
</div>
<button id="newWatermelon">
Новый арбуз
</button>
<div id="watermelonShadow"></div>
<img
id="watermelon"
src="https://img3.procvetok.com/crop/w500h500/32/a0/32a074858cfe875cb16d47a91e7d3c93.jpg"
draggable="false"
>
<img
id="hammer"
src="https://static5.depositphotos.com/1035219/428/i/450/depositphotos_4288272-stock-photo-iron-hammer-isolated-on-white.jpg"
draggable="false"
>
<div id="pieces"></div>
<div id="flash"></div>
<div id="hint">
Перетащи молоток на арбуз
</div>
</div>
<script>
const game =
document.getElementById("game");
const hammer =
document.getElementById("hammer");
const watermelon =
document.getElementById("watermelon");
const pieces =
document.getElementById("pieces");
const scoreText =
document.getElementById("score");
const flash =
document.getElementById("flash");
const newWatermelon =
document.getElementById("newWatermelon");
const smashSound =
new Audio(
"https://sfxmint.com/dl/impact-crate-break-07.mp3"
);
smashSound.preload = "auto";
/*
КАРТИНКА КУСОЧКОВ
*/
const pieceImage =
"https://img.magnific.com/premium-photo/slice-watermelon-isolated-white-clipping-path_26628-289.jpg";
let score = 0;
let broken = false;
let dragging = false;
let pointerId = null;
let offsetX = 0;
let offsetY = 0;
/*
Получаем центр молотка
*/
function getHammerPoint(){
const r =
hammer.getBoundingClientRect();
return {
x:r.left + r.width * .5,
y:r.top + r.height * .55
};
}
/*
Получаем центр арбуза
*/
function getWatermelonPoint(){
const r =
watermelon.getBoundingClientRect();
return {
x:r.left + r.width * .5,
y:r.top + r.height * .5,
radius:Math.min(
r.width,
r.height
) * .42
};
}
/*
ГЛАВНОЕ ИСПРАВЛЕНИЕ
Проверяем столкновение постоянно,
прямо во время движения молотка.
Поэтому больше не нужно ждать
несколько секунд или несколько ударов.
*/
function checkCollision(){
if(broken)
return;
const h =
getHammerPoint();
const w =
getWatermelonPoint();
const distance =
Math.hypot(
h.x-w.x,
h.y-w.y
);
if(
distance <
w.radius
){
smashWatermelon();
}
}
/*
Разбивание
*/
function smashWatermelon(){
if(broken)
return;
broken = true;
score++;
scoreText.textContent =
"Разбито: " + score;
/*
Анимация удара
*/
hammer.classList.add(
"smash"
);
setTimeout(()=>{
hammer.classList.remove(
"smash"
);
},120);
/*
Звук
*/
smashSound.currentTime = 0;
smashSound.play().catch(
()=>{}
);
/*
Вспышка
*/
flash.classList.remove(
"active"
);
void flash.offsetWidth;
flash.classList.add(
"active"
);
/*
Кусочки
*/
createPieces();
/*
Убираем целый арбуз
*/
watermelon.style.transform =
"translate(-50%,-50%) scale(.15)";
watermelon.style.opacity =
"0";
}
/*
Создание одного кусочка
*/
function createPiece(
x,
y,
angle,
speed,
size
){
const piece =
document.createElement("img");
piece.className =
"piece";
piece.src =
pieceImage;
piece.draggable =
false;
piece.style.width =
size + "px";
piece.style.height =
size + "px";
piece.style.left =
x + "px";
piece.style.top =
y + "px";
pieces.appendChild(piece);
let px = x;
let py = y;
let vx =
Math.cos(angle) * speed;
let vy =
Math.sin(angle) * speed;
let rotation =
Math.random()*360;
let rotationSpeed =
(Math.random()-.5)*16;
let life = 0;
function animate(){
life++;
px += vx;
py += vy;
/*
Гравитация
*/
vy += .45;
vx *= .985;
rotation +=
rotationSpeed;
piece.style.left =
px + "px";
piece.style.top =
py + "px";
piece.style.transform =
"rotate(" +
rotation +
"deg)";
piece.style.opacity =
Math.max(
0,
1-life/85
);
if(
life < 85 &&
py < innerHeight + 100
){
requestAnimationFrame(
animate
);
}else{
piece.remove();
}
}
requestAnimationFrame(
animate
);
}
/*
Создаём много разлетающихся
кусочков
*/
function createPieces(){
const r =
watermelon.getBoundingClientRect();
const x =
r.left + r.width/2;
const y =
r.top + r.height/2;
for(
let i=0;
i<14;
i++
){
const angle =
Math.PI * 2 * i / 14 +
(Math.random()-.5)*.4;
const speed =
4 + Math.random()*5;
const size =
38 + Math.random()*24;
createPiece(
x,
y,
angle,
speed,
size
);
}
}
/*
НАЧАЛО ПЕРЕТАСКИВАНИЯ
*/
hammer.addEventListener(
"pointerdown",
function(e){
e.preventDefault();
dragging = true;
pointerId =
e.pointerId;
hammer.setPointerCapture(
pointerId
);
const r =
hammer.getBoundingClientRect();
offsetX =
e.clientX -
(r.left+r.width/2);
offsetY =
e.clientY -
(r.top+r.height/2);
/*
Сразу проверяем:
вдруг молоток уже оказался
на арбузе.
*/
checkCollision();
}
);
/*
ДВИЖЕНИЕ
Теперь collision проверяется
КАЖДЫЙ кадр движения.
*/
hammer.addEventListener(
"pointermove",
function(e){
if(!dragging)
return;
e.preventDefault();
hammer.style.left =
(e.clientX-offsetX) +
"px";
hammer.style.top =
(e.clientY-offsetY) +
"px";
/*
Вот здесь происходит
мгновенная проверка удара.
*/
checkCollision();
}
);
/*
ОТПУСКАНИЕ
*/
hammer.addEventListener(
"pointerup",
function(e){
dragging = false;
pointerId = null;
checkCollision();
}
);
hammer.addEventListener(
"pointercancel",
function(){
dragging = false;
pointerId = null;
}
);
/*
Новый арбуз
*/
newWatermelon.addEventListener(
"pointerdown",
function(e){
e.stopPropagation();
broken = false;
pieces.innerHTML = "";
watermelon.style.opacity =
"1";
watermelon.style.transform =
"translate(-50%,-50%) scale(1)";
}
);
/*
Начальная позиция
*/
hammer.style.left =
innerWidth * .24 + "px";
hammer.style.top =
innerHeight * .72 + "px";
</script>
</body>
</html>Game Source: Арбузный молоток
Creator: GoldenBear87
Libraries: none
Complexity: complex (744 lines, 10.8 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-goldenbear87-mu75bx0f" to link back to the original. Then publish at arcadelab.ai/publish.