代码之家  ›  专栏  ›  技术社区  ›  HasanG Joe Dabones

随机自然运动jquery

  •  6
  • HasanG Joe Dabones  · 技术社区  · 14 年前

    如何使用jquery为图像重新创建此类型移动: http://www.istockphoto.com/stock-video-12805249-moving-particles-loop-soft-green-hd-1080.php

    我打算用它作为网页背景。如果jquery无法实现,我将使用flash as3。但我更喜欢jquery。

    4 回复  |  直到 13 年前
        1
  •  10
  •   Peter Ajtai    14 年前

    编辑: Raphael 显然更适合于此,因为它支持IE。jquery的问题是,由于CSS约束,圆角在IE中很难做到…在拉斐尔,交叉浏览的圈子里没有汗水。

    jsFiddle with Raphael - all browsers :

    (尽管看起来可能更好) speeded up in IE )

    (function() {
        var paper, circs, i, nowX, nowY, timer, props = {}, toggler = 0, elie, dx, dy, rad, cur, opa;
        // Returns a random integer between min and max  
        // Using Math.round() will give you a non-uniform distribution!  
        function ran(min, max)  
        {  
            return Math.floor(Math.random() * (max - min + 1)) + min;  
        } 
    
        function moveIt()
        {
            for(i = 0; i < circs.length; ++i)
            {            
                  // Reset when time is at zero
                if (! circs[i].time) 
                {
                    circs[i].time  = ran(30, 100);
                    circs[i].deg   = ran(-179, 180);
                    circs[i].vel   = ran(1, 5);  
                    circs[i].curve = ran(0, 1);
                    circs[i].fade  = ran(0, 1);
                    circs[i].grow  = ran(-2, 2); 
                }                
                    // Get position
                nowX = circs[i].attr("cx");
                nowY = circs[i].attr("cy");   
                   // Calc movement
                dx = circs[i].vel * Math.cos(circs[i].deg * Math.PI/180);
                dy = circs[i].vel * Math.sin(circs[i].deg * Math.PI/180);
                    // Calc new position
                nowX += dx;
                nowY += dy;
                    // Calc wrap around
                if (nowX < 0) nowX = 490 + nowX;
                else          nowX = nowX % 490;            
                if (nowY < 0) nowY = 490 + nowY;
                else          nowY = nowY % 490;
    
                    // Render moved particle
                circs[i].attr({cx: nowX, cy: nowY});
    
                    // Calc growth
                rad = circs[i].attr("r");
                if (circs[i].grow > 0) circs[i].attr("r", Math.min(30, rad +  .1));
                else                   circs[i].attr("r", Math.max(10,  rad -  .1));
    
                    // Calc curve
                if (circs[i].curve > 0) circs[i].deg = circs[i].deg + 2;
                else                    circs[i].deg = circs[i].deg - 2;
    
                    // Calc opacity
                opa = circs[i].attr("fill-opacity");
                if (circs[i].fade > 0) {
                    circs[i].attr("fill-opacity", Math.max(.3, opa -  .01));
                    circs[i].attr("stroke-opacity", Math.max(.3, opa -  .01)); }
                else {
                    circs[i].attr("fill-opacity", Math.min(1, opa +  .01));
                    circs[i].attr("stroke-opacity", Math.min(1, opa +  .01)); }
    
                // Progress timer for particle
                circs[i].time = circs[i].time - 1;
    
                    // Calc damping
                if (circs[i].vel < 1) circs[i].time = 0;
                else circs[i].vel = circs[i].vel - .05;              
    
            } 
            timer = setTimeout(moveIt, 60);
        }
    
        window.onload = function () {
            paper = Raphael("canvas", 500, 500);
            circs = paper.set();
            for (i = 0; i < 30; ++i)
            {
                opa = ran(3,10)/10;
                circs.push(paper.circle(ran(0,500), ran(0,500), ran(10,30)).attr({"fill-opacity": opa,
                                                                               "stroke-opacity": opa}));
            }
            circs.attr({fill: "#00DDAA", stroke: "#00DDAA"});
            moveIt();
            elie = document.getElementById("toggle");
            elie.onclick = function() {
                (toggler++ % 2) ? (function(){
                        moveIt();
                        elie.value = " Stop ";
                    }()) : (function(){
                        clearTimeout(timer);
                        elie.value = " Start ";
                    }());
            }
        };
    }());​
    

    第一次尝试jquery解决方案如下:


    这个jquery尝试在IE中失败很多,在FF中速度很慢。Chrome和Safari做得很好:

    jsFiddle example for all browsers (IE is not that good)

    (我没有执行淡入式IE,而且IE没有圆角…另外,JS速度较慢,因此总体上看起来相当糟糕)

    jsFiddle example for Chrome and Safari only (4x more particles)

    (function() {
        var x, y, $elie, pos, nowX, nowY, i, $that, vel, deg, fade, curve, ko, mo, oo, grow, len;
    
        // Returns a random integer between min and max  
        // Using Math.round() will give you a non-uniform distribution!  
        function ran(min, max)  
        {  
            return Math.floor(Math.random() * (max - min + 1)) + min;  
        } 
    
        function moveIt()
        {
            $("div.spec").each(function(i, v) {
                $elie = $(v);
                if (! $elie.data("time"))
                {
                    $elie.data("time", ran(30, 100));
                    $elie.data("deg", ran(-179, 180));
                    $elie.data("vel", ran(3, 10));  
                    $elie.data("curve", ran(0, 1));
                    $elie.data("fade", ran(0, 1));
                    $elie.data("grow", ran(-2, 2));                
                }
    
                vel = $elie.data("vel");
                deg = $elie.data("deg");
                fade = $elie.data("fade");            
                curve = $elie.data("curve");
                grow = $elie.data("grow");
    
                len = $elie.width();
                if (grow > 0)
                    len = Math.min(len + grow, 50);
                else
                    len = Math.max(len + grow, 20);
    
                $elie.css("-moz-border-radius", len/2);
                $elie.css("border-radius", len/2);
    
                $elie.css("width", len);
                $elie.css("height", len);
    
                pos = $elie.position();            
    
                $elie.data("time", $elie.data("time") - 1);
    
                if (curve)
                    $elie.data("deg", (deg + 5) % 180);
                else
                    $elie.data("deg", (deg - 5) % 180);
    
                ko = $elie.css("-khtml-opacity");
                mo = $elie.css("-moz-opacity");
                oo = $elie.css("opacity");
                if (fade)
                {
                    $elie.css("-khtml-opacity", Math.max(ko - .1, .5));
                    $elie.css("-moz-opacity", Math.max(mo - .1, .5));
                    $elie.css("opacity", Math.max(oo - .1, .5));
                } else
                {
                    $elie.css("-khtml-opacity", Math.min(ko - -.1, 1));
                    $elie.css("-moz-opacity", Math.min(mo - -.1, 1));
                    $elie.css("opacity", Math.min(oo - -.1, 1));                
                }
    
                if (vel < 3)
                    $elie.data("time", 0);
                else
                    $elie.data("vel", vel - .2);            
    
    
                nowX = pos.left;
                nowY = pos.top;
    
                x = vel * Math.cos(deg * Math.PI/180);
                y = vel * Math.sin(deg * Math.PI/180);
    
                nowX = nowX + x;            
                nowY = nowY + y;
    
                if (nowX < 0)
                    nowX = 490 + nowX;
                else
                    nowX = nowX % 490;
    
                if (nowY < 0)
                    nowY = 490 + nowY;
                else
                    nowY = nowY % 490;            
                $elie.css("left", nowX);
                $elie.css("top",  nowY);
            });
        }
        $(function() {
            $(document.createElement('div')).appendTo('body').attr('id', 'box');
            $elie = $("<div/>").attr("class","spec");
            // Note that math random is inclussive for 0 and exclussive for Max
            for (i = 0; i < 100; ++i)
            {
                $that = $elie.clone();  
                $that.css("top", ran(0, 495));
                $that.css("left", ran(0, 495));            
                $("#box").append($that);            
            }          
            timer = setInterval(moveIt, 60);
            $("input").toggle(function() {
                clearInterval(timer);
                this.value = " Start ";
            }, function() {
                timer = setInterval(moveIt, 60);        
                this.value = " Stop ";            
            });        
        });
    }());
    ​
    
        2
  •  4
  •   David Thomas    14 年前

    [部分答案,仅针对物理。]

    [我刚才看到了前面的答案,我的答案有点类似。]

    你可以试着模拟一些布朗运动,即 来自随机力和粘性阻尼的组合。Pseudocode:

    initialize:
        x = random_position();
        v_x = random_velocity();  // v_x = velocity along x
        // and same for y
    for (each time step) {
        x += v_x;
        v_x += random_force() - time_step / damping_time * v_x;
        // and same for y
    }
    

    保持阻尼时间长(~1秒),随机力振幅小。否则,运动可能会过于剧烈。

    为了实现一个简单的高斯随机数生成器,请在维基百科中查找Box Muller。

        3
  •  2
  •   colithium    14 年前

    根据数学原理,你给每个物体一个起始位置和速度。“随机行走”是通过计算受一定数量限制的随机角度来实现的(实验)。然后用这个角度改变速度矢量的角度。你也可以计算一个随机的速度增量,然后改变这个数量的向量的大小。因为你使用的是速度,所以运动会比较平稳。一种更先进的直接利用加速度计算速度和位置的方法。

    对于随机转向值,二项分布优于均匀分布。二项分布集中在0附近,而不是均匀分布。您只需执行random()-random()(psuedocode)

    矢量数学有广泛的文档记录,但如果遇到障碍,请留下评论。

        4
  •  1
  •   renevdkooi    13 年前

    我这边的回答很晚,但我想我可能会给出一个方法…

    我个人会使用SVG矢量图像。 创建一个jquery插件,它接受不透明度和大小。使它们随机移动。 然后在创建一组这些粒子时执行javascript循环(不透明度和大小是随机的,加上起始位置是随机的) 然后让jquery插件在卸载粒子时启动自身的新实例。

    (如果你看这部小电影,你会发现它们向一个方向移动,然后淡出,然后另一部淡入。)

    不透明度效果将提供深度透视。

    不确定我的回答是否有帮助,但我会朝那个方向走。