Skip to content

Dynamics.applyCollision()

Turn your DOM elements into physical objects that bounce off each other and the walls of their container! applyCollision() uses circular hitboxes to calculate realistic 2D physics.


1. Container Billiards

To get started, simply pass an array of elements (or a CSS selector) to Dynamics.applyCollision(). AnimX will automatically track their positions and bounce them off one another.

Click the stage to scatter the balls with random velocity.
60 FPS
js
import { Dynamics } from 'animx/dynamics'

const engine = Dynamics.applyCollision('.balls', {
  bounds: '#stage',    // Container walls
  friction: 0.995,     // Rolling friction (1 = endless rolling)
  restitution: 0.9     // Bounciness of collisions (1 = perfectly elastic)
});

2. Collision & Wall Callbacks

You can listen for exactly when two items collide or when an item hits a wall by using the onCollide and onWallBounce callbacks. AnimX even provides the impact force of the collision, which is perfect for driving responsive UI animations (like making a ball squish harder the faster it hits the wall) or triggering sound effects.

Click the stage to see the balls squish when they collide.
60 FPS
js
Dynamics.applyCollision('.balls', {
  bounds: '#stage',
  onCollide: (el1, el2, impact) => {
    console.log(`Collision between elements with impact force ${impact}`);
    AnimX.animate(el1, { scale: 1.2, duration: 0.1, yoyo: true });
  },
  onWallBounce: (el, side, impact) => {
    // side will be 'top', 'bottom', 'left', or 'right'
    console.log(`Hit ${side} wall with force ${impact}`);
  }
});

3. Interactive Grab & Throw

Collisions play perfectly with the Interactable module! When dragging an element, use engine.setDragged(el, true) to temporarily pause physics on that specific element. When the user lets go, use engine.injectVelocity() to throw it back into the mix.

Drag and throw the white cue ball into the red targets!
60 FPS
js
const engine = Dynamics.applyCollision('.ball', { bounds: '#stage' });
let tracker;

Interactable.create('.cue-ball', {
  bounds: '#stage',
  onPress() {
    // 1. Isolate the target from physics integration while interacting
    engine.setDragged(this.target, true); 
    tracker = Dynamics.trackVelocity(this.target, 'x,y');
  },
  onRelease() {
    // 2. Re-integrate the target upon release
    engine.setDragged(this.target, false);
    
    // 3. Inject the tracked pointer velocity
    engine.injectVelocity(this.target, tracker.get('x'), tracker.get('y'));
    tracker.kill();
  }
});

Properties & Methods

Configuration Options

OptionTypeDefaultDescription
frictionnumber0.99How much velocity the items retain per frame. 1.0 means they roll forever, while lower numbers (like 0.9) make them slow down quickly.
restitutionnumber0.9The bounciness of the collision! 1.0 is perfectly bouncy (no energy lost), while 0.5 means it loses half its speed when bouncing.
boundsstring | Element | { minX?: number, maxX?: number, minY?: number, maxY?: number }nullThe container walls. Accepts a CSS selector, DOM element, or custom coordinate object.
onCollideFunctionnullFires when two elements hit each other. Provides (el1, el2, impact), where impact is the force of the collision.
onWallBounceFunctionnullFires when an element hits the container wall. Provides (el, side, impact), where side is 'top', 'bottom', 'left', or 'right'.

Engine Methods

The object returned by applyCollision() controls the entire simulation:

MethodDescription
setDragged(el, true)Tells the engine to ignore this element because the user is currently dragging it.
setDragged(el, false)Hands control of the element back to the physics engine.
injectVelocity(el, vx, vy)Instantly applies a push (velocity in px/s) to the element. Wakes up the engine automatically if it was sleeping.
kill()Stops the physics simulation completely and cleans up all listeners.
stateRead-only array of all objects in the simulation, including their current x, y, vx, vy, and radius.