画布粒子文字

时间:2016-03-22 13:47:44

标签: javascript jquery html5 canvas

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

我想在这里尝试一些事情,但我有点陷入困境。

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

  2. 我也想拥有它,以便随后的粒子运动然后形成文本,在文本形式中保持几秒钟,然后再次分解成粒子。例如。随机粒子&gt; <粒子形成的文本>随机粒子&gt;屏幕清晰。

  3. 以下代码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();
        }
    }
    

2 个答案:

答案 0 :(得分:0)

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

// 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;}

示例代码和演示:

&#13;
&#13;
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();
    }
}
&#13;
body{ background-color: ivory; }
canvas{border:1px solid red; margin:0 auto; }
&#13;
<canvas id="canvas"></canvas>
&#13;
&#13;
&#13;

我至少可以考虑另外两种方法来制作变速动画。

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

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

答案 1 :(得分:0)

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

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);

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