【问题标题】:Apply random rotation to canvas particles对画布粒子应用随机旋转
【发布时间】:2020-02-27 17:15:22
【问题描述】:

我正在尝试更新现有的particle animation。在这个演示中,粒子只是具有不同lineWidths 的线条,它们在下落时具有随机大小和旋转。

我的目标是用不同的形状替换线条,如下图所示,保持其他所有内容不变。

我已经改变了形状,但我遇到了几个问题:

  1. 它不再像上面发布的原始演示链接那样旋转。
  2. 因为我用形状的图像替换了线条,所以如果我增加粒子,我也会面临性能问题。
  3. 如果我随机化大小,它会不断更新形状的大小,而不保持第一个随机大小。

    context.drawImage(svg, x, y, 20, 40)
                      |
                      v
    context.drawImage(svg, x, y, Math.random() * 20, Math.random() * 40)
    

谁能指出我应该解决上述问题的正确方向。感谢任何帮助!

var confetti = {
  maxCount: 150,     //set max confetti count
  speed: 1,          //set the particle animation speed
  frameInterval: 30, //the confetti animation frame interval in milliseconds
  alpha: 1.0,        //the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
  gradient: false,   //whether to use gradients for the confetti particles
  start: null,       //call to start confetti animation (with optional timeout in milliseconds, and optional min and max random confetti count)
  stop: null,        //call to stop adding confetti
  toggle: null,      //call to start or stop the confetti animation depending on whether it's already running
  pause: null,       //call to freeze confetti animation
  resume: null,      //call to unfreeze confetti animation
  togglePause: null, //call to toggle whether the confetti animation is paused
  remove: null,      //call to stop the confetti animation and remove all confetti immediately
  isPaused: null,    //call and returns true or false depending on whether the confetti animation is paused
  isRunning: null    //call and returns true or false depending on whether the animation is running
};

(function() {
  confetti.start = startConfetti;
  confetti.stop = stopConfetti;
  confetti.toggle = toggleConfetti;
  confetti.pause = pauseConfetti;
  confetti.resume = resumeConfetti;
  confetti.togglePause = toggleConfettiPause;
  confetti.isPaused = isConfettiPaused;
  confetti.remove = removeConfetti;
  confetti.isRunning = isConfettiRunning;
  var supportsAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
  var colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,", "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,", "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
  var streamingConfetti = false;
  var animationTimer = null;
  var pause = false;
  var lastFrameTime = Date.now();
  var particles = [];
  var waveAngle = 0;
  var context = null;

  function getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }

  function resetParticle(particle, width, height) {
    particle.color = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
    particle.color2 = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
    particle.x = getRandomInt(0, width);
    particle.y = getRandomInt(-height / 2, 0);
    particle.diameter = Math.random() * 10 + 5;
    particle.tilt = Math.random() * 10 - 10;
    particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
    particle.tiltAngle = Math.random() * Math.PI;
    return particle;
  }

  function toggleConfettiPause() {
    if (pause)
      resumeConfetti();
    else
      pauseConfetti();
  }

  function isConfettiPaused() {
    return pause;
  }

  function pauseConfetti() {
    pause = true;
  }

  function resumeConfetti() {
    pause = false;
    runAnimation();
  }

  function runAnimation() {
    if (pause)
      return;
    else if (particles.length === 0) {
      context.clearRect(0, 0, window.innerWidth, window.innerHeight);
      animationTimer = null;
    } else {
      var now = Date.now();
      var delta = now - lastFrameTime;
      if (!supportsAnimationFrame || delta > confetti.frameInterval) {
        context.clearRect(0, 0, window.innerWidth, window.innerHeight);
        updateParticles();
        drawParticles(context);
        lastFrameTime = now - (delta % confetti.frameInterval);
      }
      animationTimer = requestAnimationFrame(runAnimation);
    }
  }

  function startConfetti(timeout, min, max) {
    var width = window.innerWidth;
    var height = window.innerHeight;
    window.requestAnimationFrame = (function() {
      return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(callback) {
          return window.setTimeout(callback, confetti.frameInterval);
        };
    })();
    var canvas = document.getElementById("confetti-canvas");
    if (canvas === null) {
      canvas = document.createElement("canvas");
      canvas.setAttribute("id", "confetti-canvas");
      canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0");
      document.body.prepend(canvas);
      canvas.width = width;
      canvas.height = height;
      window.addEventListener("resize", function() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
      }, true);
      context = canvas.getContext("2d");
    } else if (context === null)
      context = canvas.getContext("2d");
    var count = confetti.maxCount;
    if (min) {
      if (max) {
        if (min == max)
          count = particles.length + max;
        else {
          if (min > max) {
            var temp = min;
            min = max;
            max = temp;
          }
          count = particles.length + ((Math.random() * (max - min) + min) | 0);
        }
      } else
        count = particles.length + min;
    } else if (max)
      count = particles.length + max;
    while (particles.length < count)
      particles.push(resetParticle({}, width, height));
    streamingConfetti = true;
    pause = false;
    runAnimation();
    if (timeout) {
      window.setTimeout(stopConfetti, timeout);
    }
  }

  function stopConfetti() {
    streamingConfetti = false;
  }

  function removeConfetti() {
    stop();
    pause = false;
    particles = [];
  }

  function toggleConfetti() {
    if (streamingConfetti)
      stopConfetti();
    else
      startConfetti();
  }

  function isConfettiRunning() {
    return streamingConfetti;
  }

  function drawParticles(context) {
    var particle;
    var x, y, x2, y2;
    for (var i = 0; i < particles.length; i++) {
      particle = particles[i];
      particleWidth = particle.diameter;
      x2 = particle.x + particle.tilt;
      x = x2 + particle.diameter / 2;
      y = particle.y;

      var svg = new Image();
      svg.src = 'https://i.postimg.cc/TPBmVXH1/confetti.png';
      context.drawImage(svg, x, y, 20, 40)

      /*context.beginPath();
      context.lineWidth = particle.diameter;
      x2 = particle.x + particle.tilt;
      x = x2 + particle.diameter / 2;
      y2 = particle.y + particle.tilt + particle.diameter / 2;
      if (confetti.gradient) {
      	var gradient = context.createLinearGradient(x, particle.y, x2, y2);
      	gradient.addColorStop("0", particle.color);
      	gradient.addColorStop("1.0", particle.color2);
      	context.strokeStyle = gradient;
      } else
      	context.strokeStyle = particle.color;
      context.moveTo(x, particle.y);
      context.lineTo(x2, y2);
      context.stroke();*/
    }
  }

  function updateParticles() {
    var width = window.innerWidth;
    var height = window.innerHeight;
    var particle;
    waveAngle += 0.01;
    for (var i = 0; i < particles.length; i++) {
      particle = particles[i];
      if (!streamingConfetti && particle.y < -15)
        particle.y = height + 100;
      else {
        particle.tiltAngle += particle.tiltAngleIncrement;
        particle.x += Math.sin(waveAngle) - 0.5;
        particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5;
        particle.tilt = Math.sin(particle.tiltAngle) * 15;
      }
      if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
        if (streamingConfetti && particles.length <= confetti.maxCount)
          resetParticle(particle, width, height);
        else {
          particles.splice(i, 1);
          i--;
        }
      }
    }
  }
  startConfetti(5000, 20, 25)
})();
html {
  height: 100%;
}

body,
html {
  margin: 0;
}

body {
  background: black;
}

【问题讨论】:

    标签: javascript html animation canvas html5-canvas


    【解决方案1】:

    好的,这就是我所做的:

    1. 我将drawParticles 更改为应用particle.tilt
    2. 我将svg 变量移到了顶部,这样它就可以重复使用而不是多次加载。
    3. 我确实在resetParticle 中设置了随机大小,所以之后它不会改变。

    var confetti = {
      maxCount: 150,		//set max confetti count
      speed: 1,			//set the particle animation speed
      frameInterval: 30,	//the confetti animation frame interval in milliseconds
      alpha: 1.0,			//the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
      gradient: false,	//whether to use gradients for the confetti particles
      start: null,		//call to start confetti animation (with optional timeout in milliseconds, and optional min and max random confetti count)
      stop: null,			//call to stop adding confetti
      toggle: null,		//call to start or stop the confetti animation depending on whether it's already running
      pause: null,		//call to freeze confetti animation
      resume: null,		//call to unfreeze confetti animation
      togglePause: null,	//call to toggle whether the confetti animation is paused
      remove: null,		//call to stop the confetti animation and remove all confetti immediately
      isPaused: null,		//call and returns true or false depending on whether the confetti animation is paused
      isRunning: null		//call and returns true or false depending on whether the animation is running
    };
    
    (function () {
      confetti.start = startConfetti;
      confetti.stop = stopConfetti;
      confetti.toggle = toggleConfetti;
      confetti.pause = pauseConfetti;
      confetti.resume = resumeConfetti;
      confetti.togglePause = toggleConfettiPause;
      confetti.isPaused = isConfettiPaused;
      confetti.remove = removeConfetti;
      confetti.isRunning = isConfettiRunning;
      var supportsAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
      var colors = ["rgba(30,144,255,", "rgba(107,142,35,", "rgba(255,215,0,", "rgba(255,192,203,", "rgba(106,90,205,", "rgba(173,216,230,", "rgba(238,130,238,", "rgba(152,251,152,", "rgba(70,130,180,", "rgba(244,164,96,", "rgba(210,105,30,", "rgba(220,20,60,"];
      var streamingConfetti = false;
      var animationTimer = null;
      var pause = false;
      var lastFrameTime = Date.now();
      var particles = [];
      var waveAngle = 0;
      var context = null;
      
      var sizes = []; 
      var svg = new Image();
      
      svg.src = 'https://i.postimg.cc/TPBmVXH1/confetti.png';
    
      function getRandomInt(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min;
      }
    
      function resetParticle(particle, width, height) {
        particle.color = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
        particle.color2 = colors[(Math.random() * colors.length) | 0] + (confetti.alpha + ")");
        particle.x = getRandomInt(0, width);
        particle.y = getRandomInt(-height / 2, 0);
        particle.diameter = Math.random() * 10 + 5;
        particle.tilt = Math.random() * 10 - 10;
        particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
        particle.tiltAngle = Math.random() * Math.PI;
        
        particle.width = Math.random() * 20
        particle.height = Math.random() * 40
        
        return particle;
      }
    
      function toggleConfettiPause() {
        if (pause)
          resumeConfetti();
        else
          pauseConfetti();
      }
    
      function isConfettiPaused() {
        return pause;
      }
    
      function pauseConfetti() {
        pause = true;
      }
    
      function resumeConfetti() {
        pause = false;
        runAnimation();
      }
    
      function runAnimation() {
        if (pause)
          return;
        else if (particles.length === 0) {
          context.clearRect(0, 0, window.innerWidth, window.innerHeight);
          animationTimer = null;
        } else {
          var now = Date.now();
          var delta = now - lastFrameTime;
          if (!supportsAnimationFrame || delta > confetti.frameInterval) {
            context.clearRect(0, 0, window.innerWidth, window.innerHeight);
            updateParticles();
            drawParticles(context);
            lastFrameTime = now - (delta % confetti.frameInterval);
          }
          animationTimer = requestAnimationFrame(runAnimation);
        }
      }
    
      function startConfetti(timeout, min, max) {
        var width = window.innerWidth;
        var height = window.innerHeight;
        window.requestAnimationFrame = (function () {
          return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function (callback) {
              return window.setTimeout(callback, confetti.frameInterval);
            };
        })();
        var canvas = document.getElementById("confetti-canvas");
        if (canvas === null) {
          canvas = document.createElement("canvas");
          canvas.setAttribute("id", "confetti-canvas");
          canvas.setAttribute("style", "display:block;z-index:999999;pointer-events:none;position:fixed;top:0");
          document.body.prepend(canvas);
          canvas.width = width;
          canvas.height = height;
          window.addEventListener("resize", function () {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
          }, true);
          context = canvas.getContext("2d");
        } else if (context === null)
          context = canvas.getContext("2d");
        var count = confetti.maxCount;
        if (min) {
          if (max) {
            if (min == max)
              count = particles.length + max;
            else {
              if (min > max) {
                var temp = min;
                min = max;
                max = temp;
              }
              count = particles.length + ((Math.random() * (max - min) + min) | 0);
            }
          } else
            count = particles.length + min;
        } else if (max)
          count = particles.length + max;
        while (particles.length < count)
          particles.push(resetParticle({}, width, height));
        streamingConfetti = true;
        pause = false;
        runAnimation();
        if (timeout) {
          window.setTimeout(stopConfetti, timeout);
        }
      }
    
      function stopConfetti() {
        streamingConfetti = false;
      }
    
      function removeConfetti() {
        stop();
        pause = false;
        particles = [];
      }
    
      function toggleConfetti() {
        if (streamingConfetti)
          stopConfetti();
        else
          startConfetti();
      }
    
      function isConfettiRunning() {
        return streamingConfetti;
      }
    
      function drawParticles(context) {
        var particle;
        var x, y, x2, y2;
        for (var i = 0; i < particles.length; i++) {
          particle = particles[i];
          particleWidth = particle.diameter;
          x2 = particle.x + particle.tilt;
          x = x2 + particle.diameter / 2;
          y = particle.y;
    
          context.save();
          context.translate(x, y);
          context.rotate(particle.tilt / 180 * Math.PI);
          context.translate(-x, -y);
          context.drawImage(svg, x, y, particle.width, particle.height);
          context.restore();
    
    			/*context.beginPath();
    			context.lineWidth = particle.diameter;
    			x2 = particle.x + particle.tilt;
    			x = x2 + particle.diameter / 2;
    			y2 = particle.y + particle.tilt + particle.diameter / 2;
    			if (confetti.gradient) {
    				var gradient = context.createLinearGradient(x, particle.y, x2, y2);
    				gradient.addColorStop("0", particle.color);
    				gradient.addColorStop("1.0", particle.color2);
    				context.strokeStyle = gradient;
    			} else
    				context.strokeStyle = particle.color;
    			context.moveTo(x, particle.y);
    			context.lineTo(x2, y2);
    			context.stroke();*/
        }
      }
    
      function updateParticles() {
        var width = window.innerWidth;
        var height = window.innerHeight;
        var particle;
        waveAngle += 0.01;
        for (var i = 0; i < particles.length; i++) {
          particle = particles[i];
          if (!streamingConfetti && particle.y < -15)
            particle.y = height + 100;
          else {
            particle.tiltAngle += particle.tiltAngleIncrement;
            particle.x += Math.sin(waveAngle) - 0.5;
            particle.y += (Math.cos(waveAngle) + particle.diameter + confetti.speed) * 0.5;
            particle.tilt = Math.sin(particle.tiltAngle) * 15;
          }
          if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
            if (streamingConfetti && particles.length <= confetti.maxCount)
              resetParticle(particle, width, height);
            else {
              particles.splice(i, 1);
              i--;
            }
          }
        }
      }
      startConfetti(5000, 20, 25)
    })();
    html {
      height: 100%;
    }
    body, html {
      margin: 0;
    }
    body {
      background: black;
    }

    【讨论】:

    • 感谢您的回答。是否可以通过交替方向(顺时针和逆时针)改变旋转,使其看起来像一个自然的波浪形水滴。它目前仅顺时针旋转。它缺少最后一件事,使它看起来更真实。如果您能提供帮助将非常高兴。谢谢你:)
    • 我更新了上面的代码,现在使用 tilt 而不是 tiltAngle 这应该可以满足您的需求。但我可以建议检查一下threejs.org,它比仅使用画布更好,而且它有据可查 (threejs.org/docs/index.html#manual/en/introduction/…)!
    • 感谢您的建议。我实际上做了一个尝试,但我不确定这是否是正确的方法。我已经把它贴在这里了。你介意看看吗?这是我的最新帖子。
    • 你在哪里发的?
    • [Here on stackoverflow](stackoverflow.com/q/58670868/10301756 ),这是我几个小时前发布的新问题。
    猜你喜欢
    • 2016-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多