Repulsive force in Javascript with Mootools draggables
4 January 2009 | 3 Comments | 1,074 view(s)
A couple of days ago I was reading about the Boids demo made by Ben from Coderholic. I though that was very interesting, specially since I hadn’t heard about boids before.
I wanted to create my own version but instead I decided to play only with the repulsive force, making it interactive using Mootools drag & drop functionality. When the demo loads, 20 elements will be created and they will start moving on their own, trying to keep the minimum distance with any other element. This distance is configurable, as it is the option to keep the element speed or to make them slow down until they stop. At any time, the user can move elements on the screen dragging them with the mouse.
See the demo in action.
Source code
-
var Item = new Class({
-
Implements: Options,
-
-
options: {
-
radius: 10,
-
position: { x: window.clientWidth/2, y: window.clientHeight/|>2 },
-
speed: { x: 0, y: 0 },
-
color: '#333'
-
},
-
-
initialize: function(options) {
-
this.setOptions(options);
-
this.addToDOM();
-
-
var self = this;
-
this.dragController = new Drag(this.element, {
-
onStart: function() {
-
self.dragging = true;
-
},
-
onDrag: function() {
-
self.options.position = self.element.getPosition();
-
},
-
onComplete: function() {
-
self.options.position = self.element.getPosition();
-
self.speed = {x:0,y:0};
-
self.dragging = false;
-
}
-
});
-
this.dragging = false;
-
},
-
-
destroy: function() {
-
this.element.destroy();
-
delete this;
-
},
-
-
addToDOM: function() {
-
this.element = new Element('div', { 'class': 'item' });
-
this.element.setStyles({
-
background: this.options.color,
-
height: 2*this.options.radius,
-
width: 2*this.options.radius,
-
left: this.options.position.x,
-
top: this.options.position.y,
-
'-moz-border-radius': Math.round(this.options.radius) + 'px',
-
'-webkit-border-radius': Math.round(this.options.radius) + 'px',
-
'border-radius': Math.round(this.options.radius) + 'px'
-
}).inject(document.body);
-
},
-
-
moveTo: function(x, y) {
-
if (this.dragging) return;
-
-
this.options.position.x = x;
-
this.options.position.y = y;
-
-
// Check bounds
-
if (this.options.position.x < 0) {
-
this.options.position.x = 0;
-
this.options.speed.x = 0;
-
}
-
if (this.options.position.x > window.getWidth() – 2*this.options.radius) {
-
this.options.position.x = window.getWidth() – 2*this.options.radius;
-
this.options.speed.x = 0;
-
}
-
if (this.options.position.y < $('header').getHeight()) {
-
this.options.position.y = $('header').getHeight();
-
this.options.speed.y = 0;
-
}
-
if (this.options.position.y > window.getHeight() – 2*this.options.radius) {
-
this.options.position.y = window.getHeight() – 2*this.options.radius;
-
this.options.speed.y = 0;
-
}
-
-
// Slow down
-
if ($('chk-slowdown').get('checked')) {
-
this.options.speed.x *= 0.7;
-
this.options.speed.y *= 0.7;
-
}
-
-
if (this.options.speed.x < 0.001) this.options.speed.x = 0;
-
if (this.options.speed.y < 0.001) this.options.speed.y = 0;
-
},
-
-
draw: function() {
-
if (this.dragging) return;
-
this.element.setStyles({
-
left: Math.round(this.options.position.x),
-
top: Math.round(this.options.position.y)
-
});
-
}
-
})
-
-
var ItemController = new Class({
-
Extends: Thread,
-
items: [],
-
-
initialize: function(options) {
-
this.parent(options);
-
},
-
-
addItem: function(options) {
-
this.items.push(new Item(options));
-
},
-
-
execute: function(){
-
this.compute();
-
this.draw();
-
},
-
-
compute: function() {
-
var self = this;
-
self.items.each(function(item1, index1) {
-
self.items.each(function(item2, index2) {
-
if (index1 != index2) {
-
var distance = {}
-
distance.x = (item2.options.position.x + item2.options.radius) -
-
(item1.options.position.x + item1.options.radius);
-
distance.y = (item2.options.position.y + item2.options.radius) -
-
(item1.options.position.y + item1.options.radius);
-
-
distance.total = Math.sqrt(Math.pow(distance.x, 2) + Math.pow(distance.y, 2));
-
if (distance.total < $('distance').get('value')) {
-
-
var angle = Math.atan2(distance.y, distance.x);
-
var speed = 1000 / distance.total;
-
-
item1.options.speed.x += -speed * Math.cos(angle);
-
item1.options.speed.y += -speed * Math.sin(angle);
-
}
-
}
-
});
-
-
if (item1.options.speed.x || item1.options.speed.y) {
-
item1.moveTo(item1.options.position.x + item1.options.speed.x * (self.options.interval/1000),
-
item1.options.position.y + item1.options.speed.y * (self.options.interval/1000));
-
}
-
});
-
},
-
-
draw: function() {
-
this.items.each(function(item) { item.draw(); });
-
},
-
-
addRandom: function() {
-
this.addItem({
-
position: { x: Random.get(100,500), y: Random.get(100,400) },
-
color: 'rgb('+Random.get(0,255)+','+Random.get(0,255)+','+Random.get(0,255)+')'
-
});
-
}
-
});
-
-
window.addEvent('domready', function() {
-
window.system = new ItemController({autostart: true, interval: 50});
-
for (var i=0;i<20;i++)
-
system.addRandom();
-
-
$('btnAddRandom').addEvent('click', function(){
-
system.addRandom();
-
});
-
});
The code is very similar to the Gravity demo, although in this case the attraction becomes repulsion.

that’s just cool, another great example of what’s possible using MooTools.
Me “caguen sos” jajja, ya es dificil llegar aqui. Pulsè por curiosidad en un blog de un español en irlanda, a ver si vivias y lleguè hasta aqui. Como comprenderàs lei por encima y entre informatica e ingles no me enterè de nada.
Yo te doy la enhorabuena porque me medio enterè que te has hecho padre y te felicito el año nuevo. Siento dar la nota y escribir en español pero es lo que hay. Un abrazo Eneko y espero que todo te siga yendo bien.
Gracias Alberto :)