Ball-klasse
Deze tutorial legt uit hoe u JavaScript-klassen gebruikt in canvas-animaties en is niet bedoeld voor beginners. We raden u aan eerst onze sectie Verlichtingsengine door te nemen en de Kleurcyclus-tutorial te proberen.
Dat gezegd hebbende, zullen we hier geen strikte klasse-syntaxis gebruiken omdat het gewoon JavaScript “syntactische suiker” is en de onderliggende concepten hetzelfde blijven. Als u meer wilt weten over complexe animaties, raden we u aan deze pagina te raadplegen voor een uitgebreide inleiding. Laten we anders bij de basis blijven.
Om te beginnen: een klasse is simpelweg een gewone JavaScript-functie. Het werkt op dezelfde manier, maar we voegen de mogelijkheid toe om waarden te koppelen aan een specifieke instantie van de functie. In dit voorbeeld heb ik een bal gemaakt die zich gedraagt volgens de basisprincipes van de fysica. De bal houdt zijn positie en snelheid bij, evenals de “zwaartekracht” die erop werkt.
Eenvoudig stuiteren
Section titled “Eenvoudig stuiteren”<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(); } }
Volledig stuiteren
Section titled “Volledig stuiteren”Zodra we de initiële complexiteit hebben doorgewerkt, is het eenvoudiger om geavanceerder gedrag toe te voegen. In het volgende voorbeeld heb ik botsingen met wanden toegevoegd en wat willekeurige variatie in de x-richting geïntroduceerd om de bal in beweging te houden.
<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(); } }
Meerdere ballen
Section titled “Meerdere ballen”Om eerlijk te zijn: dit voorbeeld is Complex met een hoofdletter C en vereist waarschijnlijk meer inspanning dan wat u nodig heeft voor de meeste basiseffecten. Om te voorkomen dat de code te veel commentaar krijgt, bespreken we hier eerst een paar lastige concepten.
Ten eerste: hoe weet een bal dat hij met een andere zal botsen? Elke frame moet elke instantie van de balklasse de positie van elke andere balinstantie controleren. Door hun posities te vergelijken, kunnen we bepalen of twee ballen binnen elkaars straal zijn en vervolgens hun x- en y-snelheden omdraaien.
Soms kan een bal genoeg snelheid krijgen om door een andere bal heen te gaan, waardoor ze bijna dezelfde ruimte innemen. De wiskunde die nodig is om beide balposities aan te passen zou te complex zijn, dus verwijder ik gewoon de balinstantie die we momenteel controleren. Omdat de updatefunctie een minimum aantal ballen garandeert, wordt er een nieuwe balinstantie aangemaakt en gaat de simulatie verder.
Tot slot: wanneer moeten we de snelheids- en positieverandering voor elke bal berekenen? Maakt het uit of de bal voor of na het updaten van de variabelen wordt getekend? Het probleem bij programmeren is dat er meerdere antwoorden zijn op beide vragen. Hiervoor heb ik een iets andere methode geïntroduceerd voor het bijhouden van botsingen. Het is aan u om de gewenste methode te kiezen, maar beide methoden kunnen en werken wel.
<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(); } }
Gebruikersbesturingen
Section titled “Gebruikersbesturingen”Tot slot voegen we enkele basisbesturingen toe om de simulatie interessanter te maken. De kleurveranderingen zijn al uitgebreid besproken, dus om de code niet nog complexer te maken, focussen we op het aanpassen van het aantal ballen en hun straal. Dit vereist slechts enkele kleine aanpassingen aan de bestaande functies.
<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 . . .}
We hopen dat u heeft genoten van onze JavaScript-klasse-animatietutorial! Zelfs op dit niveau van complexiteit is dit effect slechts het topje van de ijsberg van wat u kunt bereiken met het HTML-canvas. Bovendien beschouwen we het nog niet als afgerond. Als we streven naar perfectie, zult u merken dat botsingen één frame van tevoren worden gedetecteerd, wat betekent dat ze vaak niet daadwerkelijk plaatsvinden. De botsingen zijn ook vereenvoudigd wat betreft energieoverdracht: de resulterende snelheid houdt alleen rekening met de eigen snelheid van de bal en niet met de energie van de andere bal. Tot slot is het meerdere keren per lus controleren van de positie van elke bal inefficiënt qua tijdcomplexiteit en kan worden geoptimaliseerd met eenvoudige memoizatie.
Wees altijd kritisch op uw effecten. Ze kunnen bijna altijd worden verbeterd, en het verschil tussen een middelmatig effect en een geweldig effect komt vaak simpelweg neer op er even goed naar kijken en uzelf toe te staan het op zijn minst één keer niet te vinden kloppen.
Voor een laatste stap-voor-stap-tutorial kunt u onze sectie Callbacks bekijken, waar we het proces doorlopen van het vastleggen van meterdata om effecten te activeren en deactiveren.