views:

231

answers:

2

Using Javascript I'm crudely simulating Brownian motion of particles, but for some reason I don't understand my particles are drifting up and to the left.

The algorithm is pretty straight forward. Each particle is a div and I simply add or subtract a random number from each div's top and left position each round.

I read up on Math.random() a little, and I've tried to use a function that returns a random number from min to max inclussive:

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

Here is the function for the movement of the particles:

var x, y, $elie, pos, nowX, nowY, i, $that;    

function moveIt()
{
    $("div.spec").each(function(i, v) {
        x = ran(-5, 5);
        y = ran(-5, 5);
        $elie = $(v);
        pos = $elie.position();
        nowX = pos.left;
        nowY = pos.top;

          // The min and abs are to keep the particles within a box
          // The drift occurs even if I remove min and abs
        $elie.css("left", Math.min(Math.abs(nowX + x), 515));
        $elie.css("top",  Math.min(Math.abs(nowY + y), 515)); 
    });
}

And here is how the particles are initially set up an the setInterval started.

$(function() {
    $("body").append("<div/>").attr("id","box");
    $elie = $("<div/>").attr("class","spec");
    // Note that math random is inclussive for 0 and exclussive for Max
    for (i = 0; i < 25; ++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 ";            
    });        
});

My problem is that using the min and max from above ( -5, 5 ), all the particles drift up and to the left very fast.

jsFiddle example of drift (-5, 5)

Example of drift even with the removal of .min() and .abs().

To counteract this, I have to use a min and max of -1, 5.

jsFiddle example of no drift (-1, 5)


Here is the CSS for the div all the particles are contained in:

#box {
    width:500px;
    height:500px;
    border:2px #000 solid;
    position: relative; }

Here is the default CSS for each particle:

div.spec {
    width:5px;
    height:5px;
    background-color:#00DDAA;
    position:absolute; }

What is going on? Why does a min and max of -5 and 5 cause an upward and leftward drift?

A test of the random function ran() doesn't seem to show such a persistent negative drift.

jsFiddle example of testing ran()



The ran() function was taken from the MDC Math.random() page.

+2  A: 

Instead of using .position(), try .offset() instead. Looks like it works. http://jsfiddle.net/gxesg/

Position: http://api.jquery.com/position/

Offset: http://api.jquery.com/offset/

It works this way because you're setting the absolute 'left' and 'top' values in CSS. Instead, you can use this (example: http://jsfiddle.net/GtTLv/):

$elie.css("margin-left", nowX + x);
$elie.css("margin-top",  nowY + y);
ChessWhiz
`.offset()` does seem to solve the problem. My particles are contained in a parent `div`, so I thought `.position()` would work too. Why isn't it working?
Peter Ajtai
It's because your css setter is setting an absolute 'left' and 'top' instead of one relative to the parent. Instead, use 'margin-left'. See my edit.
ChessWhiz
Actually, the parent `div` has a `relative` position and the particles have an `absolute` position within the parent. The problem seems to be something funky with the parent's `id`.
Peter Ajtai
Ah, interesting. Sounds like fixing the root issue is the best way to go here.
ChessWhiz
+5  A: 

Your mistake is to use

pos = $elie.position();

rather than

pos = $elie.offset();

This wouldn't have made a difference had they been added to parent div, but your elements aren't properly added to a parent div, they're appended directly to the document body. So your other mistake is this:

$("body").append("<div/>").attr("id","box");

If you want the div to have id of 'box', the line should read:

$box = $("<div/>").attr("id","box");
$("body").append($box)

Otherwise you're actually giving "body" the id of "box"

EDIT:

The most efficient way to append the div would be the following (as noted by this post):

$(document.createElement('div')).appendTo('body').attr('id', 'box')
Herman
Good eye. [ ](http://eca.com)
Peter Ajtai
Thanks, that's handy to know. I added it to the answer.
Herman
Thanks Herman, the speed tests for the various ways of adding elements to the DOM come from [ **this answer** ](http://stackoverflow.com/questions/327047/what-is-the-most-efficient-way-to-create-html-elements-using-jquery/327065#327065). --------------- PS: I was editing my comment and removed that piece of code by accident, and then the time for editing the comment expired.
Peter Ajtai
Here's the final version of this (not so good in IE) - http://fiddle.jshell.net/PppZ3/show/light/ - I think this calls for another type of solution... looking into [ **Raphael** ](http://raphaeljs.com)
Peter Ajtai