【问题标题】:Canvas Particle Text画布粒子文本
【发布时间】:2016-03-22 13:47:44
【问题描述】:

我是画布和粒子的新手,但我正在玩它here

我想在这里实现几件事,但我有点卡住了。

  1. 使文本在分解成粒子之前更长时间可见。因为它目前不可读。

  2. 我也想要它,以便有一个随机的粒子运动,然后形成文本,在文本形式中停留几秒钟,然后再次分裂成粒子。例如。随机粒子 > 粒子形成文本 > 随机粒子 > 清除屏幕。

除了fiddle,代码也在下面:

/**
 * Init
 */
var canvas = document.getElementsByClassName('canvas')[0];

window.onresize = function () {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
};

window.onresize();

var ctx = canvas.getContext('2d');

ctx.font = 'bold 50px "Arial"';
ctx.textBaseline = 'center';
ctx.fillStyle = '#fff';

var _particles = [];
var particlesLength = 0;

var currentText = "Create something beautiful";

if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = window.mozRequestAnimationFrame
        || window.webkitRequestAnimationFrame
        || window.msRequestAnimationFrame;
}

/**
 * Create one particle
 * @param x
 * @param y
 */
var createParticle = function createParticle(x, y) {
    _particles.push(new Particle(x, y));
};

/**
 * Check if pixel has alpha
 * @param pixels
 * @param i
 * @returns {boolean}
 */
var checkAlpha = function checkAlpha(pixels, i) {
    return pixels[i * 4 + 3] > 0;
};

/**
 * Create _particles
 */
var createParticles = function createParticles() {
    var textSize = ctx.measureText(currentText);
    ctx.fillText(
        currentText,
        Math.round((canvas.width / 2) - (textSize.width / 2)),
        Math.round(canvas.height / 2)
    );

    var imageData = ctx.getImageData(1, 1, canvas.width, canvas.height);
    var pixels = imageData.data;
    var dataLength = imageData.width * imageData.height;

    //Loop through all pixels
    for (var i = 0; i < dataLength; i++) {
        var currentRow = Math.floor(i / imageData.width);
        var currentColumn = i - Math.floor(i / imageData.height);

        if (currentRow % 2 || currentColumn % 2) {
            continue;
        }

        //If alpha channel is greater than 0
        if (checkAlpha(pixels, i)) {
            var cy = ~~(i / imageData.width);
            var cx = ~~(i - (cy * imageData.width));

            createParticle(cx, cy);
        }
    }

    particlesLength = _particles.length;
};

/**
 * new Point(x, y)
 * @param x pointer
 * @param y pointer
 * @constructor
 */
var Point = function Point(x, y) {
    this.set(x, y);
};

Point.prototype = {
    set: function (x, y) {
        x = x || 0;
        y = y || x || 0;

        /**
         * x start pointer
         * @type {*|number}
         * @private
         */
        this._sX = x;

        /**
         * y start pointer
         * @type {*|number}
         * @private
         */
        this._sY = y;

        /**
         * Call reset
         */
        this.reset();
    },

    /**
     * add one point to another
     * @param point
     */
    add: function (point) {
        this.x += point.x;
        this.y += point.y;
    },

    /**
     * multiply two points
     * @param point
     */
    multiply: function (point) {
        this.x *= point.x;
        this.y *= point.y;
    },

    /**
     * Reset point
     */
    reset: function () {
        /**
         * x pointer
         * @type {*|number}
         */
        this.x = this._sX;

        /**
         * y pointer
         * @type {*|number}
         */
        this.y = this._sY;

        return this;
    },
};

var FRICT = new Point(0.98);
/**
 * Particle constructor
 * @param x
 * @param y
 * @constructor
 */
var Particle = function Particle(x, y) {
    this.startPos = new Point(x, y);

    /**
     * Movement variables
     */
    this.v = new Point();
    this.a = new Point();

    /**
     * First init reset
     */
    this.reset();
};

Particle.prototype = {
    /**
     * Reset particle
     */
    reset: function () {
        this.x = this.startPos.x;
        this.y = this.startPos.y;

        this.life = Math.round(random() * 300);
        this.isActive = true;

        /**
         * Movement variables
         */
        this.v.reset();
        this.a.reset();
    },
    /**
     * Particle tick
     */
    tick: function () {
        if (!this.isActive) return;

        this.physics();
        this.checkLife();

        this.draw();

        return this.isActive;
    },
    /**
     * Calculate life
     */
    checkLife: function () {
        this.life -= 1;

        this.isActive = !(this.life < 1);
    },

    /**
     * Draw particle
     */
    draw: function () {
        ctx.fillRect(this.x, this.y, 1, 1);
    },

    /**
     * Calculate particle movement
     */
    physics: function () {
        this.a.x = (random() - 0.5) * 0.8;
        this.a.y = (random() - 0.5) * 0.8;

        this.v.add(this.a);
        this.v.multiply(FRICT);

        this.x += this.v.x;
        this.y += this.v.y;

        this.x = Math.round(this.x * 10) / 10;
        this.y = Math.round(this.y * 10) / 10;
    }
};

/**
 * Start the party
 */
createParticles();

/**
 * Clear canvas
 */
function clearCanvas() {
    ctx.fillStyle = 'rgba(255,255,255,0.2)';

    ctx.fillRect(0, 0, canvas.width, canvas.height);
}

(function clearLoop() {
    /**
     * Do clearing
     */
    clearCanvas();

    /**
     * next loop
     */
    requestAnimationFrame(clearLoop);
})();

/**
 * Main animation loop
 */
(function animLoop() {
    ctx.fillStyle = '#ea541b';
    var isAlive = true;

    /**
     * Loop _particles
     */
    for (var i = 0; i < particlesLength; i++) {
        /**
         * If particle is active
         */
        if (_particles[i].tick()) isAlive = true;
    }



    /**
     * next loop
     */
    requestAnimationFrame(animLoop);
})();

function resetParticles() {
    for (var i = 0; i < particlesLength; i++) {
        _particles[i].reset();
    }
}

【问题讨论】:

    标签: javascript jquery html canvas


    【解决方案1】:

    我认为您需要一个将点从一个位置移动到所需位置的函数,并添加一些随机性。这是我得到的:

    function getRandomMove(pos,end,speed,randomness) {
        var a=Math.random()<randomness
            ? Math.random()*Math.PI/2
            : Math.atan2(end.y-pos.y,end.x-pos.x);
        dist=Math.sqrt(Math.pow(end.x-pos.x,2)+Math.pow(end.y-pos.y,2));
        var spd=Math.min(speed,dist);
        return { x: pos.x+spd*Math.cos(a), y: pos.y+spd*Math.sin(a), dist:dist-spd };
    }
    

    其中速度是您希望粒子移动的速度,而随机性仅在 0 到 0.5 之间(可能更多,但执行速度会非常慢,甚至会破坏内存)。您可以将随机性设置为一个较小的值,因为如果它太小,它无论如何都会适应滴答声。

    这样做的问题是,如果您对每个粒子都使用它,您可能无法同时将所有粒子都放在您想要的位置。这就是为什么我想到这个函数以指定数量的步骤生成从一个位置到另一个位置的整个路径:

    function reduceMoves(moves,ticks) {
        var temp=[];
        for (var i=0; i<moves.length-2; i++) {
            var m1=moves[i];
            var m2=moves[i+2];
            temp.push({index:i+1,dist:Math.sqrt(Math.pow(m1.x-m2.x,2)+Math.pow(m1.y-m2.y,2))});
        }
        temp.sort(function(a,b) { return a.dist-b.dist; });
        temp=temp.splice(0,moves.length-ticks);
        temp.sort(function(a,b) { return b.index-a.index });
        temp.forEach(function(t) {
            moves.splice(t.index,1);
        });
    }
    
    function moveRandomly(pos,end,speed,randomness,ticks) {
        var move=pos;
        var result=[move];
        var dist=100000;
        while (dist>0.1) {
            move=getRandomMove(move,end,speed,randomness);
            result.push(move);
            dist=move.dist;
        }
        move.x=end.x;
        move.y=end.y;
        if (result.length<ticks) {
            return moveRandomly(pos,end,speed,randomness+0.1,ticks);
        }
        reduceMoves(result,ticks);
        return result;
    }
    

    把它当作

    var pos={x:Math.random()*500,y:Math.random()*500};
    var end={x:Math.random()*500,y:Math.random()*500};
    var moves=moveRandomly(pos,end,1,0.2,100);
    

    我没有时间测试它,我不知道它是否是你要找的,但我希望如此。

    【讨论】:

      【解决方案2】:

      只需将物理延迟几秒钟,以便用户可以看到文本:

      // set a time that's 3 seconds in the future
      var nextTime=performance.now()+3000;
      
      // wait until the current time is >= nextTime
      if(nextTime && performance.now()<nextTime){return;}else{nextTime=null;}
      

      示例代码和演示:

      var random=Math.random;
      
      
      /**
       * Init
       */
      var canvas = document.getElementById('canvas');
      
      window.onresize = function () {
          canvas.width = window.innerWidth;
          canvas.height = window.innerHeight;
      };
      
      window.onresize();
      
      var ctx = canvas.getContext('2d');
      
      ctx.font = 'bold 50px "Arial"';
      ctx.textBaseline = 'center';
      ctx.fillStyle = '#fff';
      
      var _particles = [];
      var particlesLength = 0;
      
      var currentText = "Create something beautiful";
      
      /**
       * Create one particle
       * @param x
       * @param y
       */
      var createParticle = function createParticle(x, y) {
          _particles.push(new Particle(x, y));
      };
      
      /**
       * Check if pixel has alpha
       * @param pixels
       * @param i
       * @returns {boolean}
       */
      var checkAlpha = function checkAlpha(pixels, i) {
          return pixels[i * 4 + 3] > 0;
      };
      
      /**
       * Create _particles
       */
      var createParticles = function createParticles() {
          var textSize = ctx.measureText(currentText);
          ctx.fillText(
              currentText,
              Math.round((canvas.width / 2) - (textSize.width / 2)),
              Math.round(canvas.height / 2)
          );
      
          var imageData = ctx.getImageData(1, 1, canvas.width, canvas.height);
          var pixels = imageData.data;
          var dataLength = imageData.width * imageData.height;
      
          //Loop through all pixels
          for (var i = 0; i < dataLength; i++) {
              var currentRow = Math.floor(i / imageData.width);
              var currentColumn = i - Math.floor(i / imageData.height);
      
              if (currentRow % 2 || currentColumn % 2) {
                  continue;
              }
      
              //If alpha channel is greater than 0
              if (checkAlpha(pixels, i)) {
                  var cy = ~~(i / imageData.width);
                  var cx = ~~(i - (cy * imageData.width));
      
                  createParticle(cx, cy);
              }
          }
      
          particlesLength = _particles.length;
      };
      
      /**
       * new Point(x, y)
       * @param x pointer
       * @param y pointer
       * @constructor
       */
      var Point = function Point(x, y) {
          this.set(x, y);
      };
      
      Point.prototype = {
          set: function (x, y) {
              x = x || 0;
              y = y || x || 0;
      
              /**
               * x start pointer
               * @type {*|number}
               * @private
               */
              this._sX = x;
      
              /**
               * y start pointer
               * @type {*|number}
               * @private
               */
              this._sY = y;
      
              /**
               * Call reset
               */
              this.reset();
          },
      
          /**
           * add one point to another
           * @param point
           */
          add: function (point) {
              this.x += point.x;
              this.y += point.y;
          },
      
          /**
           * multiply two points
           * @param point
           */
          multiply: function (point) {
              this.x *= point.x;
              this.y *= point.y;
          },
      
          /**
           * Reset point
           */
          reset: function () {
              /**
               * x pointer
               * @type {*|number}
               */
              this.x = this._sX;
      
              /**
               * y pointer
               * @type {*|number}
               */
              this.y = this._sY;
      
              return this;
          },
      };
      
      var FRICT = new Point(0.98);
      /**
       * Particle constructor
       * @param x
       * @param y
       * @constructor
       */
      var Particle = function Particle(x, y) {
          this.startPos = new Point(x, y);
      
          /**
           * Movement variables
           */
          this.v = new Point();
          this.a = new Point();
      
          /**
           * First init reset
           */
          this.reset();
      };
      
      Particle.prototype = {
          /**
           * Reset particle
           */
          reset: function () {
              this.x = this.startPos.x;
              this.y = this.startPos.y;
      
              this.life = Math.round(random() * 300);
              this.isActive = true;
      
              /**
               * Movement variables
               */
              this.v.reset();
              this.a.reset();
          },
          /**
           * Particle tick
           */
          tick: function () {
              if (!this.isActive) return;
      
              this.physics();
              this.checkLife();
      
              this.draw();
      
              return this.isActive;
          },
          /**
           * Calculate life
           */
          checkLife: function () {
              this.life -= 1;
      
              this.isActive = !(this.life < 1);
          },
      
          /**
           * Draw particle
           */
          draw: function () {
              ctx.fillRect(this.x, this.y, 1, 1);
          },
      
          /**
           * Calculate particle movement
           */
          physics: function () {
              if(performance.now()<nextTime){return;}
              this.a.x = (random() - 0.5) * 0.8;
              this.a.y = (random() - 0.5) * 0.8;
      
              this.v.add(this.a);
              this.v.multiply(FRICT);
      
              this.x += this.v.x;
              this.y += this.v.y;
      
              this.x = Math.round(this.x * 10) / 10;
              this.y = Math.round(this.y * 10) / 10;
          }
      };
      
      /**
       * Start the party
       */
      var nextTime=performance.now()+3000;
      createParticles();
      
      /**
       * Clear canvas
       */
      function clearCanvas() {
          ctx.fillStyle = 'rgba(255,255,255,0.2)';
      
          ctx.fillRect(0, 0, canvas.width, canvas.height);
      }
      
      (function clearLoop() {
          /**
           * Do clearing
           */
          clearCanvas();
      
          /**
           * next loop
           */
          requestAnimationFrame(clearLoop);
      })();
      
      /**
       * Main animation loop
       */
      (function animLoop(time) {
      
          ctx.fillStyle = '#2c87c4';
          var isAlive = true;
      
          /**
           * Loop _particles
           */
          for (var i = 0; i < particlesLength; i++) {
              /**
               * If particle is active
               */
              if (_particles[i].tick()) isAlive = true;
          }
      
         
      
          /**
           * next loop
           */
          requestAnimationFrame(animLoop);
      })();
      
      function resetParticles() {
          for (var i = 0; i < particlesLength; i++) {
              _particles[i].reset();
          }
      }
      body{ background-color: ivory; }
      canvas{border:1px solid red; margin:0 auto; }
      &lt;canvas id="canvas"&gt;&lt;/canvas&gt;

      我可以想到至少 2 种其他方式来制作变速动画。

      1. 使用 Robert Penner 的 easing functions 来减慢动画的开始速度(以延长文本的显示时间)并加快动画的结束速度(以更快地显示溶解)。这是以前的 Stackoverflow Q&A,其中有一个在动画中使用缓动的示例。

      2. requestAnimationFrame 自动发送时间戳参数,您可以使用该参数来改变动画的执行。使用时间戳计算自上次动画循环以来经过的时间。在动画序列开始时允许更长的经过时间。这允许文本显示更长的时间。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-11-22
        • 1970-01-01
        • 1970-01-01
        • 2013-11-20
        • 2014-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多