BLOK — rozbijacz cegieł
GryPodgląd
Ładowanie podglądu…
Odbijaj piłkę, zbij 24 cegły i pilnuj trzech żyć. Paletka podąża za wskaźnikiem lub strzałkami.
Kod źródłowy
Licencja MIT — także do użytku komercyjnego. Zachowaj informacje o autorze i licencji.
HTML
<main><header><span class="eyebrow">Rozbijanie cegieł</span><h1>BLOK / 24</h1><p>Rozbij układ. Utrzymaj piłkę w grze.</p></header><div class="desktop-split"><section aria-label="Plansza"><canvas id="screen" width="600" height="400" tabindex="0" aria-label="BLOK / 24. Przesuwaj paletkę myszą, palcem lub strzałkami. Masz trzy życia.">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">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>Przesuwaj paletkę myszą, palcem lub strzałkami. Masz trzy życia.</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:#f5eee3;--ink:#172153;--focus:#d62e28}h1{font-weight:900;font-size:clamp(44px,10vw,96px);color:#d62e28}header{display:block}.desktop-split{display:grid;grid-template-columns:minmax(0,680px) 1fr;gap:20px}aside{background:#f7c940;padding:20px}canvas{border:3px solid #172153}.metric{font-size:60px}.actions button{font-weight:800}
@media(max-width:600px){aside .metric{font-size:36px}aside .status{min-height:0;margin:8px 0}aside .actions{margin:12px 0}}
JavaScript
function brickHit(ball, brick) { return ball.x + ball.r >= brick.x && ball.x - ball.r <= brick.x + brick.w && ball.y + ball.r >= brick.y && ball.y - ball.r <= brick.y + brick.h; }
$(function () {
const canvas = document.getElementById('screen'), ctx = canvas.getContext('2d');
let ball, paddle, bricks, lives, score;
function serve() { ball = {x:300,y:280,r:7,vx:145,vy:-195}; }
function init() { paddle=250; lives=3; score=0; bricks=Array.from({length:24},(_,i)=>({x:28+(i%6)*92,y:35+Math.floor(i/6)*30,w:84,h:22,live:true}));serve();$('#score').text('0 / 24'); }
function direction(key) { if(key==='ArrowLeft')paddle=Math.max(0,paddle-16); if(key==='ArrowRight')paddle=Math.min(500,paddle+16); }
$(canvas).on('pointermove pointerdown', function(event){if(!playing)return;const box=canvas.getBoundingClientRect();paddle=Math.max(0,Math.min(500,(event.originalEvent.clientX-box.left)*600/box.width-50));});
function update(dt) {
if(held.has('ArrowLeft'))paddle=Math.max(0,paddle-dt*360);if(held.has('ArrowRight'))paddle=Math.min(500,paddle+dt*360);
const oldY=ball.y;ball.x+=ball.vx*dt;ball.y+=ball.vy*dt;
if(ball.x<7||ball.x>593){ball.x=Math.max(7,Math.min(593,ball.x));ball.vx*=-1;}if(ball.y<7){ball.y=7;ball.vy=Math.abs(ball.vy);}
if(ball.vy>0 && oldY+7<=365 && ball.y+7>=365 && ball.x>=paddle-7 && ball.x<=paddle+107){ball.y=358;ball.vy=-Math.abs(ball.vy);ball.vx=(ball.x-paddle-50)*4;}
for(const brick of bricks){if(brick.live&&brickHit(ball,brick)){brick.live=false;ball.vy*=-1;score++;$('#score').text(score+' / 24');break;}}
if(score===24){finish('Wszystkie cegły rozbite!');return;}
if(ball.y>410){lives--;if(!lives){finish('Koniec gry. Zbite cegły: '+score+'.');return;}serve();$('#status').text('Pozostałe życia: '+lives+'.');}
}
function draw() {
ctx.fillStyle='#f5eee3';ctx.fillRect(0,0,600,400);
bricks.forEach((b,i)=>{if(b.live){ctx.fillStyle=['#d62e28','#172153','#eebc24','#287f64'][Math.floor(i/6)];ctx.fillRect(b.x,b.y,b.w,b.h);}});
ctx.fillStyle='#172153';ctx.fillRect(paddle,365,100,12);ctx.beginPath();ctx.arc(ball.x,ball.y,ball.r,0,Math.PI*2);ctx.fill();
ctx.font='14px monospace';ctx.fillText('ŻYCIA '+lives,28,330);
}
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.