みかづきブログ その3

3ヶ月つづけてみました。

Canvasで炭酸的な表現を試みる

試みました。


DEMO

See the Pen Coke by kimmy (@kimmy) on CodePen.


JavaScript

(function(win) {
  var requestAnimationFrame = win.requestAnimationFrame || win.mozRequestAnimationFrame ||
                              win.webkitRequestAnimationFrame || win.msRequestAnimationFrame;
  win.requestAnimationFrame = requestAnimationFrame;
})(this);

(function(win, doc, $) {

  "use strict"

  function Bubble(ctx, x, y, r) {
    var SWING_LEVEL = 5,
        model = {
          top: y,
          left: x,
          size: 0,
          color: "rgba(255, 255, 255, " +  (0.4 + Math.random() / 4) + ")",
          innerRate: 0.4 + Math.random() / 4,
          param: {
            ctx: ctx,
            x: x,
            y: y,
            r: r
          }
        };

    this.model = model;
    this.$model = $(this.model);
    this.ctx = ctx;
    this.swingLevel = SWING_LEVEL * Math.random();

    this.born();
    this.flow();
    this.draw();
  }

  Bubble.prototype._progress = function(_, per) {
    this.model.left = this.model.left + Math.sin(2 * Math.PI * per * this.swingLevel);
  };

  Bubble.prototype._compleate = function() {
    this.model.size = 0;
    this.model.top = this.model.param.y;

    this.born();
    this.flow();
  };

  Bubble.prototype.draw = function() {
    var ctx = this.ctx;

    ctx.save();
      ctx.globalCompositeOperation = "lighter";
      ctx.fillStyle = this.model.color;
      ctx.beginPath();
        ctx.arc(this.model.left, this.model.top, this.model.size, 0, Math.PI * 2, false);
      ctx.fill();
      ctx.save();
        ctx.globalCompositeOperation = "destination-out";
        ctx.fillStyle = "#000";
        ctx.beginPath();
          ctx.arc(this.model.left, this.model.top - (1 - this.model.size) * this.model.innerRate, this.model.size * this.model.innerRate, 0, Math.PI * 2, false);
        ctx.fill();
      ctx.restore();  
    ctx.restore();
  };

  Bubble.prototype.born = function() {
    var SINK_DISTANCE = 50,
        DURATION = 500;

    this.$model.animate({
      size: this.model.param.r,
      top: this.model.top + SINK_DISTANCE * Math.random() | 0
    }, DURATION * Math.random() | 0);
  };

  Bubble.prototype.flow = function() {
    var EASING_TYPE = "easeInCubic",
        DURATION = 4000;

    this.$model.animate({
      top: -this.model.param.r
    }, {
      easing: EASING_TYPE,
      progress: this._progress.bind(this),
      complete: this._compleate.bind(this),
      duration: DURATION - (DURATION / 2 * Math.random() | 0)
    });
  };

  function BubbleManager(ctx, width, height, bottomLimit, maxSize, length) {
    var bubbles = [];

    for (var i = 0; i < length; ++i) {
      bubbles.push(new Bubble(
        ctx,
        width * Math.random(),
        height - bottomLimit * Math.random(),
        maxSize * Math.random()
      ));
    }

    this.length = length;
    this.bubbles = bubbles;
  }

  BubbleManager.prototype.draw = function() {
    for (var i = 0, length = this.length; i < length; ++i) {
      this.bubbles[i].draw();
    }
  }

  var canvas = doc.getElementById("canvas"),
      ctx = canvas.getContext("2d"),
      canvasWidth = win.innerWidth,
      canvasHeight = win.innerHeight;

  _init();

  function _init() {
    var BOTTOM_LIMIT = canvasHeight,
        BUBBLE_MAX_SIZE = 5,
        BUBBLE_LENGTH = canvasWidth * 2 | 0;

    var bubbleManager = new BubbleManager(
          ctx,
          canvasWidth,
          canvasHeight,
          BOTTOM_LIMIT,
          BUBBLE_MAX_SIZE,
          BUBBLE_LENGTH
        );

    _setCnavasSize();
    requestAnimationFrame(_draw);

    function _draw() {
      _setCnavasSize();
      bubbleManager.draw();
      requestAnimationFrame(_draw);
    }
  }

  function _setCnavasSize() {
    canvas.width = canvasWidth;
    canvas.height = canvasHeight; 
  }

})(this, document, $);