HTML5 画布下落的五彩纸屑/雪多个对象

Posted

技术标签:

【中文标题】HTML5 画布下落的五彩纸屑/雪多个对象【英文标题】:HTML5 Canvas Falling Confetti / Snow Multiple Objects 【发布时间】:2016-07-28 21:39:22 【问题描述】:

我从这个例子开始:http://thecodeplayer.com/walkthrough/html5-canvas-snow-effect

我正在尝试添加多种形状的坠落物体。然而,只有我打电话给的最后一个似乎有效。它会覆盖屏幕上的其他内容。

我正在尝试做的另一件事是从数组中随机化颜色,但是当我添加一个随机函数时,我会得到一个疯狂的颜色变化效果。我不完全明白发生了什么,这是我第一次进入画布。似乎每帧都在重新绘制对象。这适用于雪,因为它都是白色的圆圈。

知道如何进行这些更改吗?这是我的代码(扣押警告!):http://codepen.io/paper_matthew/pen/vGpyeq

HTML

<canvas id="confetti"></canvas>

<div id="party-info">

  <h2>PARTY!!</h2>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt impedit animi enim iste repellat aliquam laborum consequuntur asperiores neque eos praesentium quis, consectetur cupiditate suscipit cum inventore excepturi? Vel, laudantium.</p>
</div>

CSS

html,
body 
  width: 100%;
  height: 100%;
  margin: 0;
  background-color: #fff;
  overflow: hidden;


#confetti 
  position: relative;
  top:0;
  left: 0;
  z-index: 1;


#party-info 
  position: absolute;
  background: #fff;
  padding: 20px;
  margin: 0 auto;
  height: 200px;
  top: 0;
  left: 0;
  right: 0;
  bottom:0;
  text-align: center;
  width: 400px;
  z-index: 22;
  color: gray;

javascript

window.onload = function() 
  //canvas init
  var canvas = document.getElementById("confetti");
  var ctx = canvas.getContext("2d");

  COLORS = [
    [238, 96, 169],
    [68, 213, 217],
    [245, 187, 152],
    [144, 148, 188],
    [235, 234, 77]
  ];

  //canvas dimensions
  var W = window.innerWidth;
  var H = window.innerHeight;
  canvas.width = W;
  canvas.height = H;

  //particles
  var mp = 100; //max particles
  var particles = [];
  for (var i = 0; i < mp; i++) 
    particles.push(
      x: Math.random() * W, //x-coordinate
      y: Math.random() * H, //y-coordinate
      r: Math.random() * 4 + 1, //radius
      d: Math.random() * mp //density
    )
  

  // Draw the shapes
  function drawCircle() 

    ctx.clearRect(0, 0, W, H);
    ctx.fillStyle = "rgba(" + COLORS[Math.floor(Math.random()*5+0)] + ", 0.8)";
    ctx.beginPath();
    for (var i = 0; i < mp; i++) 
      var p = particles[i];

      ctx.moveTo(p.x, p.y);
      ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2, true);

    
    ctx.fill();
    update();

  

  function drawTriangle() 

    ctx.clearRect(0, 0, W, H);
    ctx.fillStyle = "rgba(" + COLORS[2] + ", 0.8)";
    ctx.beginPath();
    for (var i = 0; i < mp; i++) 
      var p = particles[i];

      ctx.moveTo(p.x, p.y);
      ctx.lineTo(p.x + 15, p.y);
      ctx.lineTo(p.x + 15, p.y + 15);
      ctx.closePath();

    
    ctx.fill();
    update();

  

  function drawLine() 
    ctx.clearRect(0, 0, W, H);
    ctx.strokeStyle = "rgba(" + COLORS[3] + ", 0.8)";
    ctx.beginPath();
    for (var i = 0; i < mp; i++) 
      var p = particles[i];

      ctx.moveTo(p.x, p.y);
      ctx.lineTo(p.x, p.y + 20);
      ctx.lineWidth = 2;

    
    ctx.stroke();
    update();
  

  function update() 

    for (var i = 0; i < mp; i++) 
      var p = particles[i];
      p.y += Math.cos(p.d) + 1 + p.r / 2;
      p.x += Math.sin(0) * 2;

      if (p.x > W + 5 || p.x < -5 || p.y > H) 
        particles[i] = 
          x: Math.random() * W,
          y: -10,
          r: p.r,
          d: p.d
        ;
      
    
  

  function drawShapes() 
    drawTriangle();
    drawLine();
    drawCircle();
  

  //animation loop
  setInterval(drawShapes, 33);


【问题讨论】:

我的帖子中有一个 codepen 示例,这对你不起作用吗? 不,我是盲人。我的道歉 【参考方案1】:

到画布的任何动画循环都涉及对 update() 和 draw() 的重复调用。您应该更新模型,然后绘制到画布上。冲洗并重复。

我创建了一个新的 Javascript 文件来配合您的 HTML/CSS。它允许创建一个粒子对象,它可以是 3 种类型之一:

    圈子 三角形 行

然后我创建这些对象类型的数组。每种类型的三分之一填充到数组中。然后,我只需遍历这些对象,并分别在我的更新和绘制函数中调用动画循环中每个对象的更新和绘制方法。

这是代码,粒子数减少到 40 以提高流畅的动画速度:

function Particle(ctx, width, height, maxParticles, particleType) 
    COLORS = [
        [238, 96, 169],
        [68, 213, 217],
        [245, 187, 152],
        [144, 148, 188],
        [235, 234, 77]
    ];
    var ctx = ctx;
    var width = width;
    var height = height;
    var maxParticles = maxParticles;
    var particleType = particleType;
    var color = COLORS[Math.floor(Math.random() * 5)];

    var x = Math.random() * width;
    var y = Math.random() * height;
    var r = Math.random() * 4 + 1;
    var d = Math.random() * maxParticles;

    this.update = function() 
        y += Math.cos(d) + 1 + (r / 2);
        x += Math.sin(0) * 2;
        if (x > width + 5 || x < -5 || y > height) 
            x = Math.random() * width;
            y = -10;
        
    ;

    this.draw = function() 
        ctx.save();
        ctx.strokeStyle = "rgba(" + color + ", 0.8)";
        ctx.fillStyle = ctx.strokeStyle;
        ctx.beginPath();
        for (var i = 0; i < maxParticles; i++) 
            ctx.moveTo(x, y);
            switch (particleType) 
            case 1:
                ctx.arc(x, y, r, 0, Math.PI * 2, false);
                ctx.fill();
                break;
            case 2:
                ctx.lineTo(x + 15, y);
                ctx.lineTo(x + 15, y + 15);
                ctx.fill();
                break;
            case 3:
                ctx.lineWidth = 2;
                ctx.lineTo(x, y + 20);
                ctx.stroke();
                break;
            default:
                console.log('Unable to draw: undefined particle type [' + particleType + ']');
                break;
            
        

        ctx.restore();
    ;


window.onload = function() 
    //canvas init
    var canvas = document.getElementById("confetti");
    var ctx = canvas.getContext("2d");

    //canvas dimensions
    var W = window.innerWidth;
    var H = window.innerHeight;
    canvas.width = W;
    canvas.height = H;

    //particles
    var mp = 40;
    var particles = [];
    for (var i = 0; i < mp; i++) 
        var type = Math.floor(i * 3 / mp) + 1;
        particles.push(new Particle(ctx, W, H, particles.length, type));
    

    function drawShapes() 
        update();
        draw();
        setTimeout(drawShapes, 20);
    

    function update() 
        for (var key in particles) 
            particles[key].update();
        
    

    function draw() 
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        for (var key in particles) 
            particles[key].draw();
        
    

    //animation loop
    drawShapes();

【讨论】:

这使得所有三个形状都显示在彼此之上,但我试图一次做一个。每个下落的物体都是三种形状之一。 然后,我猜,如果您希望避免过度绘制问题,您将不得不有选择地使用 clearRect。您无法在每个形状之后清除整个画布,因为您只会丢失之前绘制操作的所有内容。 或者,如果每个粒子只能是这三个中的一个,那么您必须在每个粒子对象上存储一个属性,以便您知道它是哪种类型,然后调用适当的绘制函数那种粒子。或者,更好的是,让每种粒子类型都有自己的更新和绘制方法。然后,当您创建每个粒子时,您可以将它们添加到数组中,然后调用 update 并在每个粒子上绘制,这意味着它们将处理如何绘制自己,而不是为其实现任何 if-else 构造。在这种情况下,您需要做的就是将上下文的引用传递给它们。 目前,您只有一个名为粒子的数组。您要么需要为每种类型的粒子设置一个单独的数组,要么按照我上面所说的为每种粒子类型实现不同的更新/绘制方法,然后将它们添加到您的粒子数组中。然后让他们自己画。如果这真的是您的要求,我可以尝试写一些东西来反映这一点?告诉我。 我尝试将更新函数添加到绘图函数中,每个函数都有单独的粒子数组,但这似乎不起作用。【参考方案2】:

您正在调用 drawShapes(),它会立即绘制 3 个形状。 Var ctx 是全局的,因此每次绘制都会立即覆盖前一次。

【讨论】:

我应该为每种形状类型创建一个新的上下文变量吗? 我不知道,因为我也没有使用画布的经验。这只是我的第一次观察。和其他人一起去修复;) @yezzz 正确识别了您的关键问题,因此对此表示敬意。你的画布只需要一个上下文变量,所以就使用它。请参阅我关于操作顺序以及如何最好地实现函数调用的答案:)

以上是关于HTML5 画布下落的五彩纸屑/雪多个对象的主要内容,如果未能解决你的问题,请参考以下文章

我如何制作五彩纸屑?

如何使用JavaScript创建五彩纸屑效果

在 html5 中的 fabric.js 画布上一次删除多个对象

HTML5 Canvas 设置 z-index

如何在画布上制作 HTML5 可拖动对象?

html5 画布导航栏的对象数组