İçeriğe geç

Top Sınıfı

Bu eğitim, canvas animasyonlarında JavaScript sınıflarının nasıl kullanılacağını açıklamaktadır ve yeni başlayanlar için tasarlanmamıştır. Önce Aydınlatma Motoru bölümümüzü incelemenizi ve Renk Döngüsü eğitimini denemenizi öneririm.

Bununla birlikte, burada katı sınıf sözdizimini kullanmayacağım çünkü bu sadece JavaScript’in “sözdizimi şekeri”dir ve temel kavramlar aynı kalır. Daha karmaşık animasyonlar hakkında bilgi edinmek istiyorsanız, kapsamlı bir giriş için bu sayfaya göz atmanızı öneririm. Yoksa temel konulara devam edelim.

Başlamak için, bir sınıf basitçe normal bir JavaScript fonksiyonudur. Aynı şekilde çalışır, ancak değerleri fonksiyonun belirli bir örneğine bağlama yeteneği ekleriz. Bu örnekte, temel fizik kurallarına göre davranan bir top oluşturdum. Top, konumunu ve hızını, ayrıca üzerinde etkili olan “yerçekimini” takip eder.

<head>
<title>Simple Ball Bounce</title>
<meta description="Ball bounce"/>
<meta publisher="WhirlwindFX" />
</head>
<body style="margin: 0; padding: 0;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
// Canvas öğesini DOM'dan al
var c = document.getElementById("exCanvas");
var ctx = c.getContext("2d");
// Canvas değişkenleri
var width = 320;
var height = 200;
// Topun X konumu
let ballX = 160;
// Topun Y konumu
let ballY = 100;
// ballY'nin zamanla değişimi. Pozitif değerler aşağı, negatif değerler yukarı hareket ettirir
let velocityY = 0;
// velocityY'nin zamanla değişimi.
let accelerationY = 1;
// Efektler dizisi
let effects = [];
function update() {
// 1. Önceki kareyi üzerine yaz
ctx.fillStyle = background;
ctx.fillRect(0, 0, 320,200);
// 2. Efektler dizisinde bir Ball sınıfı örneği olduğundan emin ol
if (effects.length < 1){
effects.push(new Ball());
}
// 3. Efektler dizisini yinele ve her öğenin draw fonksiyonunu çağır
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
// 4. Ball sınıfını bildir
function Ball(){
// 5. Örnek değişkenlerini ayarla
this.bx = ballX;
this.by = ballY;
this.vy = velocityY;
this.ay = accelerationY;
this.radius = 15;
this.impact = false;
// 6. Draw fonksiyonunu tanımla
this.draw = function(){
// 7. Y hızını Y ivmesiyle değiştir
this.vy += this.ay;
// 8. Yerle bir çarpışma algılandıysa hızı yukarı ayarla ve çarpışma boolean'ını false yap. Bu kontrol bir sonraki fonksiyondan önce gerçekleşir, böylece çarpışma karesini çizeriz, atlamayız.
if (this.impact){
this.vy = -10;
this.impact = false;
}
// 9. Gelecekteki bir çarpışmayı tespit et. Top konumu artı yarıçapı artı hızı topu zeminin altına koyacaksa birkaç işlem yaparız -
if (this.by + this.radius + this.vy > 200){
// Topu bu karede yerleştirdiğimiz yerde kalması için hızı 0 yap.
this.vy = 0;
// Bir sonraki döngünün yukarı zıplamayı çizmesi için impact'i true yap
this.impact = true;
// Sonraki kare yukarı zıplamayı çizer. Bu kare gelecekteki bir çarpışmayı tespit etti. Çarpışmanın kendisini görmek istiyorsak bunu zorlamak gerekir. Topun dış yarıçapı zeminin altındayken topu yukarı taşırız.
while (this.by + this.radius < 200){
this.by++;
}
}
// 10. Y bileşenini Y hızıyla değiştir.
this.by += this.vy;
// Şekli çiz.
ctx.beginPath();
ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "black"
ctx.fill();
}
}
window.requestAnimationFrame(update);
</script>
<head>
<title>Simple Ball Bounce</title>
<meta description="Ball bounce"/>
<meta publisher="WhirlwindFX" />
</head>
<body style="margin: 0; padding: 0;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
// Canvas öğesini DOM'dan al
var c = document.getElementById("exCanvas");
var ctx = c.getContext("2d");
// Canvas değişkenleri
var width = 320;
var height = 200;
// Topun X konumu
let ballX = 160;
// Topun Y konumu
let ballY = 100;
// ballY'nin zamanla değişimi. Pozitif değerler aşağı, negatif değerler yukarı hareket ettirir
let velocityY = 0;
// velocityY'nin zamanla değişimi.
let accelerationY = 1;
// Efektler dizisi
let effects = [];
function update() {
// 1. Önceki kareyi üzerine yaz
ctx.fillStyle = background;
ctx.fillRect(0, 0, 320,200);
// 2. Efektler dizisinde bir Ball sınıfı örneği olduğundan emin ol
if (effects.length < 1){
effects.push(new Ball());
}
// 3. Efektler dizisini yinele ve her öğenin draw fonksiyonunu çağır
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
// 4. Ball sınıfını bildir
function Ball(){
// 5. Örnek değişkenlerini ayarla
this.bx = ballX;
this.by = ballY;
this.vy = velocityY;
this.ay = accelerationY;
this.radius = 15;
this.impact = false;
// 6. Draw fonksiyonunu tanımla
this.draw = function(){
// 7. Y hızını Y ivmesiyle değiştir
this.vy += this.ay;
// 8. Yerle bir çarpışma algılandıysa hızı yukarı ayarla ve çarpışma boolean'ını false yap. Bu kontrol bir sonraki fonksiyondan önce gerçekleşir, böylece çarpışma karesini çizeriz, atlamayız.
if (this.impact){
this.vy = -10;
this.impact = false;
}
// 9. Gelecekteki bir çarpışmayı tespit et. Top konumu artı yarıçapı artı hızı topu zeminin altına koyacaksa birkaç işlem yaparız -
if (this.by + this.radius + this.vy > 200){
// Topu bu karede yerleştirdiğimiz yerde kalması için hızı 0 yap.
this.vy = 0;
// Bir sonraki döngünün yukarı zıplamayı çizmesi için impact'i true yap
this.impact = true;
// Sonraki kare yukarı zıplamayı çizer. Bu kare gelecekteki bir çarpışmayı tespit etti. Çarpışmanın kendisini görmek istiyorsak bunu zorlamak gerekir. Topun dış yarıçapı zeminin altındayken topu yukarı taşırız.
while (this.by + this.radius < 200){
this.by++;
}
}
// 10. Y bileşenini Y hızıyla değiştir.
this.by += this.vy;
// Şekli çiz.
ctx.beginPath();
ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "black"
ctx.fill();
}
}
window.requestAnimationFrame(update);
</script>
function update() {
// 1. Önceki kareyi üzerine yaz
ctx.fillStyle = background;
ctx.fillRect(0, 0, 320,200);
// 2. Efektler dizisinde bir Ball sınıfı örneği olduğundan emin ol
if (effects.length < 1){
effects.push(new Ball());
}
// 3. Efektler dizisini yinele ve her öğenin draw fonksiyonunu çağır
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
// 4. Ball sınıfını bildir
function Ball(){
// 5. Örnek değişkenlerini ayarla
this.bx = ballX;
this.by = ballY;
this.vy = velocityY;
this.ay = accelerationY;
this.radius = 15;
this.impact = false;
// 6. Draw fonksiyonunu tanımla
this.draw = function(){
// 7. Y hızını Y ivmesiyle değiştir
this.vy += this.ay;
// 8. Yerle bir çarpışma algılandıysa hızı yukarı ayarla ve çarpışma boolean'ını false yap. Bu kontrol bir sonraki fonksiyondan önce gerçekleşir, böylece çarpışma karesini çizeriz, atlamayız.
if (this.impact){
this.vy = -10;
this.impact = false;
}
// 9. Gelecekteki bir çarpışmayı tespit et. Top konumu artı yarıçapı artı hızı topu zeminin altına koyacaksa birkaç işlem yaparız -
if (this.by + this.radius + this.vy > 200){
// Topu bu karede yerleştirdiğimiz yerde kalması için hızı 0 yap.
this.vy = 0;
// Bir sonraki döngünün yukarı zıplamayı çizmesi için impact'i true yap
this.impact = true;
// Sonraki kare yukarı zıplamayı çizer. Bu kare gelecekteki bir çarpışmayı tespit etti. Çarpışmanın kendisini görmek istiyorsak bunu zorlamak gerekir. Topun dış yarıçapı zeminin altındayken topu yukarı taşırız.
while (this.by + this.radius < 200){
this.by++;
}
}
// 10. Y bileşenini Y hızıyla değiştir.
this.by += this.vy;
// Şekli çiz.
ctx.beginPath();
ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "black"
ctx.fill();
}
}

İlk karmaşıklıkla başa çıktıktan sonra, daha gelişmiş davranışlar eklemek daha kolay hale gelir. Bir sonraki örnekte duvar çarpışmalarını dahil ettim ve topu hareket halinde tutmak için X yönünde bazı rastgele varyasyonlar ekledim.

<head>
<title>Full Bounce</title>
<meta description="Snake-like climbing gradient effect"/>
<meta publisher="WhirlwindFX" />
</head>
<body style="margin: 0; padding: 0;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
// Canvas öğesini DOM'dan al
var c = document.getElementById("exCanvas");
var ctx = c.getContext("2d");
var width = 320;
var height = 200;
var hue = 0;
let ballX = 160;
// 1. velocityX eklendi
let velocityX = 0;
let ballY = 100;
let velocityY = 0;
let accelerationY = 1;
let effects = [];
function update() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
if (effects.length < 1){
effects.push(new Ball());
}
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
function Ball(){
this.bx = ballX;
// 2. X'e rastgele bir başlangıç hızı ver
this.vx = 100 * Math.sin(Date.now());
this.by = ballY;
this.vy = velocityY;
this.ay = accelerationY;
this.radius = 15;
// 3. Yatay temas için impact boolean'ına X ve Y bileşenleri ekle
this.impactX = false;
this.impactY = false;
this.draw = function(){
this.vy += this.ay;
// 4. Her karede, ivmeyi korumak için X yönünde küçük miktarda rastgele hız ekle
this.vx += Math.sin(Date.now());
if (this.impactY){
this.vy = -10;
this.impactY = false;
}
// 5. Yatay bir çarpışma algılanırsa X hızını ters çevir ve kontrolden çıkan topu önlemek için küçük miktarda kuvveti çıkar. Buradaki kuvveti yansıtmak veya eklemek ilginç sonuçlar doğurabilir.
if (this.impactX){
this.vx *= -.9;
this.impactX = false;
}
if (this.by + this.radius + this.vy > height){
this.vy = 0;
this.impactY = true;
while (this.by + this.radius < height){
this.by++;
}
}
// 6. Sol veya sağ yatay çarpışmayı tespit et ve Y bileşeninde yaptığımız gibi ayarla.
if (this.bx + this.radius + this.vx > width){
this.vx = 0;
this.impactX = true;
while (this.bx + this.radius > width){
this.bx--;
}
} else if (this.bx - this.radius + this.vx < 0){
this.vx = 0;
this.impactX = true;
while (this.bx - this.radius < 0){
this.bx++;
}
}
// 7. X ve Y bileşenlerini hızlarıyla değiştir.
this.by += this.vy;
this.bx += this.vx;
ctx.beginPath();
ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "black"
ctx.fill();
}
}
window.requestAnimationFrame(update);
</script>
<head>
<title>Full Bounce</title>
<meta description="Snake-like climbing gradient effect"/>
<meta publisher="WhirlwindFX" />
</head>
<body style="margin: 0; padding: 0;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
// Canvas öğesini DOM'dan al
var c = document.getElementById("exCanvas");
var ctx = c.getContext("2d");
var width = 320;
var height = 200;
var hue = 0;
let ballX = 160;
// 1. velocityX eklendi
let velocityX = 0;
let ballY = 100;
let velocityY = 0;
let accelerationY = 1;
let effects = [];
function update() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
if (effects.length < 1){
effects.push(new Ball());
}
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
function Ball(){
this.bx = ballX;
// 2. X'e rastgele bir başlangıç hızı ver
this.vx = 100 * Math.sin(Date.now());
this.by = ballY;
this.vy = velocityY;
this.ay = accelerationY;
this.radius = 15;
// 3. Yatay temas için impact boolean'ına X ve Y bileşenleri ekle
this.impactX = false;
this.impactY = false;
this.draw = function(){
this.vy += this.ay;
// 4. Her karede, ivmeyi korumak için X yönünde küçük miktarda rastgele hız ekle
this.vx += Math.sin(Date.now());
if (this.impactY){
this.vy = -10;
this.impactY = false;
}
// 5. Yatay bir çarpışma algılanırsa X hızını ters çevir ve kontrolden çıkan topu önlemek için küçük miktarda kuvveti çıkar. Buradaki kuvveti yansıtmak veya eklemek ilginç sonuçlar doğurabilir.
if (this.impactX){
this.vx *= -.9;
this.impactX = false;
}
if (this.by + this.radius + this.vy > height){
this.vy = 0;
this.impactY = true;
while (this.by + this.radius < height){
this.by++;
}
}
// 6. Sol veya sağ yatay çarpışmayı tespit et ve Y bileşeninde yaptığımız gibi ayarla.
if (this.bx + this.radius + this.vx > width){
this.vx = 0;
this.impactX = true;
while (this.bx + this.radius > width){
this.bx--;
}
} else if (this.bx - this.radius + this.vx < 0){
this.vx = 0;
this.impactX = true;
while (this.bx - this.radius < 0){
this.bx++;
}
}
// 7. X ve Y bileşenlerini hızlarıyla değiştir.
this.by += this.vy;
this.bx += this.vx;
ctx.beginPath();
ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "black"
ctx.fill();
}
}
window.requestAnimationFrame(update);
</script>
function update() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
if (effects.length < 1){
effects.push(new Ball());
}
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
function Ball(){
this.bx = ballX;
// 2. X'e rastgele bir başlangıç hızı ver
this.vx = 100 * Math.sin(Date.now());
this.by = ballY;
this.vy = velocityY;
this.ay = accelerationY;
this.radius = 15;
// 3. Yatay temas için impact boolean'ına X ve Y bileşenleri ekle
this.impactX = false;
this.impactY = false;
this.draw = function(){
this.vy += this.ay;
// 4. Her karede, ivmeyi korumak için X yönünde küçük miktarda rastgele hız ekle
this.vx += Math.sin(Date.now());
if (this.impactY){
this.vy = -10;
this.impactY = false;
}
// 5. Yatay bir çarpışma algılanırsa X hızını ters çevir ve kontrolden çıkan topu önlemek için küçük miktarda kuvveti çıkar. Buradaki kuvveti yansıtmak veya eklemek ilginç sonuçlar doğurabilir.
if (this.impactX){
this.vx *= -.9;
this.impactX = false;
}
if (this.by + this.radius + this.vy > height){
this.vy = 0;
this.impactY = true;
while (this.by + this.radius < height){
this.by++;
}
}
// 6. Sol veya sağ yatay çarpışmayı tespit et ve Y bileşeninde yaptığımız gibi ayarla.
if (this.bx + this.radius + this.vx > width){
this.vx = 0;
this.impactX = true;
while (this.bx + this.radius > width){
this.bx--;
}
} else if (this.bx - this.radius + this.vx < 0){
this.vx = 0;
this.impactX = true;
while (this.bx - this.radius < 0){
this.bx++;
}
}
// 7. X ve Y bileşenlerini hızlarıyla değiştir.
this.by += this.vy;
this.bx += this.vx;
ctx.beginPath();
ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "black"
ctx.fill();
}
}

Dürüst olmak gerekirse, bu örnek büyük C harfiyle Karmaşıktır ve büyük olasılıkla çoğu temel efekt için ihtiyaç duyacağınızdan daha fazla çaba gerektirir. Kodda çok fazla yorum eklemekten kurtulmak için, burada birkaç zorlu kavramı ele alacağız.

İlk olarak, bir top başka bir topla çarpışacağını nasıl bilir? Her karede, top sınıfının her örneği diğer her top örneğinin konumunu kontrol etmelidir. Konumlarını karşılaştırarak iki topun birbirinin yarıçapı içinde olup olmadığını belirleyebilir ve ardından X ile Y hızlarını buna göre ters çevirebiliriz.

Bazen, bir top başka bir toptan geçecek kadar yüksek hız kazanabilir ve neredeyse aynı alanı işgal edebilirler. Her iki topun konumunu ayarlamak için gereken matematik çok karmaşık olacağından, şu anda kontrol ettiğimiz top örneğini kaldırıyorum. Güncelleme fonksiyonu minimum sayıda topu garantilediğinden, yeni bir top örneği oluşturulacak ve simülasyon devam edecektir.

Son olarak, her topun hız ve konum değişimini ne zaman hesaplamalıyız? Topun değişkenler güncellenmeden önce mi yoksa sonra mı çizildiği önemli midir? Programlamadaki sorun, her iki sorunun da birden fazla yanıtının olmasıdır. Bununla başa çıkmak için çarpışmaları takip etmenin biraz farklı bir yöntemini tanıttım. Tercih ettiğinizi seçmek size kalmış, ancak her iki yöntem de işe yarıyor.

<head>
<title>Multiple Balls</title>
<meta description="Snake-like climbing gradient effect"/>
<meta publisher="WhirlwindFX" />
</head>
<body style="margin: 0; padding: 0;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
// Canvas öğesini DOM'dan al
var c = document.getElementById("exCanvas");
var ctx = c.getContext("2d");
var width = 320;
var height = 200;
var hue = 0;
let ballX = 160;
let velocityX = 0;
let ballY = 100;
let velocityY = 0;
let accelerationY = 1;
let effects = [];
function update() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
// 1. Efektler dizisindeki top sayısını artır
if (effects.length < 3){
effects.push(new Ball());
}
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
function Ball(){
this.bx = ballX;
this.vx = 100 * Math.sin(Date.now());
this.by = ballY;
this.vy = velocityY;
this.ay = accelerationY;
this.radius = 15;
// 2. Her topa benzersiz bir ID ekle, böylece birbirlerini (kendilerini değil) karşılaştırabiliriz. Daha fazla top için daha iyi bir çözüm lehine "impactX" ve "impactY"yi de kaldırdık.
this.id = Math.random();
this.draw = function(){
this.vy += this.ay;
this.vx += Math.sin(Date.now());
if (this.by + this.radius + this.vy > height){
this.vy = -10;
} else if (this.by - this.radius + this.vy < 0){
// 3. Bir top çarpışması yüksekliğin 0y'yi aşmasına neden olması durumunda canvas'a bir "tavan" ekle.
this.vy = 1;
}
if (this.bx + this.radius + this.vx > width){
this.vx = -.9;
} else if (this.bx - this.radius + this.vx < 0){
this.vx = -.9;
}
// 4. Çarpışmaları kontrol etmek için efektler dizisindeki her topu yinele
for (let i = 0; i < effects.length; i++){
let ele = effects[i];
// Kontrol ettiğimiz top bu topsa bu döngüyü atla
if (this.id == ele.id){
continue;
}
// Toplar çok fazla geçiyorsa bu topu canvas'tan kaldır
if (Math.abs(this.bx - ele.bx) < this.radius && Math.abs(this.by - ele.by) < this.radius){
effects.splice(i, 1);
}
// Toplar çarpışacaksa hızlarını ters çevir ve esnekliği hesaba katmak için biraz kuvveti çıkar
if (Math.abs(this.bx + this.vx - ele.bx) < (this.radius * 2) && Math.abs(this.by + this.vy - ele.by) < (this.radius * 2)){
this.vx *= -.9;
this.vy *= -.9;
}
}
this.by += this.vy;
this.bx += this.vx;
// 5. Hızlar için karmaşık hesaplamalar bu noktadan önce gerçekleştiğinden, X ve Y koordinatlarını canvas içinde sınırlı tutarak kesinleştir.
while (this.bx - this.radius < 0){
this.bx++
}
while (this.bx + this.radius > width){
this.bx--
}
while (this.by - this.radius < 0){
this.by++
}
while (this.by + this.radius > height){
this.by--
}
ctx.beginPath();
ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "black"
ctx.fill();
}
}
window.requestAnimationFrame(update);
</script>
<head>
<title>Multiple Balls</title>
<meta description="Snake-like climbing gradient effect"/>
<meta publisher="WhirlwindFX" />
</head>
<body style="margin: 0; padding: 0;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
// Canvas öğesini DOM'dan al
var c = document.getElementById("exCanvas");
var ctx = c.getContext("2d");
var width = 320;
var height = 200;
var hue = 0;
let ballX = 160;
let velocityX = 0;
let ballY = 100;
let velocityY = 0;
let accelerationY = 1;
let effects = [];
function update() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
// 1. Efektler dizisindeki top sayısını artır
if (effects.length < 3){
effects.push(new Ball());
}
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
function Ball(){
this.bx = ballX;
this.vx = 100 * Math.sin(Date.now());
this.by = ballY;
this.vy = velocityY;
this.ay = accelerationY;
this.radius = 15;
// 2. Her topa benzersiz bir ID ekle, böylece birbirlerini (kendilerini değil) karşılaştırabiliriz. Daha fazla top için daha iyi bir çözüm lehine "impactX" ve "impactY"yi de kaldırdık.
this.id = Math.random();
this.draw = function(){
this.vy += this.ay;
this.vx += Math.sin(Date.now());
if (this.by + this.radius + this.vy > height){
this.vy = -10;
} else if (this.by - this.radius + this.vy < 0){
// 3. Bir top çarpışması yüksekliğin 0y'yi aşmasına neden olması durumunda canvas'a bir "tavan" ekle.
this.vy = 1;
}
if (this.bx + this.radius + this.vx > width){
this.vx = -.9;
} else if (this.bx - this.radius + this.vx < 0){
this.vx = -.9;
}
// 4. Çarpışmaları kontrol etmek için efektler dizisindeki her topu yinele
for (let i = 0; i < effects.length; i++){
let ele = effects[i];
// Kontrol ettiğimiz top bu topsa bu döngüyü atla
if (this.id == ele.id){
continue;
}
// Toplar çok fazla geçiyorsa bu topu canvas'tan kaldır
if (Math.abs(this.bx - ele.bx) < this.radius && Math.abs(this.by - ele.by) < this.radius){
effects.splice(i, 1);
}
// Toplar çarpışacaksa hızlarını ters çevir ve esnekliği hesaba katmak için biraz kuvveti çıkar
if (Math.abs(this.bx + this.vx - ele.bx) < (this.radius * 2) && Math.abs(this.by + this.vy - ele.by) < (this.radius * 2)){
this.vx *= -.9;
this.vy *= -.9;
}
}
this.by += this.vy;
this.bx += this.vx;
// 5. Hızlar için karmaşık hesaplamalar bu noktadan önce gerçekleştiğinden, X ve Y koordinatlarını canvas içinde sınırlı tutarak kesinleştir.
while (this.bx - this.radius < 0){
this.bx++
}
while (this.bx + this.radius > width){
this.bx--
}
while (this.by - this.radius < 0){
this.by++
}
while (this.by + this.radius > height){
this.by--
}
ctx.beginPath();
ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "black"
ctx.fill();
}
}
window.requestAnimationFrame(update);
</script>
function update() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
// 1. Efektler dizisindeki top sayısını artır
if (effects.length < 3){
effects.push(new Ball());
}
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
function Ball(){
this.bx = ballX;
this.vx = 100 * Math.sin(Date.now());
this.by = ballY;
this.vy = velocityY;
this.ay = accelerationY;
this.radius = 15;
// 2. Her topa benzersiz bir ID ekle, böylece birbirlerini (kendilerini değil) karşılaştırabiliriz. Daha fazla top için daha iyi bir çözüm lehine "impactX" ve "impactY"yi de kaldırdık.
this.id = Math.random();
this.draw = function(){
this.vy += this.ay;
this.vx += Math.sin(Date.now());
if (this.by + this.radius + this.vy > height){
this.vy = -10;
} else if (this.by - this.radius + this.vy < 0){
// 3. Bir top çarpışması yüksekliğin 0y'yi aşmasına neden olması durumunda canvas'a bir "tavan" ekle.
this.vy = 1;
}
if (this.bx + this.radius + this.vx > width){
this.vx = -.9;
} else if (this.bx - this.radius + this.vx < 0){
this.vx = -.9;
}
// 4. Çarpışmaları kontrol etmek için efektler dizisindeki her topu yinele
for (let i = 0; i < effects.length; i++){
let ele = effects[i];
// Kontrol ettiğimiz top bu topsa bu döngüyü atla
if (this.id == ele.id){
continue;
}
// Toplar çok fazla geçiyorsa bu topu canvas'tan kaldır
if (Math.abs(this.bx - ele.bx) < this.radius && Math.abs(this.by - ele.by) < this.radius){
effects.splice(i, 1);
}
// Toplar çarpışacaksa hızlarını ters çevir ve esnekliği hesaba katmak için biraz kuvveti çıkar
if (Math.abs(this.bx + this.vx - ele.bx) < (this.radius * 2) &&
Math.abs(this.by + this.vy - ele.by) < (this.radius * 2)){
this.vx *= -.9;
this.vy *= -.9;
}
}
this.by += this.vy;
this.bx += this.vx;
// 5. Hızlar için karmaşık hesaplamalar bu noktadan önce gerçekleştiğinden, X ve Y koordinatlarını canvas içinde sınırlı tutarak kesinleştir.
while (this.bx - this.radius < 0){
this.bx++
}
while (this.bx + this.radius > width){
this.bx--
}
while (this.by - this.radius < 0){
this.by++
}
while (this.by + this.radius > height){
this.by--
}
ctx.beginPath();
ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "black"
ctx.fill();
}
}

Son olarak, işleri daha ilginç hale getirmek için bazı temel kullanıcı kontrolleri ekleyeceğiz. Renk tonu değişikliklerini zaten ayrıntılı olarak ele aldım, bu nedenle kodu daha da karmaşık hale getirmemek için topların sayısını ve yarıçapını ayarlamaya odaklanacağız. Bu yalnızca mevcut fonksiyonlarda birkaç değişiklik gerektirir.

<head>
<title>User Controls</title>
<meta description="Snake-like climbing gradient effect"/>
<meta publisher="WhirlwindFX" />
<!-- İki sayısal kaydırıcı ekle: yarıçap ve miktar --->
<meta property="radius" label="Size" type="number" min="5" max="100" default="20">
<meta property="amount" label="Amount" type="number" min="1" max="50" default="2">
</head>
function update() {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
// Minimum efekt sayısını kullanıcının kontrol ettiği miktar olarak ayarla. Efektlerin uzunluğu miktardan fazlaysa sondan bir öğe çıkar.
if (effects.length < amount){
effects.push(new Ball());
} else if (effects.length > amount){
effects.pop();
}
effects.forEach(ele => {
ele.draw();
})
window.requestAnimationFrame(update);
}
this.draw = function(){
// Son olarak, her karede draw fonksiyonunun içinde yarıçapı kullanıcının kontrol ettiği yarıçap olarak ayarla.
this.radius = radius;
this.vy += this.ay;
this.vx += Math.sin(Date.now());
// . . . fonksiyonun geri kalanı . . .
}

JavaScript sınıfı animasyon eğitimimizi beğendiğinizi umuyorum! Bu karmaşıklık düzeyinde bile bu efekt, HTML canvas ile başarabileceklerinizin yalnızca küçük bir kısmını oluşturmaktadır. Ayrıca henüz tamamlanmış saymıyorum. Mükemmeliyeti hedeflersek, top çarpışmalarının gerçekleşmeden bir kare önce tespit edildiğini fark edeceksiniz; bu da genellikle gerçekten çarpışmadıkları anlamına gelir. Çarpışmalar, enerji transferi açısından da basitleştirilmiştir; çünkü elde edilen hız yalnızca topun mevcut hızını dikkate alır ve diğer topun enerjisini hesaba katmaz. Son olarak, her topun konumunu döngü başına birden fazla kontrol etmek zaman karmaşıklığı açısından verimsizdir ve temel memoization ile optimize edilebilir.

Sonuç olarak, efektlerinizi her zaman eleştirin. Genellikle bir şekilde geliştirilebilirler; vasat bir efekt ile harika bir efekt arasındaki fark çoğunlukla ona bakıp en az bir kez beğenmemenize izin vermekten kaynaklanır.

Son adım adım eğitim için, Callbacks bölümümüze göz atın; burada efektleri etkinleştirmek ve devre dışı bırakmak için metre verilerini yakalama sürecini adım adım anlatıyorum.