🎮ArcadeLab

自定义皮肤打地鼠

by CrystalMeteor49
212 lines6.7 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义皮肤打地鼠</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;font-family:system-ui}
img {
  object-fit: contain;
}
body{background:#eef6ff;padding:16px}
#wrap{max-width:500px;margin:0 auto}
.topbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}
.info{font-size:28px;font-weight:bold}
#startBtn{padding:8px 16px;font-size:20px}
.holeGrid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin:20px 0}
.hole{aspect-ratio:1/1;background:#8b572a;border-radius:50%;position:relative;overflow:hidden}
.mouse{width:70%;height:70%;position:absolute;bottom:0;left:15%;display:none}
.bottomBar{display:flex;justify-content:space-between;align-items:center;margin-top:16px;gap:8px}
.bottomBar button{padding:8px 12px;font-size:18px}
#drawPanel{position:fixed;inset:0;background:rgba(0,0,0,0.6);display:none;align-items:center;justify-content:center}
.drawBox{background:white;padding:16px;border-radius:10px}
#skinCanvas{border:1px solid #999}
.drawBtns{margin-top:8px;display:flex;gap:8px}
#bagPanel{position:fixed;inset:0;background:rgba(0,0,0,0.6);display:none;align-items:center;justify-content:center}
.bagBox{background:white;padding:16px;border-radius:10px;max-width:90vw}
.skinItem{width:80px;height:80px;border:1px solid #ccc;margin:4px;cursor:pointer}
</style>
</head>
<body>
<div id="wrap">
  <div class="topbar">
    <div class="info">分数:<span id="score">0</span> &nbsp;&nbsp;时间:<span id="time">60</span></div>
    <button id="startBtn">开始游戏</button>
  </div>
  <div class="holeGrid" id="grid">
    <div class="hole"><img class="mouse" src="" alt=""></div>
    <div class="hole"><img class="mouse" src="" alt=""></div>
    <div class="hole"><img class="mouse" src="" alt=""></div>
    <div class="hole"><img class="mouse" src="" alt=""></div>
    <div class="hole"><img class="mouse" src="" alt=""></div>
    <div class="hole"><img class="mouse" src="" alt=""></div>
    <div class="hole"><img class="mouse" src="" alt=""></div>
    <div class="hole"><img class="mouse" src="" alt=""></div>
    <div class="hole"><img class="mouse" src="" alt=""></div>
  </div>
  <div class="bottomBar">
    <button id="drawBtn">自定义皮肤</button>
    <button id="bagBtn">背包</button>
    <button id="soundBtn">🔊</button>
  </div>
</div>

<div id="drawPanel">
  <div class="drawBox">
    <canvas id="skinCanvas" width="120" height="120"></canvas>
    <div class="drawBtns">
      <button id="saveSkin">保存皮肤</button>
      <button id="closeDraw">关闭</button>
    </div>
  </div>
</div>

<div id="bagPanel">
  <div class="bagBox">
    <div id="skinList"></div>
    <button id="closeBag">关闭</button>
  </div>
</div>

<script>
const scoreEl=document.getElementById("score");
const timeEl=document.getElementById("time");
const startBtn=document.getElementById("startBtn");
const grid=document.getElementById("grid");
const holes=Array.from(document.querySelectorAll(".hole"));
const mouses=Array.from(document.querySelectorAll(".mouse"));
const drawBtn=document.getElementById("drawBtn");
const bagBtn=document.getElementById("bagBtn");
const soundBtn=document.getElementById("soundBtn");
const drawPanel=document.getElementById("drawPanel");
const bagPanel=document.getElementById("bagPanel");
const skinCanvas=document.getElementById("skinCanvas");
const ctx=skinCanvas.getContext("2d");
const saveSkin=document.getElementById("saveSkin");
const closeDraw=document.getElementById("closeDraw");
const closeBag=document.getElementById("closeBag");
const skinList=document.getElementById("skinList");

let score=0;
let time=60;
let running=false;
let timer=null;
let popTimer=null;
let soundOn=true;
let currentSkin=null;
let skins=JSON.parse(localStorage.getItem("skinList")||"[]");

// 默认皮肤
const defaultSkinUrl="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='45' fill='%238b572a'/%3E%3Ccircle cx='35' cy='40' r='8' fill='black'/%3E%3Ccircle cx='65' cy='40' r='8' fill='black'/%3E%3Cellipse cx='50' cy='65' rx='10' ry='7' fill='%23553322'/%3E%3C/svg%3E";
skins.unshift(defaultSkinUrl);
renderBag();

// 音效
function hitSound(){
  if(!soundOn) return;
  const a=new AudioContext();
  const o=a.createOscillator();
  const g=a.createGain();
  o.connect(g);g.connect(a.destination);
  o.frequency.value=600;
  g.gain.setValueAtTime(0.2,a.currentTime);
  g.gain.exponentialRampToValueAtTime(0.001,a.currentTime+0.12);
  o.start();o.stop(a.currentTime+0.12);
}

// 开始游戏
startBtn.onclick=()=>{
  if(running) return;
  running=true;
  score=0;time=60;
  scoreEl.textContent=score;
  timeEl.textContent=time;
  timer=setInterval(()=>{
    time--;
    timeEl.textContent=time;
    if(time<=0){clearInterval(timer);running=false;clearTimeout(popTimer)}
  },1000);
  popUp();
}

function popUp(){
  if(!running) return;
  hideAll();
  const idx=Math.floor(Math.random()*9);
  const mouse=mouses[idx];
  mouse.src=currentSkin||skins[0];
  mouse.style.display="block";
  popTimer=setTimeout(()=>{
    mouse.style.display="none";
    popUp();
  },900)
}
function hideAll(){mouses.forEach(m=>m.style.display="none")}

holes.forEach((h,i)=>{
  h.onclick=()=>{
    if(!running) return;
    const m=mouses[i];
    if(m.style.display==="block"){
      score++;
      scoreEl.textContent=score;
      hitSound();
      m.style.display="none";
    }
  }
})

// 画布绘画
let drawing=false;
skinCanvas.onmousedown=(e)=>{drawing=true}
skinCanvas.onmouseup=()=>{drawing=false}
skinCanvas.onmouseleave=()=>{drawing=false}
skinCanvas.onmousemove=(e)=>{
  if(!drawing) return;
  const r=skinCanvas.getBoundingClientRect();
  const x=e.clientX-r.left;
  const y=e.clientY-r.top;
  ctx.fillStyle="#222";
  ctx.beginPath();
  ctx.arc(x,y,5,0,Math.PI*2);
  ctx.fill();
}
ctx.fillStyle="#cccccc";
ctx.fillRect(0,0,120,120);

saveSkin.onclick=()=>{
  const url=skinCanvas.toDataURL("image/png");
  skins.push(url);
  localStorage.setItem("skinList",JSON.stringify(skins));
  renderBag();
  drawPanel.style.display="none";
}
closeDraw.onclick=()=>drawPanel.style.display="none";
drawBtn.onclick=()=>{
  ctx.fillStyle="#cccccc";
  ctx.fillRect(0,0,120,120);
  drawPanel.style.display="flex";
}

closeBag.onclick=()=>bagPanel.style.display="none";
bagBtn.onclick=()=>{renderBag();bagPanel.style.display="flex"}
function renderBag(){
  skinList.innerHTML="";
  skins.forEach((s,i)=>{
    const img=document.createElement("img");
    img.src=s;
    img.className="skinItem";
    img.onclick=()=>{currentSkin=s}
    skinList.appendChild(img);
  })
}

soundBtn.onclick=()=>{
  soundOn=!soundOn;
  soundBtn.textContent=soundOn?"🔊":"🔇";
}
</script>
</body>
</html>

Game Source: 自定义皮肤打地鼠

Creator: CrystalMeteor49

Libraries: none

Complexity: complex (212 lines, 6.7 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-crystalmeteor49" to link back to the original. Then publish at arcadelab.ai/publish.