简体   繁体   中英

JavaScript Canvas moving and animating color at the same time

My script is animating the changing of the color of the ball. The 1 complete sequence takes 1 second and changes the color from green to blue using gradient. I am trying to add a move() method to move the ball.

However I have a problem where to execute my move() method. Right now the animation of the ball moving is executed witihin the animation of the color but it shouldn't be. The color should change diffrently and the ball should be moving ininitely. I want the ball to move infinitely and the chaning of the color has to take 1 second like it is now.

Am I doing this right?

<-- Edit - I changed interval to 2000ms, just to visualize the problem better ( the moving of the ball stops but it shouldn't)

 var canvas = document.querySelector('canvas'); var c = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; var i = 0; var ec; var direction = 0; var x = 100; var dx = 10; var y = 100; var radius = 100; function move() { if(x + radius > window.innerWidth || x - radius < 0) { dx = -dx; } x += dx; } function animate() { c.clearRect(0, 0, innerWidth, innerHeight) move(); var gradient = c.createLinearGradient(0, 0, 170, 0); ec = 255 - i; gradient.addColorStop(1, "rgba(" + 0 + "," + i + "," + ec + ")", 1); c.fillStyle = gradient; c.beginPath(); c.arc(x, y, radius, 0, 2 * Math.PI); c.fill(); console.log(i, ec); if(i == 255) direction = 1; if(direction == 1 && i == 0) direction = 2; if(direction == 0 ) i+=5; else if(direction == 1 && direction != 2) i -= 5; if(direction != 2) requestAnimationFrame(animate); } setInterval(function draw() { direction = 0; animate(); }, 2000);
 canvas { border: 1px solid black; } body { margin: 0; }
 <canvas></canvas>

I got everything working as desired. But it was complicated, is there any way to make it simpler? Could someone teach me how to use snippet?

 var canvas = document.querySelector('canvas'); var c = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; var i = 0; var ec; var direction = 0; var x = 300; var y = 300; var radius = 200; var speed = 5; var dateDiff; var date1; var executedTimer = false; var executedColor = false; function draw() { c.clearRect(0, 0, innerWidth, innerHeight) c.beginPath(); c.arc(x, y, radius, 0, 2 * Math.PI); c.fill(); } function move() { if(x + radius > window.innerWidth || x - radius < 0) { speed = -speed; } x += speed; } function color() { var gradient = c.createLinearGradient(0, 0, 170, 0); ec = 255 - i; gradient.addColorStop(1, "rgba(" + 0 + "," + i + "," + ec + ")", 1); c.fillStyle = gradient; //console.log(i, ec); if(i == 255) direction = 1; if(direction == 1 && i == 0) direction = 2; if(direction == 0 ) i += 5; else if(direction == 1 && direction != 2) i -= 5; } function timerStart() { date1 = new Date(); executedTimer = true; } function timerCheck() { var date2 = new Date(); dateDiff = Math.abs(date1 - date2); if(dateDiff >= 1000)date1 = date2; } function animate() { draw(); move(); if(executedTimer == false) timerStart(); if(executedColor == false) { executedColor = true; color(); } if(dateDiff < 1000) color(); if(dateDiff >= 1000 && direction == 2) { i = 0; direction = 0; //console.log(dateDiff); color(); } timerCheck(); requestAnimationFrame(animate); } requestAnimationFrame(animate);
 canvas { border: 1px solid black; } body { margin: 0; }
 <canvas></canvas>

Treat both the position and the color as two different components, each with its own duration and its update method.

Then start a single animation loop in which you will

  • update the color component
  • update the position component
  • draw the scene

To ensure your animations last the duration you want, use a delta-time, which will let you know where you are relatively to the beginning of the animation.
This avoids issues with monitor using different refresh-rates (simply incrementing a value by a fixed amount will make your objects move twice faster on a 120Hz monitor than on a 60Hz one, generally not what you want), and it also allows to not care about the fact your animation is being paused by the browser when offscreen.

 { const canvas = document.querySelector('canvas'); const ctx = canvas.getContext('2d'); const w = canvas.width = window.innerWidth; const h = canvas.height = window.innerHeight; const radius = Math.min( w, h ) / 3; const color = { duration: 1000, update( elapsed ) { const delta = (elapsed % this.duration) / this.duration; const direction = (elapsed % (this.duration * 2)) - this.duration; const val = (direction < 0) ? 255 - (delta * 255) : delta * 255 ; this.val = `rgba(0, ${ val }, ${ 255 - val })`; } }; const position = { duration: 3000, y: h / 2, update( elapsed ) { const delta = (elapsed % this.duration) / this.duration; const direction = (elapsed % (this.duration * 2)) - this.duration; this.x = direction < 0 ? delta * w : w - (delta * w); } }; function draw() { ctx.clearRect( 0, 0, w, h ); ctx.beginPath(); ctx.arc( position.x, position.y, radius, 0, Math.PI *2 ); ctx.fillStyle = color.val; ctx.fill(); } // We record the time we started the animation // We will base our time relative animation on that const start = performance.now(); function anim( timestamp ) { const elapsed = timestamp - start; color.update( elapsed ); position.update( elapsed ); draw(); requestAnimationFrame( anim ); } requestAnimationFrame( anim ); }
 body { margin: 0; }
 <canvas></canvas>

And of course if you are going to have a lot of such both-directions animations, you could write an helper function to just get the value based on the elapsed time:

 { const canvas = document.querySelector('canvas'); const ctx = canvas.getContext('2d'); const w = canvas.width = window.innerWidth; const h = canvas.height = window.innerHeight; const radius = Math.min( w, h ) / 3; function getValueAtTime( elapsed, duration, max_val ) { const delta = (elapsed % duration) / duration; const direction = (elapsed % (duration * 2)) - duration; const val = delta * max_val; return direction < 0 ? val : max_val - val; } const color = { duration: 1000, update( elapsed ) { const val = getValueAtTime( elapsed, this.duration, 255 ); this.val = `rgba(0, ${ val }, ${ 255 - val })`; } }; const position = { duration: 3000, y: h / 2, update( elapsed ) { this.x = getValueAtTime( elapsed, this.duration, w ); } }; function draw() { ctx.clearRect( 0, 0, w, h ); ctx.beginPath(); ctx.arc( position.x, position.y, radius, 0, Math.PI *2 ); ctx.fillStyle = color.val; ctx.fill(); } // We record the time we started the animation // We will base our time relative animation on that const start = performance.now(); function anim( timestamp ) { const elapsed = timestamp - start; color.update( elapsed ); position.update( elapsed ); draw(); requestAnimationFrame( anim ); } requestAnimationFrame( anim ); }
 body { margin: 0; }
 <canvas></canvas>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM