夏天最烦的是什么?不是热,是蚊子!🦟 半夜嗡嗡嗡,起床打不到,开灯就消失,关灯就出现——相信每个人都有被蚊子支配的恐惧。 作为一个程序员,被蚊子咬了怎么办?当然是写个系统出气啊!

上周半夜被蚊子咬了三个包,怒从心头起,恶向胆边生。
打不到现实中的蚊子,我还打不到虚拟的吗?
于是一个脑洞诞生了——电蚊拍灭蚊排行系统:
打开网页,一群蚊子就在画面里乱窜,翅膀还一抖一抖的,跟真的一样烦。
你只需要——点它!
点中之后:
统计项 | 说明 |
|---|---|
总灭蚊数量 | 全场累计拍死多少只蚊子 |
地区排行榜 | 10个省市PK,前三名金银铜高亮 |
用户排行榜 | 8位灭蚊达人实时竞争第一 |
整个项目只有一个HTML文件,不需要任何框架、不需要后端、不需要安装。
双击打开就能玩,复制粘贴就能改。
1. 蚊子飞行动画
// 每帧更新位置,边界反弹
function loopMove(){
mosquitoList.forEach(mos=>{
mos.x += mos.vx;
mos.y += mos.vy;
if(mos.x <=0 || mos.x >= w-32) mos.vx *= -1;
if(mos.y <=0 || mos.y >= h-32) mos.vy *= -1;
mos.dom.style.left = mos.x+'px';
mos.dom.style.top = mos.y+'px';
})
requestAnimationFrame(loopMove);
}2. 击杀粒子爆炸
// 8个方向粒子扩散
function createParticle(cx,cy){
for(let i=0;i<8;i++){
const angle = Math.PI*2/8*i;
const speed = 40 + Math.random()*30;
p.animate([
{left:cx+'px',top:cy+'px',opacity:1},
{left:endX+'px',top:endY+'px',opacity:0}
],{duration:450,fill:'forwards'})
}
}3. 排行榜实时排序
// 对象转数组排序,前三名加样式
const areaArr = Object.entries(data.areaStat)
.sort((a,b)=>b[1]-a[1]);
// idx 0/1/2 分别对应金/银/铜高亮<!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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft Yahei", sans-serif;
}
body {
width: 100vw;
height: 100vh;
max-width: 450px;
margin: 0 auto;
background: linear-gradient(135deg,#0f172a,#1e293b);
color:#fff;
overflow: hidden;
user-select:none;
}
.header{
text-align:center;
padding:12px;
background:rgba(56,189,248,0.15);
border-bottom:1px solid #38bdf844;
}
.header h1{
font-size:20px;
color:#38bdf8;
}
.total-info{
display:flex;
justify-content: space-around;
padding:10px 0;
font-size:14px;
}
.total-item{
text-align:center;
}
.total-num{
font-size:22px;
color:#facc15;
font-weight:bold;
}
#gameBox{
position:relative;
width:100%;
height:42vh;
background:#0b1220;
border:1px solid #38bdf833;
overflow:hidden;
}
.mosquito{
position:absolute;
width:32px;
height:32px;
font-size:28px;
cursor:crosshair;
transition:transform 0.08s;
z-index:2;
}
.mosquito-shake{
animation: shake 0.12s infinite;
}
@keyframes shake{
0%{transform:rotate(-8deg)}
100%{transform:rotate(8deg)}
}
.particle{
position:absolute;
width:6px;
height:6px;
border-radius:50%;
background:#facc15;
pointer-events:none;
}
.kill-float-num{
position:absolute;
color:#f87171;
font-weight:bold;
font-size:18px;
pointer-events:none;
animation: floatUp 0.7s forwards;
}
@keyframes floatUp{
0%{opacity:1;transform:translateY(0)}
100%{opacity:0;transform:translateY(-60px)}
}
.rank-wrap{
height:calc(58vh - 120px);
overflow-y:auto;
padding:10px;
}
.rank-title{
font-size:16px;
margin:8px 0;
color:#38bdf8;
display:flex;
align-items:center;
gap:6px;
}
.rank-item{
display:flex;
justify-content:space-between;
padding:8px 10px;
background:rgba(255,255,255,0.06);
margin:4px 0;
border-radius:6px;
font-size:14px;
}
.rank-top1{background:rgba(250,204,21,0.2);}
.rank-top2{background:rgba(156,163,175,0.25);}
.rank-top3{background:rgba(180,83,9,0.25);}
.btn-area{
text-align:center;
padding:8px;
}
button{
padding:8px 16px;
border:none;
border-radius:6px;
background:#2563eb;
color:white;
font-size:14px;
margin:0 4px;
}
</style>
</head>
<body>
<div class="header">
<h1>🦟电蚊拍灭蚊排行系统</h1>
<div class="total-info">
<div class="total-item">
<div>总灭蚊数量</div>
<div class="total-num" id="totalKill">0</div>
</div>
<div class="total-item">
<div>场内蚊子</div>
<div class="total-num" id="liveMos">0</div>
</div>
</div>
</div>
<div id="gameBox"></div>
<div class="btn-area">
<button onclick="spawnMosquito(6)">🦟生成一批蚊子</button>
<button onclick="resetAll()">🔄重置数据</button>
</div>
<div class="rank-wrap">
<div class="rank-title">📍地区灭蚊排行榜</div>
<div id="areaRank"></div>
<div class="rank-title">👤用户灭蚊排行榜</div>
<div id="userRank"></div>
</div>
<script>
const areaList = ["广东","重庆","江苏","浙江","湖南","湖北","四川","广西","江西","安徽"];
const userPool = ["灭蚊战神","拍蚊小能手","夏夜斗士","驱蚊大队长","蚊子天敌","夜间猎手","电蚊拍大师","夏日守护者"];
let data = {
totalKill:0,
areaStat:{},
userStat:{}
};
areaList.forEach(a=>{ data.areaStat[a] = 0; })
userPool.forEach(u=>{ data.userStat[u] = Math.floor(Math.random()*15); })
const gameBox = document.getElementById('gameBox');
let mosquitoList = [];
function renderRank(){
const areaArr = Object.entries(data.areaStat).sort((a,b)=>b[1]-a[1]);
let areaHtml = '';
areaArr.forEach((item,idx)=>{
let cls = '';
if(idx===0) cls='rank-top1';
else if(idx===1) cls='rank-top2';
else if(idx===2) cls='rank-top3';
areaHtml += `<div class="rank-item {idx+1}. {item[1]}只</span>
</div>`
})
document.getElementById('areaRank').innerHTML = areaHtml;
const userArr = Object.entries(data.userStat).sort((a,b)=>b[1]-a[1]);
let userHtml = '';
userArr.forEach((item,idx)=>{
let cls = '';
if(idx===0) cls='rank-top1';
else if(idx===1) cls='rank-top2';
else if(idx===2) cls='rank-top3';
userHtml += `<div class="rank-item {idx+1}. {item[1]}只</span>
</div>`
})
document.getElementById('userRank').innerHTML = userHtml;
document.getElementById('totalKill').innerText = data.totalKill;
document.getElementById('liveMos').innerText = mosquitoList.length;
}
function spawnMosquito(count){
for(let i=0;i<count;i++){ createOneMosquito(); }
renderRank();
}
function createOneMosquito(){
const el = document.createElement('div');
el.className = 'mosquito mosquito-shake';
el.innerText = '🦟';
const w = gameBox.clientWidth;
const h = gameBox.clientHeight;
let x = Math.random()*(w-40);
let y = Math.random()*(h-40);
el.style.left = x+'px';
el.style.top = y+'px';
let vx = (Math.random()-0.5)*2.2;
let vy = (Math.random()-0.5)*2.2;
const areaRandom = areaList[Math.floor(Math.random()*areaList.length)];
const userRandom = userPool[Math.floor(Math.random()*userPool.length)];
const mosObj = { dom:el, x,y,vx,vy, area:areaRandom, user:userRandom }
mosquitoList.push(mosObj);
el.onclick = (e)=>{
e.stopPropagation();
killMosquito(mosObj);
}
gameBox.appendChild(el);
}
function killMosquito(mosObj){
const idx = mosquitoList.indexOf(mosObj);
if(idx===-1) return;
mosquitoList.splice(idx,1);
data.totalKill +=1;
data.areaStat[mosObj.area] +=1;
if(!data.userStat[mosObj.user]) data.userStat[mosObj.user]=0;
data.userStat[mosObj.user] +=1;
showKillNum(mosObj.x,mosObj.y,mosObj.area);
createParticle(mosObj.x+16, mosObj.y+16);
mosObj.dom.remove();
renderRank();
}
function showKillNum(x,y,areaName){
const div = document.createElement('div');
div.className = 'kill-float-num';
div.innerText = `+1 ${areaName}`;
div.style.left = x+'px';
div.style.top = y+'px';
gameBox.appendChild(div);
setTimeout(()=>div.remove(),700);
}
function createParticle(cx,cy){
for(let i=0;i<8;i++){
const p = document.createElement('div');
p.className='particle';
p.style.left = cx+'px';
p.style.top = cy+'px';
gameBox.appendChild(p);
const angle = Math.PI*2/8*i;
const speed = 40 + Math.random()*30;
const endX = cx + Math.cos(angle)*speed;
const endY = cy + Math.sin(angle)*speed;
p.animate([
{left:cx+'px',top:cy+'px',opacity:1},
{left:endX+'px',top:endY+'px',opacity:0}
],{duration:450,fill:'forwards'})
setTimeout(()=>p.remove(),450);
}
}
function loopMove(){
const w = gameBox.clientWidth;
const h = gameBox.clientHeight;
mosquitoList.forEach(mos=>{
mos.x += mos.vx;
mos.y += mos.vy;
if(mos.x <=0 || mos.x >= w-32) mos.vx *= -1;
if(mos.y <=0 || mos.y >= h-32) mos.vy *= -1;
mos.dom.style.left = mos.x+'px';
mos.dom.style.top = mos.y+'px';
})
requestAnimationFrame(loopMove);
}
function resetAll(){
mosquitoList.forEach(m=>m.dom.remove());
mosquitoList = [];
data.totalKill =0;
areaList.forEach(a=>data.areaStat[a]=0);
userPool.forEach(u=>data.userStat[u]=Math.floor(Math.random()*8));
renderRank();
}
loopMove();
spawnMosquito(5);
renderRank();
</script>
</body>
</html>灭蚊排行.html操作说明:
这个小项目虽然简单,但扩展性很强:
程序员的快乐有时候就是这么简单——
被蚊子咬了 → 写个灭蚊游戏出气 → 顺便练了前端动画 → 还能发个朋友圈装逼。
一个几十KB的HTML文件,没有复杂的架构,没有高深的算法,但就是好玩。
这大概就是编程的魅力吧——想到什么,就能做出来什么。
你的家乡灭蚊战斗力排第几? 快去试试,评论区报出你的最高纪录!🏆
🔖 收藏本文,下次被蚊子咬了拿出来泄愤! 👆 点赞关注,更多有趣的前端小项目持续更新!
#前端 #趣味编程 #HTML #程序员日常 #灭蚊大战 #小项目