【问题标题】:Animating opacity in HTML5 CanvasHTML5 Canvas 中的动画不透明度
【发布时间】:2015-06-26 01:00:26
【问题描述】:

我正在尝试重新创建this effect,但我希望它在画布中,基本上使用循环绘制具有淡入淡出效果的正方形。循环部分没问题,我想不通的是淡入效果。我正在使用requestAnimationFrame,每次重绘globalAlpha都会增加,旧的squaure被删除并绘制一个新的square。这是函数:

function renderSquare(x, y) {

requestID = requestAnimationFrame(renderSquare);
alpha = requestID/100;  

ctx.clearRect(x,y,size,size);
ctx.globalAlpha = alpha;

var colour = "rgb(58,150,270)";
ctx.fillStyle = colour;
ctx.fillRect(x, y, size, size);


console.log("ID: " + requestID);  
console.log("Alpha: " + alpha);

if(alpha == 1) {
    cancelAnimationFrame(requestID);
}   

};

function drawSquare(x,y) {
  requestAnimationFrame(function render(){
    renderSquare(x,y);          
  });   
}

但我就是无法让它工作。这是一个包含全部内容的代码笔。

http://codepen.io/easynowbaby/pen/GJKOej?editors=001

最终,我希望能够在 loopSquares 函数中使用该函数。非常感谢任何帮助。干杯!

编辑:我应该让自己更清楚。我不想用图像重新创建画廊,我只对正方形的级联淡入淡出感兴趣。我想在画布中实现这种效果,我将使用 fillRect 函数淡化小方块。

【问题讨论】:

    标签: javascript canvas


    【解决方案1】:

    问题

    这里首先要指出的是如何使用requestID 来设置alpha。来自MDN(我的重点):

    一个长整数值,请求 id,唯一标识 回调列表中的条目。这是一个非零值,但您可能不会 对其价值做出任何其他假设。您可以将此值传递给 window.cancelAnimationFrame() 取消刷新回调请求。

    换句话说,不要假设这将保持与单元格的当前索引等效的运行值。它可能会在一个浏览器中意外地这样做,但在另一个浏览器中则不会。通过其他方式跟踪此值。

    其次,globalAlpha 作用于整个上下文,用于绘制在它旁边的任何内容。这意味着您需要跟踪当前的 alpha per 正方形,或使用颜色样式 rgba,它允许您为每个样式设置 alpha。不过这并不重要,因为您还需要在这里跟踪 alpha。

    一个解决方案

    我建议为此使用一个对象,一个方形猴子,可以训练它正确设置其 alpha,然后跨网格复制。

    示例

    主对象将跟踪所有设置,例如当前 alpha、更新量、颜色等。当然不仅限于这些——你也可以添加缩放、旋转等,但这里我只展示 alpha:

    // Square-monkey object
    function Rectangle(ctx, x, y, w, h, color, speed) {
    
      this.ctx = ctx;
      this.x = x;
      this.y = y;
      this.height = h;
      this.width = w;
      this.color = color;
    
      this.alpha = 0;                        // current alpha for this instance
      this.speed = speed;                    // increment for alpha per frame
      this.triggered = false;                // is running
      this.done = false;                     // has finished
    }
    

    为了节省一些内存并提高性能,我们可以将原型用于常用功能/方法:

    Rectangle.prototype = {
    
      trigger: function() {                  // start this rectangle
        this.triggered = true
      },
    
      update: function() {
        if (this.triggered && !this.done) {  // only if active
          this.alpha += this.speed;          // update alpha
          this.done = (this.alpha >= 1);     // update status
        }
    
        this.ctx.fillStyle = this.color;           // render this instance
        this.ctx.alpha = Math.min(1, this.alpha);  // clamp value
        this.ctx.fillRect(this.x, this.y, this.width, this.height);
      }  
    };
    

    我们现在需要做的是定义一个网格和单元格大小:

    var cols = 10,
        rows = 6,
        cellWidth = canvas.width / cols,
        cellHeight = canvas.height /rows;
    

    现在我们可以为每个单元格填充一个对象:

    var grid = [],
        len = cols * rows,
        y = 0, x;
    
    for(; y < rows; y++) {
      for(x = 0; x < cols; x++) {
        grid.push(new Rectangle(ctx, x * cellWidth, y * cellHeight,
                                cellWidth, cellHeight, "#79f", 0.25));
      }
    }
    

    最后,我们需要以任何我们喜欢的方式触发这些方格,这里只是对角级联方式。让我们也跟踪一下是否所有都完成了:

    function loop() {
      ctx.globalAlpha = 1;           // reset alpha
      ctx.clearRect(0, 0, w, h);     // clear canvas
    
      // trigger cells
      for(y = 0; y < rows; y++) {    // iterate each row
        var gx = (x|0) - y;          // calc an x for current row
        if (gx >= 0 && gx < cols) {  // within limit?
          index = y * cols + gx;     // calc index
          grid[index].trigger();     // trigger this cell's animation if not running
        }
      }
    
      x += 0.25;                     // how fast to cascade
    
      hasActive = false;             // enable ending the animation when all done
    
      // update/render all cells
      for(var i = 0; i < grid.length; i++) {
        grid[i].update();
        if (!grid[i].done) hasActive = true;      // anyone active?
      }
    
      if (hasActive) requestAnimationFrame(loop); // animate if anyone's active
    }
    

    现场示例

    var canvas = document.querySelector("canvas"),
        ctx = canvas.getContext("2d"),
        w = canvas.width,
        h = canvas.height;
    
    // Square-monkey object
    function Rectangle(ctx, x, y, w, h, color, speed) {
    
      this.ctx = ctx;
      this.x = x;
      this.y = y;
      this.height = h;
      this.width = w;
      this.color = color;
      
      this.alpha = 0;                        // current alpha for this instance
      this.speed = speed;                    // increment for alpha per frame
      this.triggered = false;                // is running
      this.done = false;                     // has finished
    }
    
    // prototype methods that will be shared
    Rectangle.prototype = {
    
      trigger: function() {                  // start this rectangle
        this.triggered = true
      },
      
      update: function() {
        if (this.triggered && !this.done) {  // only if active
          this.alpha += this.speed;          // update alpha
          this.done = (this.alpha >= 1);     // update status
        }
          
        this.ctx.fillStyle = this.color;   // render this instance
        this.ctx.globalAlpha = Math.min(1, this.alpha);
        this.ctx.fillRect(this.x, this.y, this.width, this.height);
      }  
    };
    
    // Populate grid
    var cols = 10,
        rows = 6,
        cellWidth = canvas.width / cols,
        cellHeight = canvas.height /rows,
        grid = [],
        len = cols * rows,
        y = 0, x;
    
    for(; y < rows; y++) {
      for(x = 0; x < cols; x++) {
        grid.push(new Rectangle(ctx, x * cellWidth, y * cellHeight, cellWidth, cellHeight, "#79f", 0.025));
      }
    }
    
    var index,
        hasActive = true;
    
    x = 0;
    
    function loop() {
    
      ctx.globalAlpha = 1;
      ctx.clearRect(0, 0, w, h);
      
      // trigger cells
      for(y = 0; y < rows; y++) {
        var gx = (x|0) - y;
        if (gx >= 0 && gx < cols) {
          index = y * cols + gx;
          grid[index].trigger();
        }
      }
      
      x += 0.25;
      
      hasActive = false;
      
      // update all
      for(var i = 0; i < grid.length; i++) {
        grid[i].update();
        if (!grid[i].done) hasActive = true;
      }
      
      if (hasActive) requestAnimationFrame(loop)
    }
    loop();
    &lt;canvas width=500 height=300&gt;&lt;/canvas&gt;

    实例扩展对象

    通过使用单个对象,您可以在一个地方维护代码。为了好玩,让我们也添加旋转和缩放:

    var canvas = document.querySelector("canvas"),
        ctx = canvas.getContext("2d"),
        w = canvas.width,
        h = canvas.height;
    
    // Square-monkey object
    function Rectangle(ctx, x, y, w, h, color, speed) {
    
      this.ctx = ctx;
      this.x = x;
      this.y = y;
      this.height = h;
      this.width = w;
      this.color = color;
      
      this.alpha = 0;                        // current alpha for this instance
      this.speed = speed;                    // increment for alpha per frame
      this.triggered = false;                // is running
      this.done = false;                     // has finished
    }
    
    // prototype methods that will be shared
    Rectangle.prototype = {
    
      trigger: function() {                  // start this rectangle
        this.triggered = true
      },
      
      update: function() {
        if (this.triggered && !this.done) {  // only if active
          this.alpha += this.speed;          // update alpha
          this.done = (this.alpha >= 1);     // update status
        }
    
        this.ctx.fillStyle = this.color;     // render this instance
        this.ctx.globalAlpha = Math.min(1, this.alpha);
        
        var t = this.ctx.globalAlpha,        // use current alpha as t
            cx = this.x + this.width * 0.5,  // center position
            cy = this.y + this.width * 0.5;
        
        this.ctx.setTransform(t, 0, 0, t, cx, cy); // scale and translate
        this.ctx.rotate(0.5 * Math.PI * (1 - t));  // rotate, 90° <- alpha
        this.ctx.translate(-cx, -cy);              // translate back
        this.ctx.fillRect(this.x, this.y, this.width, this.height);
      }  
    };
    
    // Populate grid
    var cols = 20,
        rows = 12,
        cellWidth = canvas.width / cols,
        cellHeight = canvas.height /rows,
        grid = [],
        len = cols * rows,
        y = 0, x;
    
    for(; y < rows; y++) {
      for(x = 0; x < cols; x++) {
        grid.push(new Rectangle(ctx, x * cellWidth, y * cellHeight, cellWidth, cellHeight, "#79f", 0.02));
      }
    }
    
    var index,
        hasActive = true;
    
    x = 0;
    
    function loop() {
    
      ctx.setTransform(1,0,0,1,0,0);
      ctx.globalAlpha = 1;
      ctx.clearRect(0, 0, w, h);
      
      // trigger cells
      for(y = 0; y < rows; y++) {
        var gx = (x|0) - y;
        if (gx >= 0 && gx < cols) {
          index = y * cols + gx;
          grid[index].trigger();
        }
      }
      
      x += 0.333;
      
      hasActive = false;
      
      // update all
      for(var i = 0; i < grid.length; i++) {
        grid[i].update();
        if (!grid[i].done) hasActive = true;
      }
      
      if (hasActive) requestAnimationFrame(loop)
    }
    loop();
    &lt;canvas width=500 height=300&gt;&lt;/canvas&gt;

    【讨论】:

    • 你需要提高你的游戏水平 - 我已经完全习惯了你史诗般的答案! +1 :)
    • 确实是正确的答案!非常感谢您的详尽解释!
    【解决方案2】:

    这不是画布也不是画布效果。

    这些是通过大小和不透明度进行动画处理的简单图像标签。 只需将任何图像拖到 URL 栏,您就会看到全分辨率图像打开。之所以如此,是因为它们是 DOM 元素。如果这是一个画布,你不可能将它们拖到 URL 栏上。

    您需要做的就是正确地为一个图像标签设置动画(大小和不透明度)。对于 jQuery,两者都非常简单且解释清楚。接下来,将该效果与它们之间的延迟组合在一个数组中,然后您就可以开始了。

    【讨论】:

    • 对不起,我的解释可能不清楚。我所追求的只是我想用画布上绘制的正方形重新创建的视觉效果。请参阅我的编辑。干杯!
    猜你喜欢
    • 2017-03-09
    • 2011-03-14
    • 2010-09-25
    • 1970-01-01
    • 2013-10-08
    • 1970-01-01
    • 1970-01-01
    • 2011-08-27
    • 2012-09-09
    相关资源
    最近更新 更多