ŁAPACZ — kosmiczny deszcz
GryPodgląd
Ładowanie podglądu…
Zbieraj gwiazdy przez 45 sekund i omijaj meteoryty. Każda gwiazda daje 10 punktów, a trzy zderzenia kończą lot.
Kod źródłowy
Licencja MIT — także do użytku komercyjnego. Zachowaj informacje o autorze i licencji.
HTML
<main><header><span class="eyebrow">Gra zręcznościowa</span><h1>ŁAPACZ!</h1><p>Gwiazdy do koszyka. Skały za burtę.</p></header><div class="desktop-split"><section aria-label="Plansza"><canvas id="screen" width="600" height="400" tabindex="0" aria-label="ŁAPACZ!. Złap żółte gwiazdy, omijaj różowe meteoryty. Steruj myszą, palcem albo strzałkami. Runda trwa 45 sekund.">Gra wymaga obsługi canvas. Wynik i przyciski znajdują się pod planszą.</canvas><div class="controls" aria-label="Sterowanie"><button type="button" data-key="ArrowLeft" aria-label="Ruch: lewo">←</button><button type="button" data-key="ArrowRight" aria-label="Ruch: prawo">→</button></div></section><aside><div class="eyebrow">Ładunek / punkty</div><div id="score" class="metric">0</div><p id="status" class="status" role="status">Naciśnij Graj.</p><div class="actions"><button id="play" type="button">Graj</button><button id="reset" type="button">Od nowa</button></div><p>Złap żółte gwiazdy, omijaj różowe meteoryty. Steruj myszą, palcem albo strzałkami. Runda trwa 45 sekund.</p><small>Esc — pauza. Gra zatrzymuje się po opuszczeniu okna.</small></aside></div></main>
CSS
*{box-sizing:border-box}body{margin:0;font-family:system-ui,sans-serif;background:var(--bg);color:var(--ink)}main{width:min(100% - 32px,980px);margin:32px auto}h1{font-size:clamp(32px,7vw,64px);line-height:1;margin:12px 0 20px;letter-spacing:-.045em}p{line-height:1.55}button{font:inherit;min-height:44px;padding:10px 16px;border:1px solid currentColor;border-radius:0;background:transparent;color:inherit;cursor:pointer}button:hover{background:var(--ink);color:var(--bg)}button:disabled{cursor:default;opacity:.55}button:focus-visible,canvas:focus-visible{outline:3px solid var(--focus);outline-offset:3px}button[aria-pressed="true"]{background:var(--ink);color:var(--bg)}.eyebrow{font:12px ui-monospace,monospace;letter-spacing:.16em;text-transform:uppercase}.status{min-height:48px;font-weight:600}.actions{display:flex;flex-wrap:wrap;gap:8px}.metric{font:700 28px ui-monospace,monospace}small{line-height:1.5}canvas{display:block;width:100%;height:auto;border:1px solid currentColor;touch-action:none}.controls{display:flex;flex-wrap:wrap;gap:8px;margin:14px 0}.controls button{min-width:54px;touch-action:none;user-select:none}.board{display:grid;gap:5px}.board button{padding:0;aspect-ratio:1;min-width:0;font-size:clamp(18px,5vw,30px)}@media(max-width:600px){main{width:calc(100% - 24px);margin:20px auto}h1{margin-bottom:16px}.desktop-split{display:block!important}}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
:root{--bg:#c9e8f0;--ink:#17354b;--focus:#a01f52}h1{font-weight:900;font-style:italic;font-size:clamp(62px,12vw,120px);transform:rotate(-3deg);display:inline-block;margin:22px 0}header p{font-weight:700}.desktop-split{display:grid;grid-template-columns:minmax(0,660px) 1fr;gap:26px}canvas{border:4px solid #17354b}aside{background:#ffdb66;padding:20px;border:2px solid #17354b}.metric{font-size:70px}.actions button{background:#17354b;color:#fff}.actions button:hover{background:#fff;color:#17354b}
@media(max-width:600px){aside .metric{font-size:36px}aside .status{min-height:0;margin:8px 0}aside .actions{margin:12px 0}}
JavaScript
function catchResult(item,basket){if(item.y<348||item.y>382||Math.abs(item.x-basket)>44)return null;return item.kind==='star'?{points:10,damage:0}:{points:0,damage:1};}
$(function(){const canvas=document.getElementById('screen'),ctx=canvas.getContext('2d');let basket,items,elapsed,spawnTime,score,lives;
function init(){basket=300;items=[{x:140,y:60,kind:'star'},{x:350,y:140,kind:'rock'},{x:480,y:30,kind:'star'}];elapsed=0;spawnTime=0;score=0;lives=3;$('#score').text(0);}
function direction(key){if(key==='ArrowLeft')basket=Math.max(40,basket-18);if(key==='ArrowRight')basket=Math.min(560,basket+18);}
$(canvas).on('pointermove pointerdown',function(event){if(!playing)return;const box=canvas.getBoundingClientRect();basket=Math.max(40,Math.min(560,(event.originalEvent.clientX-box.left)*600/box.width));});
function update(dt){elapsed+=dt;spawnTime+=dt;if(held.has('ArrowLeft'))basket-=dt*360;if(held.has('ArrowRight'))basket+=dt*360;basket=Math.max(40,Math.min(560,basket));
if(spawnTime>.65){spawnTime=0;items.push({x:25+Math.random()*550,y:-15,kind:Math.random()<.7?'star':'rock'});}
const remaining=[];for(const item of items){item.y+=dt*(95+elapsed*2);const result=catchResult(item,basket);if(result){score+=result.points;lives=Math.max(0,lives-result.damage);$('#score').text(score);}else if(item.y<420)remaining.push(item);}items=remaining;
if(lives===0)finish('Trzy zderzenia. Wynik: '+score+'.');else if(elapsed>=45)finish('Dostawa zakończona! Wynik: '+score+'.');
}
function draw(){ctx.fillStyle='#14364b';ctx.fillRect(0,0,600,400);ctx.fillStyle='#7290a2';for(let i=0;i<30;i++)ctx.fillRect((i*97)%600,(i*71)%340,2,2);
items.forEach(item=>{ctx.beginPath();if(item.kind==='star'){ctx.fillStyle='#ffdb66';for(let i=0;i<10;i++){const angle=-Math.PI/2+i*Math.PI/5,r=i%2?6:14,x=item.x+Math.cos(angle)*r,y=item.y+Math.sin(angle)*r;if(i===0)ctx.moveTo(x,y);else ctx.lineTo(x,y);}}else{ctx.fillStyle='#f284aa';for(let i=0;i<7;i++){const angle=i*Math.PI*2/7;const x=item.x+Math.cos(angle)*13,y=item.y+Math.sin(angle)*13;if(i===0)ctx.moveTo(x,y);else ctx.lineTo(x,y);}}ctx.closePath();ctx.fill();});
ctx.fillStyle='#c9e8f0';ctx.fillRect(basket-35,363,70,13);ctx.fillRect(basket-40,352,8,24);ctx.fillRect(basket+32,352,8,24);ctx.font='bold 16px monospace';ctx.fillStyle='#fff';ctx.fillText('CZAS '+Math.max(0,Math.ceil(45-elapsed))+' s',20,27);ctx.fillText('ŻYCIA '+lives,480,27);
}
let playing = false, ended = false, frameId = null, previous = 0;
const held = new Set();
function pause() {
if (!playing) return;
playing = false; held.clear(); cancelAnimationFrame(frameId);
$('#play').text('Wznów'); $('#status').text('Pauza.');
}
function finish(message) {
playing = false; ended = true; held.clear(); cancelAnimationFrame(frameId);
$('#play').text('Zagraj ponownie'); $('#status').text(message);
}
function reset() {
playing = false; ended = false; held.clear(); cancelAnimationFrame(frameId);
init(); draw(); $('#play').text('Graj'); $('#status').text('Naciśnij Graj.');
}
function frame(now) {
if (!playing) return;
const dt = Math.min((now - previous) / 1000, .035); previous = now;
update(dt); draw();
if (playing) frameId = requestAnimationFrame(frame);
}
$('#play').on('click', function () {
if (playing) { pause(); return; }
if (ended) reset();
playing = true; previous = performance.now();
$(this).text('Pauza'); $('#status').text('Gra trwa.'); canvas.focus();
frameId = requestAnimationFrame(frame);
});
$('#reset').on('click', reset);
$(document).on('keydown', function (event) {
if (event.key === 'Escape') { pause(); return; }
if (playing && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
event.preventDefault(); held.add(event.key); direction(event.key);
}
}).on('keyup', event => held.delete(event.key));
$('[data-key]').on('pointerdown', function (event) {
if (!playing) return;
event.preventDefault(); this.setPointerCapture(event.originalEvent.pointerId);
const key = $(this).attr('data-key'); held.add(key); direction(key);
}).on('pointerup pointercancel lostpointercapture', function () { held.delete($(this).attr('data-key')); })
.on('click', function (event) { if (playing && event.originalEvent.detail === 0) direction($(this).attr('data-key')); });
$(window).on('blur', pause);
$(document).on('visibilitychange', function () { if (document.hidden) pause(); });
reset();
});
Edytuj swój pen
Podaj adres e-mail użyty przy zapisie. Wyślemy link do edycji.