1

I'm trying to build a physics simulation where DOM objects are subjected to gravity and can be dragged and thrown around while still colliding with each other and reaction to the collision in realistic ways.

As a reference: https://lovehotel.world/home

The main issue in doing such a thing is that checking for the collision of the DOM elements and having them to realistically react to it it gets super messy, mainly because we're talking about rectangles from a geometric point of view.

The issues I've been encountering are then many, mainly that it's quite easy to do with ellipses native to p5.js (codepen for that: https://codepen.io/giambrodo/pen/LYBxMZZ)

When it's about building it using links for instance it gets quite messy, I understand that checking for the rectangle collision is super messy compared to a perfect circle (where the distance from center to border is always the same).

I managed to get the links to replace the ellipses: https://editor.p5js.org/giampo/sketches/SzRduNmSm

{ "links": [
    {
        "label": "who are you?",
        "section_link": "who"
    },
    {
        "label": "what do you do?",
        "section_link": "what"
    },
    {
        "label": "how do you do that?",
        "section_link": "how?"
    },
    {
        "label": "why?",
        "section_link": "why"
    },
    {
        "label": "where?",
        "section_link": "where"
    }
]
}
let numBalls = 13;
let spring = 0.05;
let gravity = 0.05;
let friction = -0.9;
let balls = []; 
let menuData;


function preload() {
    menuData = loadJSON('menu.json');
}

function setup() {
    createCanvas(500, 500);
    for (let i = 0; i < menuData.links.length; i++) {
    balls[i] = new Ball(
      random(width),
      random(height),
      random(30, 40),
      i,
      balls
    );
  }
  noStroke();
  fill(255, 204);
}

function draw() {
  background(0);
  balls.forEach(ball => {
    ball.collide();
    ball.move();
    ball.display();
  });
}

function mousePressed() {
    balls.forEach(ball => {
        if ( ball.onBall(mouseX, mouseY))
        ball.startDrag();
      }
    );
  }
  
function mouseDragged() {
    balls.forEach(ball => {
      if (ball.dragging) {
        ball.x = mouseX;
        ball.y = mouseY;
      }
    });
  }
  
function mouseReleased() {
    balls.forEach(ball => {
      if (ball.dragging) {
        // Calculate the ball's new velocity based on how fast the mouse was moving when it was released
        ball.vx = (mouseX - ball.mousex) / 10;
        ball.vy = (mouseY - ball.mousey) / 10;
  
        // Set the ball's dragging property to false to indicate that it is no longer being dragged
        ball.dragging = false;
      }
    });
  }
  
  

class Ball {
  constructor(xin, yin, din, idin, oin) {
    this.x = xin;
    this.y = yin;
    this.vx = 0;
    this.vy = 0;
    this.diameter = din;
    this.id = idin;
    this.others = oin; 
    this.dragging = false;
    this.anchor = createA(menuData.links[this.id].href, menuData.links[this.id].label);
  }
  

onBall(x, y) {
    // Get the bounding rect of the anchor element
    let rect = this.anchor.elt.getBoundingClientRect();
    
    // Check if the point (x, y) is within the bounding rect
    return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
  }


  startDrag() {
    this.dragging = true;
    this.mousex = mouseX;
    this.mousey = mouseY;
  }

  collide() {
    for (let i = this.id + 1; i < menuData.links.length; i++) {
      // Calculate distance and minimum distance between rectangles
      let dx = this.others[i].anchor.elt.getBoundingClientRect().x - this.anchor.elt.getBoundingClientRect().x;
      let dy = this.others[i].anchor.elt.getBoundingClientRect().y - this.anchor.elt.getBoundingClientRect().y;
      let distance = sqrt(dx * dx + dy * dy);
      let minDist = (this.others[i].anchor.elt.getBoundingClientRect().width + this.anchor.elt.getBoundingClientRect().width) / 2;
      
      // Check if rectangles are colliding
      if (distance < minDist) {
        // Calculate angle between rectangles
        let angle = atan2(dy, dx);
        // Calculate target position for rectangles to avoid overlap
        let targetX = this.anchor.elt.getBoundingClientRect().x + cos(angle) * minDist;
        let targetY = this.anchor.elt.getBoundingClientRect().y + sin(angle) * minDist;
        // Calculate force of collision using mass and elasticity
        let ax = (targetX - this.others[i].anchor.elt.getBoundingClientRect().x) * spring;
        let ay = (targetY - this.others[i].anchor.elt.getBoundingClientRect().y) * spring;
        this.vx -= ax;
        this.vy -= ay;
        this.others[i].vx += ax;
        this.others[i].vy += ay;
      }
    }
  }

  move() {

    if (this.dragging) {
        this.x = mouseX;
        this.y = mouseY;
    } else {
        // gravità
        this.vy += gravity;
        // update ball's position based on its velocity
        this.x += this.vx;
        this.y += this.vy;

        
        if (this.x + this.diameter / 2 > width) {
        this.x = width - this.diameter / 2;
        this.vx *= friction;
        } else if (this.x - this.diameter / 2 < 0) {
        this.x = this.diameter / 2;
        this.vx *= friction;
        }
        if (this.y + this.diameter / 2 > height) {
        this.y = height - this.diameter / 2;
        this.vy *= friction;
        } else if (this.y - this.diameter / 2 < 0) {
        this.y = this.diameter / 2;
        this.vy *= friction;
        }
    }
  }
  

  display() {
    this.anchor.style('position: relative');
    this.anchor.position(this.x, this.y);
    this.anchor.style('z-index: 3)');
    this.anchor.parent('maradona');
    this.anchor.style('border-style: solid');
    this.anchor.style('border-color: red');
  }
}

Can someone help me in finding a better way to deal with this?

giampo
  • 21
  • 2
  • Maybe use a physics library like Matter.js. Then you can plug in DOM elements like the love hotel example. See [1](https://stackoverflow.com/questions/63906218/using-matter-js-to-render-to-the-dom-or-react/65354225#65354225) [2](https://stackoverflow.com/questions/64432514/how-to-make-items-draggable-and-clickable/64453027#64453027) for examples. – ggorlen Jan 11 '23 at 15:14

0 Answers0