Built with anycoder
分数 0
目标 30
❤️❤️❤️
难度 入门

太空打字

在输入框中输入字母消灭单词!

  • 选中难度:入门 / 初级 / 进阶
  • 目标:达到目标分数即可通关
  • 生命:3条,错字或漏字会扣生命
=== assets/css/styles.css === :root{ --bg: #0b1020; --bg-2: #141a33; --primary: #7c3aed; --primary-2: #8b5cf6; --accent: #22d3ee; --good: #10b981; --bad: #ef4444; --text: #e5e7eb; --muted: #9ca3af; --card: #1f2937; --shadow: rgba(0,0,0,.35); --radius: 14px; } * { box-sizing: border-box; } html, body { height: 100%; } body { margin: 0; background: radial-gradient(1200px 800px at 20% 10%, #1a1f3d, transparent), radial-gradient(900px 700px at 80% 20%, #132042, transparent), var(--bg); color: var(--text); font-family: 'Nunito', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .topbar { position: sticky; top: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; background: rgba(15, 23, 42, 0.7); backdrop-filter: blur(8px); border-bottom: 1px solid rgba(255,255,255,0.06); } .brand { color: #93c5fd; text-decoration: none; font-weight: 700; padding: 6px 10px; border-radius: 8px; transition: background .2s ease; } .brand:hover { background: rgba(147,197,253,.15); } .controls { display: flex; gap: 8px; } .control-btn { background: var(--card); color: var(--text); border: 1px solid rgba(255,255,255,0.1); border-radius: 10px; padding: 8px 10px; cursor: pointer; transition: transform .08s ease, background .2s ease; } .control-btn:hover { background: #243244; } .control-btn:active { transform: scale(0.98); } .main { max-width: 1100px; margin: 0 auto; padding: 16px; display: grid; grid-template-rows: auto 1fr auto; gap: 12px; min-height: calc(100vh - 80px); } .hud { display: grid; grid-template-columns: 1fr auto 1fr; gap: 12px; align-items: center; background: rgba(31,41,55,0.6); padding: 12px; border-radius: var(--radius); border: 1px solid rgba(255,255,255,0.06); box-shadow: 0 10px 30px var(--shadow); } .hud-left, .hud-right { display: flex; align-items: center; gap: 16px; } .hud-center { display: flex; justify-content: center; } .stat, .difficulty { display: flex; gap: 6px; align-items: center; background: rgba(255,255,255,0.06); padding: 6px 10px; border-radius: 10px; } .label { color: var(--muted); font-weight: 600; } .value { color: var(--text); font-weight: 800; } .progress { width: min(60vw, 420px); height: 14px; background: rgba(255,255,255,0.08); border-radius: 999px; overflow: hidden; border: 1px solid rgba(255,255,255,0.06); } .progress-bar { height: 100%; width: 0%; background: linear-gradient(90deg, var(--primary), var(--accent)); transition: width .2s linear; } .hearts { font-size: 20px; } .game-area { position: relative; background: linear-gradient(180deg, rgba(20,26,51,0.7), rgba(11,16,32,0.8)); border-radius: var(--radius); border: 1px solid rgba(255,255,255,0.06); box-shadow: inset 0 0 0 1px rgba(255,255,255,0.02), 0 20px 40px var(--shadow); min-height: min(58vh, 520px); overflow: hidden; } /* subtle stars */ .game-area::before { content: ""; position: absolute; inset: 0; background-image: radial-gradient(2px 2px at 20% 30%, rgba(255,255,255,0.6), transparent), radial-gradient(1.5px 1.5px at 80% 60%, rgba(255,255,255,0.5), transparent), radial-gradient(1.2px 1.2px at 50% 80%, rgba(255,255,255,0.4), transparent), radial-gradient(1.5px 1.5px at 10% 70%, rgba(255,255,255,0.5), transparent), radial-gradient(2px 2px at 60% 20%, rgba(255,255,255,0.6), transparent); pointer-events: none; } .instructions { position: absolute; inset: 0; display: grid; place-items: center; text-align: center; color: var(--muted); } .instructions h1 { margin: 0 0 6px 0; color: var(--text); font-size: 32px; letter-spacing: 1px; } .word { position: absolute; top: -60px; left: 0; padding: 6px 10px; font-weight: 800; font-size: clamp(18px, 2.5vw, 24px); color: #111827; background: linear-gradient(180deg, #fef3c7, #fde68a); border: 2px solid #f59e0b; border-radius: 10px; box-shadow: 0 6px 16px rgba(0,0,0,.35), inset 0 -2px 0 rgba(0,0,0,0.1); transform: translate3d(0, 0, 0); will-change: transform; user-select: none; white-space: nowrap; } .word::after { content: ""; position: absolute; inset: -2px; border-radius: inherit; pointer-events: none; background: radial-gradient(50% 60% at 50% 10%, rgba(255,255,255,.35), transparent); } .word.matching { background: linear-gradient(180deg, #bbf7d0, #86efac); border-color: var(--good); transform: scale(1.05); } .word.target { animation: pulse 1.2s infinite; outline: 2px dashed rgba(16,185,129,.6); outline-offset: 2px; } @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(16,185,129, 0.6); } 70% { box-shadow: 0 0 0 10px rgba(16,185,129, 0); } 100% { box-shadow: 0 0 0 0 rgba(16,185,129, 0); } } .word.cleared { background: linear-gradient(180deg, #dbeafe, #93c5fd); border-color: var(--accent); animation: pop .25s ease forwards; } @keyframes pop { 0% { transform: scale(1); opacity: 1; } 100% { transform: scale(1.3) translateY(-10px); opacity: 0; } } .input-bar { display: flex; justify-content: center; padding-bottom: 8px; } #typeInput { width: min(600px, 90vw); padding: 14px 16px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.15); background: rgba(31,41,55,0.9); color: var(--text); font-size: 18px; font-weight: 700; outline: none; transition: border-color .2s ease, box-shadow .2s ease; } #typeInput:focus { border-color: var(--primary-2); box-shadow: 0 0 0 3px rgba(139,92,246,0.25); } .overlay { position: fixed; inset: 0; display: grid; place-items: center; background: rgba(0,0,0,0.55); z-index: 50; padding: 16px; } .overlay.hidden { display: none; } .modal { width: min(520px, 92vw); background: var(--card); border: 1px solid rgba(255,255,255,0.1); border-radius: 16px; padding: 18px; box-shadow: 0 30px 60px var(--shadow); } .modal h2 { margin: 0 0 8px 0; } .modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; } .primary, .secondary, .diff-btn { padding: 10px 14px; border-radius: 10px; border: 1px solid transparent; cursor: pointer; font-weight: 700; } .primary { background: linear-gradient(90deg, var(--primary), var(--primary-2)); color: white; } .secondary { background: transparent; color: var(--text); border-color: rgba(255,255,255,0.2); } .diff-btn { width: 100%; margin: 6px 0; background: rgba(255,255,255,0.06); color: var(--text); border: 1px solid rgba(255,255,255,0.12); } .diff-btn:hover { background: rgba(255,255,255,0.12); } .difficulty-picker { margin-top: 8px; } .help-content { color: var(--muted); } .help-content ul { padding-left: 20px; } .footer { text-align: center; padding: 12px; color: var(--muted); font-size: 14px; } @media (max-width: 640px) { .hud { grid-template-columns: 1fr; gap: 8px; } .hud-left, .hud-right { justify-content: space-between; } .instructions h1 { font-size: 24px; } } @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } } === assets/js/game.js === (() => { 'use strict'; // 基础元素 const gameArea = document.getElementById('gameArea'); const input = document.getElementById('typeInput'); const scoreEl = document.getElementById('score'); const livesEl = document.getElementById('lives'); const targetEl = document.getElementById('target'); const difficultyLabel = document.getElementById('difficultyLabel'); const progressBar = document.getElementById('progressBar'); const instructions = document.getElementById('instructions'); const soundToggle = document.getElementById('soundToggle'); const helpBtn = document.getElementById('helpBtn'); const overlay = document.getElementById('overlay'); const overlayTitle = document.getElementById('overlayTitle'); const overlayContent = document.getElementById('overlayContent'); const closeOverlay = document.getElementById('closeOverlay'); const helpOverlay = document.getElementById('helpOverlay'); const closeHelp = document.getElementById('closeHelp'); // 单词库(3-7字母不等) const WORDS = [ "cat","dog","sun","map","pen","cup","cap","box","bus","hat","bed","run","fun","red","big","car","toy","ball","fish","bird", "book","code","star","moon","light","storm","smile","happy","earth","water","bread","chair","house","horse","green","blue", "train","apple","table","phone","mouse","tiger","zebra","candy","music","plant","robot","magic","smile","bread","shape", "circle","square","river","mountain","forest","friend","school","letter","number","planet","camera","orange","purple","yellow", "butter","flower","bridge","window","winter","summer","spring","autumn","circle","little","simple","garden","pencil","rocket", "cookie","dragon","puzzle","pirate","castle","forest","silver","guitar","violin","orange","banana","strawb","cherry","pepper", "mother","father","sister","brother","animal","travel","future","little","memory","beauty","season","planet","energy","dreams", "bright","kindly","family","action","global","victor","bright","silent","knight","flight","string","stream","spring","stroke", "treasure","teacher","student","bicycle","picture","glitter","treetop","snowman","traffic","library","monster","fantasy", "captain","fantasy","forward","kingdom","adventure","mystery","journey","treetop","starlight","butterfly","paperplane", "imagine","harmony","pattern","freedom","silence","balance","dolphin","serpent","whisper","marbles","rainbow","boundary", "mission","journey","distant","maximum","minimal","sincere","radiant","confetti","avocado","umbrella","marathon" ]; // 难度配置 const DIFFICULTY = { easy: { label: '入门', minLen: 2, maxLen: 4, spawnInterval: 1400, speed: 40, scoreTarget: 30, lives: 3 }, medium: { label: '初级', minLen: 3, maxLen: 5, spawnInterval: 1200, speed: 55, scoreTarget: 40, lives: 3 }, hard: { label: '进阶', minLen: 4, maxLen: 7, spawnInterval: 1000, speed: 70, scoreTarget: 50, lives: 3 }, }; // 状态 const state = { running: false, score: 0, lives: 3, target: 30, level: 'easy', words: [], lastSpawn: 0, startTime: 0, timeLimit: 60, // 秒 soundOn: true, lastFrame: 0, }; // 音频 const SFX = { ctx: null, init() { if (!this.ctx) { this.ctx = new (window.AudioContext || window.webkitAudioContext)(); } }, beep({ freq = 600, duration = 0.1, type = 'sine', gain = 0.08 } = {}) { if (!state.soundOn || !this.ctx) return; const ctx = this.ctx; const osc = ctx.createOscillator(); const amp = ctx.createGain(); osc.type = type; osc.frequency.value = freq; amp.gain.value = gain; osc.connect(amp); amp.connect(ctx.destination); const now = ctx.currentTime; osc.start(now); osc.stop(now + duration); }, good() { this.beep({ freq: 720, duration: 0.08, type: 'triangle', gain: 0.09 }); }, bad() { this.beep({ freq: 200, duration: 0.12, type: 'square', gain: 0.08 }); }, tick() { this.beep({ freq: 400, duration: 0.05, type: 'sine', gain: 0.05 }); }, }; // 工具 const rand = (min, max) => Math.random() * (max - min) + min; const choice = (arr) => arr[Math.floor(Math.random() * arr.length)]; function showOverlayStart() { overlayTitle.textContent = '选择难度'; overlayContent.innerHTML = `
${Object.entries(DIFFICULTY).map(([key, cfg]) => ` `).join('')}
`; overlay.classList.remove('hidden'); overlay.querySelectorAll('.diff-btn').forEach(btn => { btn.addEventListener('click', () => { startGame(btn.dataset.level); overlay.classList.add('hidden'); }); }); } function showOverlayEnd(win) { overlayTitle.textContent = win ? '通关成功 🎉' : '游戏结束 😢'; const content = win ? `

恭喜!你达到了目标分数 ${state.target} 分。

` : `

再接再厉!本次得分 ${state.score} 分。

`; overlayContent.innerHTML = ` ${content}
`; overlay.classList.remove('hidden'); overlay.querySelectorAll('.diff-btn').forEach(btn => { btn.addEventListener('click', () => { startGame(btn.dataset.level); overlay.classList.add('hidden'); }); }); } function setHud() { scoreEl.textContent = state.score; targetEl.textContent = state.target; livesEl.textContent = '❤️'.repeat(state.lives); difficultyLabel.textContent = DIFFICULTY[state.level].label; updateProgress(); } function updateProgress() { const elapsed = (performance.now() - state.startTime) / 1000; const remain = Math.max(0, state.timeLimit - elapsed); const pct = Math.max(0, Math.min(100, (elapsed / state.timeLimit) * 100)); progressBar.style.width = `${pct}%`; if (Math.floor(remain) % 10 === 0 && remain > 0) { SFX.tick(); } } function filterWordsByLen(minLen, maxLen) { return WORDS.filter(w => w.length >= minLen && w.length <= maxLen); } // 游戏控制 function startGame(level = 'easy') { // 初始化音频上下文(必须在用户手势中) SFX.init(); state.level = level; const cfg = DIFFICULTY[level]; state.target = cfg.scoreTarget; state.lives = cfg.lives; state.timeLimit = 60; // 固定60秒 state.score = 0; state.words.forEach(w => w.el.remove()); state.words = []; state.lastSpawn = 0; state.startTime = performance.now(); state.lastFrame = 0; state.running = true; instructions.style.display = 'grid'; // 还没开始跑时显示 setHud(); input.value = ''; input.focus(); // 立即开始帧循环 requestAnimationFrame(loop); } function endGame(win) { state.running = false; showOverlayEnd(win); } function spawnWord() { const cfg = DIFFICULTY[state.level]; const pool = filterWordsByLen(cfg.minLen, cfg.maxLen); const text = choice(pool); const el = document.createElement('div'); el.className = 'word'; el.textContent = text; gameArea.appendChild(el); // 随机水平位置,避免遮挡 const areaW = gameArea.clientWidth; const w = el.offsetWidth; const x = Math.max(4, Math.min(areaW - w - 4, rand(4, areaW - w - 4))); const y = -el.offsetHeight - 4; const obj = { text, x, y, speed: cfg.speed * (0.9 + Math.random() * 0.3), // 加入轻微随机 el, }; el.style.transform = `translate3d(${x}px, ${y}px, 0)`; state.words.push(obj); } function removeWordAt(index) { const w = state.words[index]; if (!w) return; w.el.remove(); state.words.splice(index, 1); } function loseLife() { state.lives -= 1; livesEl.textContent = '❤️'.repeat(Math.max(0, state.lives)); SFX.bad(); if (state.lives <= 0) { endGame(false); } } function clearWord(i) { const w = state.words[i]; if (!w) return; w.el.classList.add('cleared'); setTimeout(() => w.el.remove(), 180); state.words.splice(i, 1); } function tryMatch(typed) { if (!typed) return null; // 找到所有匹配的单词 const matches = state.words .map((w, i) => ({ ...w, index: i })) .filter(w => w.text.startsWith(typed)); // 优先级:最靠近底部的优先 if (matches.length === 0) return null; matches.sort((a, b) => b.y - a.y); return matches[0]; } function onType() { if (!state.running) return; const typed = input.value.trim().toLowerCase(); state.words.forEach(w => { w.el.classList.remove('matching', 'target'); }); if (!typed) return; const match = tryMatch(typed); if (match) { match.el.classList.add('matching'); // 若完全匹配则清除 if (typed === match.text) { const points = Math.max(1, 5 + match.text.length); state.score += points; scoreEl.textContent = state.score; clearWord(match.index); input.value = ''; // 得分提示 showFloatingText(`+${points}`, match.x + 10, match.y, '#a7f3d0'); SFX.good(); // 胜利判断 if (state.score >= state.target) { endGame(true); } } } else { // 没有匹配则回退(仅在末次输入后触发) // 为了不打断小朋友连续输入,这里不强制清空,只用音效提示 // input.value = ''; } } function showFloatingText(text, x, y, color = '#fff') { const el = document.createElement('div'); el.textContent = text; el.style.position = 'absolute'; el.style.left = `${x}px`; el.style.top = `${y}px`; el.style.color = color; el.style.fontWeight = '800'; el.style.textShadow = '0 2px 8px rgba(0,0,0,0.6)'; el.style.pointerEvents = 'none'; el.style.transition = 'transform .6s ease, opacity .6s ease'; el.style.opacity = '1'; el.style.transform = 'translateY(0)'; gameArea.appendChild(el); requestAnimationFrame(() => { el.style.transform = 'translateY(-30px)'; el.style.opacity = '0'; }); setTimeout(() => el.remove(), 650); } function loop(ts) { if (!state.running) return; if (!state.lastFrame) state.lastFrame = ts; const dt = (ts - state.lastFrame) / 1000; state.lastFrame = ts; // 移除说明层(开始运动后) if (instructions.style.display !== 'none') { instructions.style.display = 'none'; } // 生成单词 if (ts - state.lastSpawn > DIFFICULTY[state.level].spawnInterval) { spawnWord(); state.lastSpawn = ts; } // 更新单词位置 const areaH = gameArea.clientHeight; for (let i = state.words.length - 1; i >= 0; i--) { const w = state.words[i]; w.y += w.speed * dt; w.el.style.transform = `translate3d(${w.x}px, ${w.y}px, 0)`; // 到底部判定 const rect = w.el.getBoundingClientRect(); const bottomY = rect.bottom - gameArea.getBoundingClientRect().top; if (bottomY >= areaH - 6) { // 漏掉了 removeWordAt(i); loseLife(); } } // 更新进度与计时 updateProgress(); const elapsed = (performance.now() - state.startTime) / 1000; if (elapsed >= state.timeLimit && state.score < state.target) { endGame(false); } requestAnimationFrame(loop); } // 事件 input.addEventListener('input', onType); input.addEventListener('keydown', (e) => { if (e.key === 'Escape') { input.value = ''; } }); // 音效 soundToggle.addEventListener('click', () => { state.soundOn = !state.soundOn; soundToggle.textContent = state.soundOn ? '🔊' : '🔇'; if (state.soundOn) SFX.tick(); }); // 帮助 helpBtn.addEventListener('click', () => { helpOverlay.classList.remove('hidden'); }); closeHelp.addEventListener('click', () => { helpOverlay.classList.add('hidden'); showOverlayStart(); }); // Overlay 关闭 closeOverlay.addEventListener('click', () => { overlay.classList.add('hidden'); }); // 点击游戏区时聚焦输入 gameArea.addEventListener('click', () => input.focus()); // 初始显示 helpOverlay.classList.remove('hidden'); setHud(); })();