馃幆 Budowa Interaktywnej Ko艂yski Newtona
Poci膮gnij kule poni偶ej lub kliknij na przyciski:
Rozhu艣taj 1 kul臋
Rozhu艣taj 2 kule
Reset
Czego si臋 nauczysz:
Renderowanie w Canvas i animacja
Symulacja fizyki wahad艂a
Wykrywanie kolizji i transfer p臋du
Obs艂uga zdarze艅 myszy i dotyku
Krok 1: Struktura HTML
Zacznij od prostej struktury HTML zawieraj膮cej element canvas i przyciski steruj膮ce:
<canvas id="canvas" width="600" height="400"></canvas>
<div class="controls">
<button onclick="startSwing(1)">Swing 1 Ball</button>
<button onclick="startSwing(2)">Swing 2 Balls</button>
<button onclick="reset()">Reset</button>
</div>
Krok 2: Inicjalizacja Canvas i Sta艂ych
Skonfiguruj kontekst canvas i zdefiniuj fizyczne parametry ko艂yski:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const numBalls = 5; // Liczba kul w ko艂ysce
const ballRadius = 20; // Promie艅 ka偶dej kuli
const stringLength = 150; // D艂ugo艣膰 linki wahad艂a
const frameY = 50; // Pozycja Y g贸rnej ramy
const spacing = ballRadius * 2 + 2; // Odst臋p mi臋dzy kulami
const centerX = canvas.width / 2; // 艢rodek canvas
let balls = [];
let dragging = null; // 艢led藕, kt贸ra kula jest przeci膮gana
馃挕 Wskaz贸wka: Odst臋p mi臋dzy kulami jest nieco wi臋kszy ni偶 ich 艣rednica, aby zapobiec nak艂adaniu si臋, gdy s膮 w spoczynku.
Krok 3: Utworzenie Klasy Ball
W艂a艣ciwo艣ci Kuli
Ka偶da kula jest wahad艂em z punktem zakotwiczenia, k膮tem i pr臋dko艣ci膮:
class Ball {
constructor(index) {
this.index = index;
this.anchorX = centerX + (index - 2) * spacing;
this.anchorY = frameY;
this.angle = 0; // Aktualny k膮t od pionu
this.velocity = 0; // Pr臋dko艣膰 k膮towa
this.mass = 1;
this.stringLength = stringLength;
}
}
Obliczanie Pozycji
Konwersja wsp贸艂rz臋dnych biegunowych (k膮t) na wsp贸艂rz臋dne kartezja艅skie (x, y):
get x() {
return this.anchorX + Math.sin(this.angle) * this.stringLength;
}
get y() {
return this.anchorY + Math.cos(this.angle) * this.stringLength;
}
Koncepcja Fizyczna: U偶ywamy sinusa i cosinusa, aby przekonwertowa膰 k膮t wahad艂a na wsp贸艂rz臋dne ekranu. K膮t 0 reprezentuje kul臋 wisz膮c膮 prosto w d贸艂.
Krok 4: Implementacja Fizyki Wahad艂a
Metoda Update
Zastosuj fizyk臋 prostego wahad艂a, aby zaktualizowa膰 pozycj臋 kuli w ka偶dej klatce:
update(dt) {
const gravity = 0.5;
const damping = 0.999;
// Oblicz przyspieszenie z grawitacji
let acceleration = (-gravity / this.stringLength) * Math.sin(this.angle);
// Zaktualizuj pr臋dko艣膰 i zastosuj t艂umienie
this.velocity += acceleration * dt;
this.velocity *= damping;
// Zaktualizuj k膮t
this.angle += this.velocity * dt;
}
Wz贸r Wahad艂a:
przyspieszenie = -(g / L) 脳 sin(胃)
Rozbicie na cz臋艣ci:
gravity: Symuluje przyci膮ganie grawitacyjne (mniejsze = wolniejsze wahni臋cie)
damping: Stopniowo zwalnia ruch (realistyczne tarcie)
acceleration: Jak szybko zmienia si臋 pr臋dko艣膰
dt: Delta czasu - zapewnia sp贸jn膮 fizyk臋 niezale偶nie od liczby klatek
Krok 5: Renderowanie Kul
Narysuj ka偶d膮 kul臋 i jej link臋 na canvas:
draw() {
// Narysuj link臋
ctx.strokeStyle = '#333';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(this.anchorX, this.anchorY);
ctx.lineTo(this.x, this.y);
ctx.stroke();
// Narysuj kul臋
ctx.fillStyle = '#4a5568';
ctx.beginPath();
ctx.arc(this.x, this.y, ballRadius, 0, Math.PI * 2);
ctx.fill();
// Dodaj pod艣wietlenie dla efektu 3D
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.beginPath();
ctx.arc(this.x - 5, this.y - 5, ballRadius / 3, 0, Math.PI * 2);
ctx.fill();
}
Krok 6: Wykrywanie Kolizji
Wykryj, kiedy kule zderzaj膮 si臋 i przeka偶 p臋d mi臋dzy nimi:
function detectCollisions() {
for (let i = 0; i < balls.length - 1; i++) {
const b1 = balls[i];
const b2 = balls[i + 1];
// Oblicz odleg艂o艣膰 mi臋dzy 艣rodkami kul
const dx = b2.x - b1.x;
const dy = b2.y - b1.y;
const dist = Math.sqrt(dx * dx + dy * dy);
// Je艣li kule si臋 stykaj膮
if (dist < ballRadius * 2) {
// Zamie艅 pr臋dko艣ci (kolizja spr臋偶ysta)
const temp = b1.velocity;
b1.velocity = b2.velocity;
b2.velocity = temp;
}
}
}
Koncepcja Fizyczna: W doskonale spr臋偶ystym zderzeniu mi臋dzy obiektami o r贸wnej masie, po prostu wymieniaj膮 si臋 pr臋dko艣ciami. To w艂a艣nie tworzy kultowy efekt ko艂yski Newtona!
Krok 7: P臋tla Animacji
Utw贸rz g艂贸wn膮 p臋tl臋 animacji, kt贸ra aktualizuje i renderuje wszystko:
function animate() {
const dt = 0.5; // Krok czasowy dla fizyki
// Aktualizuj wszystkie kule (z wyj膮tkiem tej przeci膮ganej)
balls.forEach(ball => {
if (dragging !== ball) {
ball.update(dt);
}
});
// Sprawd藕 kolizje
detectCollisions();
// Renderuj wszystko
draw();
// Popro艣 o nast臋pn膮 klatk臋
requestAnimationFrame(animate);
}
馃挕 Wskaz贸wka: requestAnimationFrame automatycznie synchronizuje si臋 z cz臋stotliwo艣ci膮 od艣wie偶ania przegl膮darki (zazwyczaj 60 FPS) dla p艂ynnej animacji.
Krok 8: Funkcja Rysowania
Wyczy艣膰 canvas i narysuj wszystkie elementy:
function draw() {
// Wyczy艣膰 canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Narysuj g贸rn膮 ram臋
ctx.strokeStyle = '#2d3748';
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(50, frameY);
ctx.lineTo(canvas.width - 50, frameY);
ctx.stroke();
// Narysuj wszystkie kule
balls.forEach(ball => ball.draw());
}
Krok 9: Interakcja Mysz膮
Przycisk Myszy w D贸艂 - Rozpocznij Przeci膮ganie
canvas.addEventListener('mousedown', e => {
const rect = canvas.getBoundingClientRect();
const mx = e.clientX - rect.left;
const my = e.clientY - rect.top;
// Sprawd藕, czy mysz jest nad kt贸r膮kolwiek kul膮
for (let ball of balls) {
const dx = mx - ball.x;
const dy = my - ball.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < ballRadius) {
dragging = ball;
ball.velocity = 0; // Zatrzymaj jej ruch
break;
}
}
});
Ruch Myszy - Aktualizacja Pozycji Kuli
canvas.addEventListener('mousemove', e => {
if (dragging) {
const rect = canvas.getBoundingClientRect();
const mx = e.clientX - rect.left;
const my = e.clientY - rect.top;
// Oblicz k膮t od kotwicy do myszy
const dx = mx - dragging.anchorX;
const dy = my - dragging.anchorY;
dragging.angle = Math.atan2(dx, dy);
}
});
Przycisk Myszy w G贸r臋 - Zwolnij Kul臋
canvas.addEventListener('mouseup', () => {
dragging = null;
});
canvas.addEventListener('mouseleave', () => {
dragging = null;
});
Krok 10: Obs艂uga Dotyku
Dodaj obs艂ug臋 ekranu dotykowego z podobn膮 logik膮 do zdarze艅 myszy:
canvas.addEventListener('touchstart', e => {
e.preventDefault(); // Zapobiegaj przewijaniu
const rect = canvas.getBoundingClientRect();
const touch = e.touches[0];
const mx = touch.clientX - rect.left;
const my = touch.clientY - rect.top;
for (let ball of balls) {
const dx = mx - ball.x;
const dy = my - ball.y;
if (Math.sqrt(dx * dx + dy * dy) < ballRadius) {
dragging = ball;
ball.velocity = 0;
break;
}
}
});
canvas.addEventListener('touchmove', e => {
e.preventDefault();
if (dragging) {
const rect = canvas.getBoundingClientRect();
const touch = e.touches[0];
const mx = touch.clientX - rect.left;
const my = touch.clientY - rect.top;
const dx = mx - dragging.anchorX;
const dy = my - dragging.anchorY;
dragging.angle = Math.atan2(dx, dy);
}
});
canvas.addEventListener('touchend', () => {
dragging = null;
});
canvas.addEventListener('touchcancel', () => {
dragging = null;
});
馃挕 Wa偶ne: Zawsze wywo艂uj e.preventDefault() dla zdarze艅 dotykowych, aby zapobiec przewijaniu strony podczas przeci膮gania.
Krok 11: Funkcje Pomocnicze
Inicjalizacja Ko艂yski
function init() {
balls = [];
for (let i = 0; i < numBalls; i++) {
balls.push(new Ball(i));
}
}
Rozpocznij Automatyczne Wahni臋cie
function startSwing(count) {
reset();
for (let i = 0; i < count; i++) {
balls[i].angle = -0.6; // Odchyl o 0.6 radiana
}
}
Zresetuj Wszystkie Kule
function reset() {
balls.forEach(ball => {
ball.angle = 0;
ball.velocity = 0;
});
}
Krok 12: Uruchomienie Symulacji
Na koniec zainicjalizuj kule i uruchom p臋tl臋 animacji:
init();
animate();
馃帗 Podsumowanie Kluczowych Koncepcji
Symulacja Fizyczna
Ruch Wahad艂a: Grawitacja tworzy przyspieszenie proporcjonalne do sin(k膮ta)
Kolizja Spr臋偶ysta: Obiekty o r贸wnej masie wymieniaj膮 si臋 pr臋dko艣ciami
T艂umienie: Mno偶enie pr臋dko艣ci przez 0.999 symuluje op贸r powietrza
Techniki Canvas
clearRect: Wyczy艣膰 canvas przed ka偶d膮 klatk膮
Konwersja Wsp贸艂rz臋dnych: U偶yj sin/cos do konwersji k膮t贸w na pozycje
requestAnimationFrame: P艂ynna animacja 60 FPS
Interakcja
getBoundingClientRect: Konwertuj wsp贸艂rz臋dne ekranu na wsp贸艂rz臋dne canvas
Wz贸r Odleg艂o艣ci: sqrt(dx虏 + dy虏) do sprawdzenia, czy mysz jest nad kul膮
atan2: Oblicz k膮t z dw贸ch punkt贸w
馃殌 Pomys艂y na Ulepszenia
Dodaj r贸偶ne masy kul, aby zobaczy膰 asymetryczne kolizje
Zaimplementuj rozci膮ganie linki dla bardziej realistycznej fizyki
Dodaj efekty d藕wi臋kowe podczas kolizji
Pozw贸l u偶ytkownikom dynamicznie dodawa膰/usuwa膰 kule
Dodaj smugi za kulami, aby wizualizowa膰 ruch
Zaimplementuj r贸偶ne materia艂y (metal, guma itp.) o r贸偶nej elastyczno艣ci
Wi臋cej samouczk贸w JS