在画布中循环背景图像动画

时间:2017-05-19 07:38:08

标签: javascript html5 canvas html5-canvas

我是Canvas的新手,想要在下面的烟雾效果中循环背景图像。在搜索时,我找到了一个示例,我们如何在canvas Link to looping animation中循环背景图像,所以我尝试将循环代码与烟雾效果集成,但没有成功。任何帮助将不胜感激。



     // Create an array to store our particles
     var particles = [];
     // The amount of particles to render
     var particleCount = 60;
     // The maximum velocity in each direction
     var maxVelocity = 2;
     // The target frames per second (how often do we want to update / redraw the scene)
     var targetFPS = 33;
     // Set the dimensions of the canvas as variables so they can be used.
     var canvasWidth = window.innerWidth;
     var canvasHeight = window.innerHeight;
     // borders for particles on top and bottom
     var borderTop = 0.01 * canvasHeight;
     var borderBottom = 0.99 * canvasHeight;
     // Create an image object (only need one instance)
     var imageObj = new Image();

     var looping = false;
     var totalSeconds = 0;
     // Once the image has been downloaded then set the image on all of the particles
     imageObj.onload = function() {
       particles.forEach(function(particle) {
         particle.setImage(imageObj);
       });
     };
     // Once the callback is arranged then set the source of the image
     imageObj.src = "https://image.ibb.co/fdpeJF/Smoke.png";
     // A function to create a particle object.
     function Particle(context) {
       // Set the initial x and y positions
       this.x = 0;
       this.y = 0;
       // Set the initial velocity
       this.xVelocity = 0;
       this.yVelocity = 0;
       // Set the radius
       this.radius = 5;
       // Store the context which will be used to draw the particle
       this.context = context;
       // The function to draw the particle on the canvas.
       this.draw = function() {
         // If an image is set draw it
         if (this.image) {
           this.context.drawImage(this.image, this.x - 128, this.y - 128);
           // If the image is being rendered do not draw the circle so break out of the draw function
           return;
         }
         // Draw the circle as before, with the addition of using the position and the radius from this object.
         this.context.beginPath();
         this.context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
         this.context.fillStyle = "rgba(0, 255, 255, 1)";
         this.context.fill();
         this.context.closePath();
       };
       // Update the particle.
       this.update = function() {
         // Update the position of the particle with the addition of the velocity.
         this.x += this.xVelocity;
         this.y += this.yVelocity;
         // Check if has crossed the right edge
         if (this.x >= canvasWidth) {
           this.xVelocity = -this.xVelocity;
           this.x = canvasWidth;
         }
         // Check if has crossed the left edge
         else if (this.x <= 0) {
           this.xVelocity = -this.xVelocity;
           this.x = 0;
         }
         // Check if has crossed the bottom edge
         if (this.y >= borderBottom) {
           this.yVelocity = -this.yVelocity;
           this.y = borderBottom;
         }
         // Check if has crossed the top edge
         else if (this.y <= borderTop) {
           this.yVelocity = -this.yVelocity;
           this.y = borderTop;
         }
       };
       // A function to set the position of the particle.
       this.setPosition = function(x, y) {
         this.x = x;
         this.y = y;
       };
       // Function to set the velocity.
       this.setVelocity = function(x, y) {
         this.xVelocity = x;
         this.yVelocity = y;
       };
       this.setImage = function(image) {
         this.image = image;
       };
     }
     // A function to generate a random number between 2 values
     function generateRandom(min, max) {
       return Math.random() * (max - min) + min;
     }
     // The canvas context if it is defined.
     var context;
     // Initialise the scene and set the context if possible
     function init() {
       var canvas = document.getElementById('myCanvas');
       canvas.width = window.innerWidth;
       canvas.height = window.innerHeight;
       if (canvas.getContext) {
         // Set the context variable so it can be re-used
         context = canvas.getContext('2d');
         // Create the particles and set their initial positions and velocities
         for (var i = 0; i < particleCount; ++i) {
           var particle = new Particle(context);
           // Set the position to be inside the canvas bounds
           particle.setPosition(generateRandom(0, canvasWidth), generateRandom(borderTop, borderBottom));
           // Set the initial velocity to be either random and either negative or positive
           particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
           particles.push(particle);
           context.clearRect(0, 0, canvas.width, canvas.height);	
         }
       } else {
         alert("Please use a modern browser");
       }
     }
     // The function to draw the scene
     function draw() {
       //  background image
       context.globalAlpha = 1;
             context.globalCompositeOperation = 'source-over';
       context.drawImage(backImg, 0, 0, canvasWidth, canvasHeight);
       context.fillStyle = "rgba(255,255,255, .5)";
       context.fillRect(0, 0, canvasWidth, canvasHeight);

       context.globalAlpha = 0.75;
        context.globalCompositeOperation = 'soft-lights';
       // Fog layer
       // Go through all of the particles and draw them.
       particles.forEach(function(particle) {
         particle.draw();
       });

     }
     // Update the scene
     function update() {
       particles.forEach(function(particle) {
         particle.update();
       });
     }
     // Initialize the scene
     init();
     backImg = new Image();
      backImg.src = 'https://image.ibb.co/cTOOdF/e2VZQY.jpg';
       // If the context is set then we can draw the scene (if not then the browser does not support canvas)
       if (context) {
         setInterval(function() {
           // Update the scene befoe drawing
           update();
           // Draw the scene
           draw();
         }, 1000 / targetFPS);
       }
&#13;
<canvas id="myCanvas" ></canvas>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:1)

我刚刚添加了几行。希望你能发现它们。我评论了我添加的所有内容。

&#13;
&#13;
// Create an array to store our particles
var particles = [];
// The amount of particles to render
var particleCount = 60;
// The maximum velocity in each direction
var maxVelocity = 2;
// The target frames per second (how often do we want to update / redraw the scene)
var targetFPS = 33;
// Set the dimensions of the canvas as variables so they can be used.
var canvasWidth = window.innerWidth;
var canvasHeight = window.innerHeight;
// borders for particles on top and bottom
var borderTop = 0.01 * canvasHeight;
var borderBottom = 0.99 * canvasHeight;
// Create an image object (only need one instance)
var imageObj = new Image();
// x position of scrolling image
var imageX = 0;

var looping = false;
var totalSeconds = 0;
// Once the image has been downloaded then set the image on all of the particles
imageObj.onload = function() {
  particles.forEach(function(particle) {
    particle.setImage(imageObj);
  });
};
// Once the callback is arranged then set the source of the image
imageObj.src = "https://image.ibb.co/fdpeJF/Smoke.png";
// A function to create a particle object.
function Particle(context) {
  // Set the initial x and y positions
  this.x = 0;
  this.y = 0;
  // Set the initial velocity
  this.xVelocity = 0;
  this.yVelocity = 0;
  // Set the radius
  this.radius = 5;
  // Store the context which will be used to draw the particle
  this.context = context;
  // The function to draw the particle on the canvas.
  this.draw = function() {
    // If an image is set draw it
    if (this.image) {
      this.context.drawImage(this.image, this.x - 128, this.y - 128);
      // If the image is being rendered do not draw the circle so break out of the draw function
      return;
    }
    // Draw the circle as before, with the addition of using the position and the radius from this object.
    this.context.beginPath();
    this.context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
    this.context.fillStyle = "rgba(0, 255, 255, 1)";
    this.context.fill();
    this.context.closePath();
  };
  // Update the particle.
  this.update = function() {
    // Update the position of the particle with the addition of the velocity.
    this.x += this.xVelocity;
    this.y += this.yVelocity;
    // Check if has crossed the right edge
    if (this.x >= canvasWidth) {
      this.xVelocity = -this.xVelocity;
      this.x = canvasWidth;
    }
    // Check if has crossed the left edge
    else if (this.x <= 0) {
      this.xVelocity = -this.xVelocity;
      this.x = 0;
    }
    // Check if has crossed the bottom edge
    if (this.y >= borderBottom) {
      this.yVelocity = -this.yVelocity;
      this.y = borderBottom;
    }
    // Check if has crossed the top edge
    else if (this.y <= borderTop) {
      this.yVelocity = -this.yVelocity;
      this.y = borderTop;
    }
  };
  // A function to set the position of the particle.
  this.setPosition = function(x, y) {
    this.x = x;
    this.y = y;
  };
  // Function to set the velocity.
  this.setVelocity = function(x, y) {
    this.xVelocity = x;
    this.yVelocity = y;
  };
  this.setImage = function(image) {
    this.image = image;
  };
}
// A function to generate a random number between 2 values
function generateRandom(min, max) {
  return Math.random() * (max - min) + min;
}
// The canvas context if it is defined.
var context;
// Initialise the scene and set the context if possible
function init() {
  var canvas = document.getElementById('myCanvas');
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  if (canvas.getContext) {
    // Set the context variable so it can be re-used
    context = canvas.getContext('2d');
    // Create the particles and set their initial positions and velocities
    for (var i = 0; i < particleCount; ++i) {
      var particle = new Particle(context);
      // Set the position to be inside the canvas bounds
      particle.setPosition(generateRandom(0, canvasWidth), generateRandom(borderTop, borderBottom));
      // Set the initial velocity to be either random and either negative or positive
      particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
      particles.push(particle);
      context.clearRect(0, 0, canvas.width, canvas.height);  
    }
  } else {
    alert("Please use a modern browser");
  }
}
// The function to draw the scene
function draw() {
  //  background image
  context.globalAlpha = 1;
  context.globalCompositeOperation = 'source-over';
  // draw twice to cover wrap around
  context.drawImage(backImg, imageX, 0, canvasWidth, canvasHeight);
  context.drawImage(backImg, imageX + canvasWidth, 0, canvasWidth, canvasHeight);
  context.fillStyle = "rgba(255,255,255, .5)";
  context.fillRect(0, 0, canvasWidth, canvasHeight);

  context.globalAlpha = 0.75;
   context.globalCompositeOperation = 'soft-light';
  // Fog layer
  // Go through all of the particles and draw them.
  particles.forEach(function(particle) {
    particle.draw();
  });

}
// Update the scene
function update() {
  // incrementally change image position of background to scroll left
  imageX -= maxVelocity;

  if (imageX < -canvasWidth) {
    imageX += canvasWidth;
  }

  particles.forEach(function(particle) {
    particle.update();
  });
}
// Initialize the scene
init();
backImg = new Image();
backImg.src = 'https://image.ibb.co/cTOOdF/e2VZQY.jpg';
// If the context is set then we can draw the scene (if not then the browser does not support canvas)
if (context) {
  setInterval(function() {
    // Update the scene befoe drawing
    update();
    // Draw the scene
    draw();
  }, 1000 / targetFPS);
}
&#13;
<canvas id="myCanvas"></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

在代码结构的一些额外改进的基础上添加答案。

  • 使用requestAnimationFrame来调用渲染调用。
  • 如果不需要,请不要公开对象的属性。
  • 不要在时间关键代码中使用forEach次迭代。使用for循环。
  • 尽可能使用常量。
  • 表明显而易见的注释只是源代码中的噪音,因此难以阅读。限制对摘要的评论,这对于阅读代码的其他程序员来说可能并不明显。

    例如

    // If an image is set draw it

    if (this.image) {

    对任何人都有任何用处的评论。注释应该有助于降低代码的可读性。

此外,原始代码尝试将全局复合操作设置为soft-lights这不是一个已知的操作。我将其更正为soft-light,这在一些机器上可能是一个非常慢的渲染操作。对于速度较慢的机器,可能需要支付选定的其他操作。这可以通过简单地监视粒子的渲染时间和切换操作类型太慢来完成。

快速重写OP的代码。

const particles = [];
     const particleCount = 60;
     const maxVelocity = 2;
     var canvasWidth = innerWidth;
     var canvasHeight = innerHeight;
     var borderTop = 0.01 * canvasHeight;
     var borderBottom = 0.99 * canvasHeight;
     var ctx;
     const backgroundColor = "rgba(255,255,255, .5)";
     const backgroundSpeed = -0.1;
     var looping = false;
     var totalSeconds = 0;
     var lastTime = 0;
     var frameTime = (1000 / 30) - (1000 / 120); // one quater frame short to 
                                                 // allow for timing error
     var imageCount = 0;
     const backImg = new Image();
     const imageObj = new Image();     
     backImg.src = 'https://image.ibb.co/cTOOdF/e2VZQY.jpg';
     imageObj.src = "https://image.ibb.co/fdpeJF/Smoke.png";
     backImg.onload = imageObj.onload = imageLoad;
     function imageLoad(){
         imageCount += 1;
         if(imageCount === 2){
             init();
         }
     }
     function init() {
         var canvas = myCanvas;
         canvas.width = innerWidth;
         canvas.height = innerHeight;

         ctx = canvas.getContext('2d');
         for (var i = 0; i < particleCount; i += 1) {
             particles.push(new Particle(ctx));
         }
         lastTime = performance.now();
         requestAnimationFrame(mainLoop);
     }     
     function mainLoop(time){
         if(time-lastTime > frameTime){
             lastTime = time;
             update();
             draw(time);
         }
         requestAnimationFrame(mainLoop);
     }
     const rand = (min, max) => Math.random() * (max - min) + min;  // names are best short (short only without ambiguity)
     function Particle(ctx) { 
         var x, y, xVel, yVel, radius, image;
         const color = "rgba(0, 255, 255, 1)";
         x = rand(0, canvasWidth), 
         y = rand(borderTop, borderBottom);
         xVel = rand(-maxVelocity, maxVelocity);
         yVel = rand(-maxVelocity, maxVelocity);
         radius = 5;
         image = imageObj;
         this.draw = function () { ctx.drawImage(image, x - 128, y - 128) }
         this.update = function () {
             x += xVel;
             y += yVel;
             if (x >= canvasWidth) {
                 xVel = -xVel;
                 x = canvasWidth;
             }
             else if (x <= 0) {
                 xVel = -xVel;
                 x = 0;
             }
             if (y >= borderBottom) {
                 yVel = -yVel;
                 y = borderBottom;
             }
             else if (y <= borderTop) {
                 yVel = -yVel;
                 y = borderTop;
             }
         }
     }


     function draw(time) {
         var i,x;
         ctx.globalAlpha = 1;
         ctx.globalCompositeOperation = 'source-over';
         x = time * backgroundSpeed;
         x = ((x % canvasWidth) + canvasWidth) % canvasWidth;
         ctx.drawImage(backImg, x, 0, canvasWidth, canvasHeight);
         ctx.drawImage(backImg, x - canvasWidth, 0, canvasWidth, canvasHeight);
         ctx.fillStyle = backgroundColor;
         ctx.fillRect(0, 0, canvasWidth, canvasHeight);

         ctx.globalAlpha = 0.75;
         ctx.globalCompositeOperation = 'soft-light';
         for(i = 0; i < particles.length; i += 1){
             particles[i].draw();
         }
     }
     function update() {
         for(i = 0; i < particles.length; i += 1){
             particles[i].update();
         }
     }
canvas {
   position : absolute;
   top : 0px;
   left : 0px;
}
<canvas id=myCanvas></canvas>