Ball Class
Tutorial ini menjelaskan cara menggunakan kelas JavaScript dalam animasi canvas dan tidak ditujukan untuk pemula. Saya merekomendasikan untuk terlebih dahulu mempelajari bagian Lighting Engine kami dan mencoba tutorial Color Cycle.
Meski begitu, saya tidak akan menggunakan sintaks kelas yang ketat di sini karena itu hanyalah “syntactic sugar” JavaScript, dan konsep yang mendasarinya tetap sama. Jika Anda tertarik mempelajari animasi yang lebih kompleks, saya sarankan untuk memeriksa halaman ini untuk pengenalan yang menyeluruh. Jika tidak, mari kita tetap pada hal-hal dasar.
Untuk memulai, kelas hanyalah fungsi JavaScript biasa. Cara kerjanya sama, tetapi kita menambahkan kemampuan untuk mengikat nilai ke instansi tertentu dari fungsi tersebut. Dalam contoh ini, saya telah membuat bola yang berperilaku sesuai dengan fisika dasar. Ia melacak posisi dan kecepatannya, serta “gravitasi” yang bekerja padanya.
Simple Bounce
Section titled “Simple Bounce”<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> // Get the canvas element from the DOM var c = document.getElementById("exCanvas"); var ctx = c.getContext("2d"); // Canvas variables var width = 320; var height = 200; // X-component of ball position let ballX = 160; // Y-component of ball position let ballY = 100; // Change in ballY over time. Positive values move down, negative move up let velocityY = 0; // Change in velocityY over time. let accelerationY = 1; // Effects array let effects = [];
function update() { // 1. Overwrite the previous frame ctx.fillStyle = background; ctx.fillRect(0, 0, 320,200);
// 2. Make sure there is one instance of the Ball class in the effects array if (effects.length < 1){ effects.push(new Ball()); }
// 3. Iterate through the effects array and call each element's draw function effects.forEach(ele => { ele.draw(); })
window.requestAnimationFrame(update); }
// 4. Declare the Ball class function Ball(){ // 5. Set instance variables this.bx = ballX; this.by = ballY; this.vy = velocityY; this.ay = accelerationY; this.radius = 15; this.impact = false; // 6. Define draw function this.draw = function(){ // 7. Change the y-velocity by the y-acceleration this.vy += this.ay;
// 8. If an impact with the ground has been detected, set the velocity upwards and set the impact boolean to false. This check happens before the next function so that we draw the impact frame instead of skipping it. if (this.impact){ this.vy = -10; this.impact = false; }
// 9. Detect a future impact. If the ball position plus its radius plus its velocity would put it under the floor, we run a few operations - if (this.by + this.radius + this.vy > 200){ // Set the velocity to 0 so the ball stays where we put it this frame. this.vy = 0; // Set impact to true so the next run-through draws the up-bounce this.impact = true; // Next frame draws the up-bounce. This frame detected a future collision. If we want to see the impact itself we have to force it. While the ball's outer radius is below the floor, we move the ball up. while (this.by + this.radius < 200){ this.by++; } } // 10. Change the y-component by the y-velocity. this.by += this.vy; // Draw shape. 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> // Get the canvas element from the DOM var c = document.getElementById("exCanvas"); var ctx = c.getContext("2d"); // Canvas variables var width = 320; var height = 200; // X-component of ball position let ballX = 160; // Y-component of ball position let ballY = 100; // Change in ballY over time. Positive values move down, negative move up let velocityY = 0; // Change in velocityY over time. let accelerationY = 1; // Effects array let effects = [];
function update() { // 1. Overwrite the previous frame ctx.fillStyle = background; ctx.fillRect(0, 0, 320,200);
// 2. Make sure there is one instance of the Ball class in the effects array if (effects.length < 1){ effects.push(new Ball()); }
// 3. Iterate through the effects array and call each element's draw function effects.forEach(ele => { ele.draw(); })
window.requestAnimationFrame(update); }
// 4. Declare the Ball class function Ball(){ // 5. Set instance variables this.bx = ballX; this.by = ballY; this.vy = velocityY; this.ay = accelerationY; this.radius = 15; this.impact = false; // 6. Define draw function this.draw = function(){ // 7. Change the y-velocity by the y-acceleration this.vy += this.ay;
// 8. If an impact with the ground has been detected, set the velocity upwards and set the impact boolean to false. This check happens before the next function so that we draw the impact frame instead of skipping it. if (this.impact){ this.vy = -10; this.impact = false; }
// 9. Detect a future impact. If the ball position plus its radius plus its velocity would put it under the floor, we run a few operations - if (this.by + this.radius + this.vy > 200){ // Set the velocity to 0 so the ball stays where we put it this frame. this.vy = 0; // Set impact to true so the next run-through draws the up-bounce this.impact = true; // Next frame draws the up-bounce. This frame detected a future collision. If we want to see the impact itself we have to force it. While the ball's outer radius is below the floor, we move the ball up. while (this.by + this.radius < 200){ this.by++; } } // 10. Change the y-component by the y-velocity. this.by += this.vy; // Draw shape. 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. Overwrite the previous frame ctx.fillStyle = background; ctx.fillRect(0, 0, 320,200);
// 2. Make sure there is one instance of the Ball class in the effects array if (effects.length < 1){ effects.push(new Ball()); }
// 3. Iterate through the effects array and call each element's draw function effects.forEach(ele => { ele.draw(); })
window.requestAnimationFrame(update); }// 4. Declare the Ball class function Ball(){ // 5. Set instance variables this.bx = ballX; this.by = ballY; this.vy = velocityY; this.ay = accelerationY; this.radius = 15; this.impact = false; // 6. Define draw function this.draw = function(){ // 7. Change the y-velocity by the y-acceleration this.vy += this.ay;
// 8. If an impact with the ground has been detected, set the velocity upwards and set the impact boolean to false. This check happens before the next function so that we draw the impact frame instead of skipping it. if (this.impact){ this.vy = -10; this.impact = false; }
// 9. Detect a future impact. If the ball position plus its radius plus its velocity would put it under the floor, we run a few operations - if (this.by + this.radius + this.vy > 200){ // Set the velocity to 0 so the ball stays where we put it this frame. this.vy = 0; // Set impact to true so the next run-through draws the up-bounce this.impact = true; // Next frame draws the up-bounce. This frame detected a future collision. If we want to see the impact itself we have to force it. While the ball's outer radius is below the floor, we move the ball up. while (this.by + this.radius < 200){ this.by++; } } // 10. Change the y-component by the y-velocity. this.by += this.vy; // Draw shape. ctx.beginPath(); ctx.arc(this.bx, this.by, this.radius, 0, Math.PI * 2); ctx.fillStyle = "black" ctx.fill(); } }
Full Bounce
Section titled “Full Bounce”Setelah kita melewati kompleksitas awal, lebih mudah untuk menambahkan perilaku yang lebih canggih. Dalam contoh berikutnya, saya telah menggabungkan tabrakan dinding dan memperkenalkan beberapa variasi acak dalam arah x untuk menjaga bola tetap bergerak.
<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> // Get the canvas element from the DOM var c = document.getElementById("exCanvas"); var ctx = c.getContext("2d"); var width = 320; var height = 200; var hue = 0; let ballX = 160; // 1. Added velocityX 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. Give x a random initial velocity this.vx = 100 * Math.sin(Date.now()); this.by = ballY; this.vy = velocityY; this.ay = accelerationY; this.radius = 15; // 3. Add x and y-components to the impact boolean for horizontal contact this.impactX = false; this.impactY = false;
this.draw = function(){ this.vy += this.ay;
// 4. Each frame, add a small amount of random velocity in the x-direction to keep momentum going this.vx += Math.sin(Date.now());
if (this.impactY){ this.vy = -10; this.impactY = false; } // 5. If a horizontal impact is detected, reverse the x-velocity and subtract a small amount of force to prevent an out-of-control ball. Mirroring or adding to the force here can have interesting consequences. 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. Detect a horizontal impact left or right and adjust like we did the y-component. 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. Change the x- and y-components by their velocity. 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> // Get the canvas element from the DOM var c = document.getElementById("exCanvas"); var ctx = c.getContext("2d"); var width = 320; var height = 200; var hue = 0; let ballX = 160; // 1. Added velocityX 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. Give x a random initial velocity this.vx = 100 * Math.sin(Date.now()); this.by = ballY; this.vy = velocityY; this.ay = accelerationY; this.radius = 15; // 3. Add x and y-compnents to the impact boolean for horizontal contact this.impactX = false; this.impactY = false;
this.draw = function(){ this.vy += this.ay;
// 4. Each frame, add a small amount of random velocity in the x-direction to keep momentum going this.vx += Math.sin(Date.now());
if (this.impactY){ this.vy = -10; this.impactY = false; } // 5. If a horizontal impact is detected, reverse the x-velocity and subtract a small amount of force to prevent an out-of-control ball. Mirroring or adding to the force here can have interesting consequences. 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. Detect a horizontal impact left or right and adjust like we did the y-component. 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. Change the x- and y-components by their velocity. 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. Give x a random initial velocity this.vx = 100 * Math.sin(Date.now()); this.by = ballY; this.vy = velocityY; this.ay = accelerationY; this.radius = 15; // 3. Add x and y-compnents to the impact boolean for horizontal contact this.impactX = false; this.impactY = false;
this.draw = function(){ this.vy += this.ay;
// 4. Each frame, add a small amount of random velocity in the x-direction to keep momentum going this.vx += Math.sin(Date.now());
if (this.impactY){ this.vy = -10; this.impactY = false; } // 5. If a horizontal impact is detected, reverse the x-velocity and subtract a small amount of force to prevent an out-of-control ball. Mirroring or adding to the force here can have interesting consequences. 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. Detect a horizontal impact left or right and adjust like we did the y-component. 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. Change the x- and y-components by their velocity. 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(); } }
Multiple Balls
Section titled “Multiple Balls”Saya harus jujur dengan Anda, contoh ini Rumit dengan huruf kapital R, dan kemungkinan besar memerlukan lebih banyak usaha daripada yang Anda butuhkan untuk sebagian besar efek dasar. Untuk menghemat penambahan terlalu banyak komentar dalam kode, kita akan membahas beberapa konsep yang rumit di sini.
Pertama, bagaimana sebuah bola tahu bahwa ia akan bertabrakan dengan bola lain? Setiap frame, setiap instansi kelas bola harus memeriksa posisi setiap instansi bola lainnya. Dengan membandingkan posisi mereka, kita dapat menentukan apakah dua bola berada dalam radius satu sama lain, lalu membalikkan kecepatan x dan y mereka sesuai.
Terkadang, sebuah bola mungkin mendapatkan kecepatan yang cukup untuk melewati bola lain, membuat mereka menempati hampir ruang yang sama. Matematika yang diperlukan untuk menyesuaikan kedua posisi bola akan terlalu kompleks, jadi saya hanya menghapus instansi bola yang sedang kita periksa. Karena fungsi update memastikan jumlah minimum bola, instansi bola baru akan dibuat, dan simulasi akan berlanjut.
Terakhir, kapan kita harus menghitung perubahan kecepatan dan posisi untuk setiap bola? Apakah penting apakah bola digambar sebelum atau setelah variabel diperbarui? Masalah dalam pemrograman adalah ada banyak jawaban untuk kedua pertanyaan tersebut. Untuk mengatasi hal ini, saya telah memperkenalkan metode pelacakan tabrakan yang sedikit berbeda. Terserah Anda untuk memilih yang Anda sukai, tetapi kedua metode ini dapat dan memang bekerja.
<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> // Get the canvas element from the DOM 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. Increase the number of balls present in the effects array 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. Add a unique ID to each ball so we can compare them to each other and not themselves. We have also removed "impactX" and "impactY" in favor of a better solution for more balls. 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. Add a "ceiling" to the canvas in case a ball collision causes the height to exceed 0y. 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. Iterate through each ball in the effects array to check for collisions for (let i = 0; i < effects.length; i++){ let ele = effects[i]; // If the ball we are checking against is this ball, skip this cycle if (this.id == ele.id){ continue; } // If the balls are clipping too much, remove this ball from the canvas if (Math.abs(this.bx - ele.bx) < this.radius && Math.abs(this.by - ele.by) < this.radius){ effects.splice(i, 1); } // If the balls will collide, reverse their velocities and subtract some force to account for elasticity 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. Because complicated calcualtions for the velocities happen before this point, finalize the x- and y-coordinates by making sure they are bounded within the canvas. 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> // Get the canvas element from the DOM 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. Increase the number of balls present in the effects array 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. Add a unique ID to each ball so we can compare them to each other and not themselves. We have also removed "impactX" and "impactY" in favor of a better solution for more balls. 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. Add a "ceiling" to the canvas in case a ball collision causes the height to exceed 0y. 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. Iterate through each ball in the effects array to check for collisions for (let i = 0; i < effects.length; i++){ let ele = effects[i]; // If the ball we are checking against is this ball, skip this cycle if (this.id == ele.id){ continue; } // If the balls are clipping too much, remove this ball from the canvas if (Math.abs(this.bx - ele.bx) < this.radius && Math.abs(this.by - ele.by) < this.radius){ effects.splice(i, 1); } // If the balls will collide, reverse their velocities and subtract some force to account for elasticity 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. Because complicated calcualtions for the velocities happen before this point, finalize the x- and y-coordinates by making sure they are bounded within the canvas. 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. Increase the number of balls present in the effects array 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. Add a unique ID to each ball so we can compare them to each other and not themselves. We have also removed "impactX" and "impactY" in favor of a better solution for more balls. 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. Add a "ceiling" to the canvas in case a ball collision causes the height to exceed 0y. 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. Iterate through each ball in the effects array to check for collisions for (let i = 0; i < effects.length; i++){ let ele = effects[i]; // If the ball we are checking against is this ball, skip this cycle if (this.id == ele.id){ continue; } // If the balls are clipping too much, remove this ball from the canvas if (Math.abs(this.bx - ele.bx) < this.radius && Math.abs(this.by - ele.by) < this.radius){ effects.splice(i, 1); } // If the balls will collide, reverse their velocities and subtract some force to account for elasticity 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. Because complicated calculations for the velocities happen before this point, finalize the x- and y-coordinates by making sure they are bounded within the canvas. 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(); } }
User Controls
Section titled “User Controls”Terakhir, kita akan menambahkan beberapa kontrol pengguna dasar untuk membuat segalanya lebih menarik. Saya telah membahas perubahan hue secara detail, jadi agar kode tidak semakin rumit, kita akan fokus pada penyesuaian jumlah dan radius bola. Ini hanya memerlukan beberapa modifikasi pada fungsi yang sudah ada.
<head> <title>User Controls</title> <meta description="Snake-like climbing gradient effect"/> <meta publisher="WhirlwindFX" /> <!-- Add two numerical sliders: radius and amount ---> <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);
// Set the minimum effects to be the user-controlled amount. If the length of effects is greater than amount, pop an element off the end. 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(){ // Finally, set the radius to be the user-controlled radius inside of the draw function each frame. this.radius = radius; this.vy += this.ay; this.vx += Math.sin(Date.now());
// . . . rest of function . . .}
Saya harap Anda menikmati tutorial animasi kelas JavaScript kami! Bahkan pada tingkat kompleksitas ini, efek ini hanyalah puncak gunung es dari apa yang dapat Anda capai dengan canvas HTML. Selain itu, saya belum menganggapnya selesai. Jika kita menginginkan kesempurnaan, Anda akan melihat bahwa tabrakan bola terdeteksi satu frame sebelum terjadi, yang berarti mereka sering kali tidak benar-benar bertabrakan. Tabrakan juga disederhanakan dalam hal transfer energi, karena kecepatan yang dihasilkan hanya mempertimbangkan kecepatan bola saat ini dan tidak memperhitungkan energi bola lainnya. Terakhir, memeriksa posisi setiap bola beberapa kali per loop tidak efisien dari segi kompleksitas waktu dan dapat dioptimalkan dengan memoization dasar.
Pada akhirnya, selalu kritis terhadap efek Anda. Efek tersebut biasanya dapat ditingkatkan dengan satu cara atau lainnya, dan perbedaan antara efek yang biasa-biasa saja dan yang luar biasa sering kali berasal dari sekadar menatapnya dan mengizinkan diri Anda untuk tidak menyukainya setidaknya sekali.
Untuk tutorial langkah demi langkah terakhir, lihat bagian Callbacks kami, di mana saya memandu proses pengambilan data meter untuk mengaktifkan dan menonaktifkan efek.