Create Interactive Pill Animation Using Matter.js
Tags: webdev, nextjs, react, ui
Published: 2024-03-02 12:57:36

Recently, our team at **[createxp](http://www.createxp.in/)** undertook a fascinating challenge: developing a portfolio website for a creative agency. The project involved numerous animations and interactions with the primary objective of maximising user delight and creating a visually stunning digital experience. Among the many animations, our team's favorite was the Interactive Pill Animation.

![GIF of Interactive Pill Animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwvjxwauqj4vbep1xbnp.gif)

Let's dive deep into the technicalities behind making this draggable pill-interaction animation using **Matter.js** and **Next.js**

## **Breaking Down The Animation**

1. Pills dropping from above when the section comes into view.
2. Interacting and moving the pills around using cursor.

The animation is created using a 2D physics engine for the web called **[Matter.js](https://brm.io/matter-js/)**

### Start by adding Matter.JS

```tsx
// yarn

yarn add matter-js

//npm

npm i matter-js
```

If you are using typescript, make sure you add the types for Matter.js

```tsx
// yarn

yarn add -D @types/matter-js

//npm

npm i -D @types/matter-js
```

### **Create a Scene**

A scene is like a canvas where Matter.js shows its objects. To link a div to Matter.js, we make a div and give it a 'ref'.

```tsx
const matterRef = React.createRef<HTMLDivElement>();
```

Create a wrapper for the scene

```jsx
<div ref={matterRef}></div>
```

In order to render objects to a scene, we need a canvas. We don’t have to create a canvas on our own, we can use the Matter.js create function to do that

```jsx
const canvas = matterRef.current;
        let Engine = Matter.Engine,
            Render = Matter.Render,
            Runner = Matter.Runner,
            Bodies = Matter.Bodies,
            Composite = Matter.Composite,
            Mouse = Matter.Mouse,
            MouseConstraint = Matter.MouseConstraint;
Events;
```

This will create a Canvas for us to work with.

### Activating Matter.js Engine

We need a way to render objects into our canvas, that is where the Matter.js Engine comes into the picture.

Engine is nothing but a function provided by the Matter.js to paint object into our canvas.

We can create a Engine using the following Code.

```tsx
// create an engine
let engine = Engine.create();
```

### **Rendering Objects**

To render all the shapes which we want we need to render them on the canvas. For that we can use the Matter.js inbuilt renderer. To invoke copy the following code.

```tsx
let render = Render.create({
            element: canvas || undefined,
            engine: engine,

            options: {
                wireframes: false,
                width: window.innerWidth,
                height: window.innerHeight / 3,
                background: "transparent",
            },
});
```

<aside>
💡 In Many iOS device rendered matter bodies are blurred. To fix that matter-js lets us set the PixelRatio. 

`Render.setPixelRatio(render, window.devicePixelRatio *as* number);`

Adding the above line will fix all the blur problems. Learn more about this [here](https://brm.io/matter-js/docs/classes/Render.html#method_setPixelRatio).

</aside>

### Create Sprites

Matter.js treats each element as rigid body. It provides a lot of pre defined bodies which you can use in your projects. But in our case we wanted our own images to be rendered for that  `sprites` can be used.

To create your own sprite first define your body which can be done as follows.

- Select a predefined body from the given list.
    - https://brm.io/matter-js/docs/classes/Bodies.html
        
        
- Each body has 4 `parameters:**Matter.Bodies.[circle](https://brm.io/matter-js/docs/classes/Bodies.html#method_circle)**(x, y, radius, [options], [maxSides]) → [Body](https://brm.io/matter-js/docs/classes/Body.html)`
- Each body has option in which you can select the fillStyle, strokeStyles and other properties
    
    ```tsx
    const boxA = Bodies.circle(400, 300, 80, {
          render: {
          fillStyle: '#F35e66',
          strokeStyle: 'black',
          lineWidth: 1,
          }, 
    });
    ```
    
- In our case we wanted to render our own custom pills. We exported the pill assets and defined it in a json file as shown
```jsx
const pillData: {
	name : string;
url : string;
height : number;
width : number;
}
``` 

### Render the pills

 To render the pills paste the following code.

```tsx
const renderPills = pillData.map((pill) => {
      return Bodies.rectangle(
                Math.random() * window.innerWidth,
                50,
                pill.width,
                pill.height,
                {
                    render: {
                        fillStyle: "transparent",
                        sprite: {
                            xScale: 1,
                            yScale: 1,
                            texture: pill.url,
                        },
                    },
                }
            );
        });
```

Inside the render option you can add a method called as sprite, where you can add the texture of your asset which we were storing as url.

After the pills are rendered via the engine we need a way to show it to the canvas. For that we need to add all the things to the Composite Module.

```tsx
Composite.add(engine.world, [
		...renderPills,
]);
```

After this run the renderer by calling the Renderer method

```tsx
Runner.run(runner, engine);
```

### Add Boundary To The Section

All the pills should now be rendered but there is a problem left. There is no boundary so the pills are not bounded in a region and they fall indefinitely.

To add boundaries we will create 4 bodies which will surround our canvas: Ground, roof, wallLeft & wallRight

```tsx
var ground = Bodies.rectangle(
            window.innerWidth / 2,
            window.innerHeight / 3,
            window.innerWidth + 320,
            60,
            { render: { fillStyle: "transparent" }, isStatic: true }
        );
var wallLeft = Bodies.rectangle(
            -80,
            window.innerHeight / 2,
            160,
            window.innerHeight,
            { render: { fillStyle: "transparent" }, isStatic: true }
        );
var wallRight = Bodies.rectangle(
            window.innerWidth + 80,
            window.innerHeight / 2,
            160,
            1200,
            { render: { fillStyle: "transparent" }, isStatic: true }
        );
var roof = Bodies.rectangle(
            window.innerWidth / 2 + 160,
            -80,
            window.innerWidth + 320,
            160,
            { render: { fillStyle: "transparent" }, isStatic: true }
        );
```

Make sure to add `isStatic:true` otherwise the walls will also move.

We also have to add all these to the composite.

```tsx
// add all of the bodies to the world
		Composite.add(engine.world, [
            ...renderPills,
            ground,
            wallLeft,
            wallRight,
            roof,
		]);
```

All the pills are falling from top and are bounded by walls on 4 sides. But it’s not interactive right now.

### Add Interactivity

The last step is to add the interactivity. For that we need 2 modules from Matter

- `Matter.Mouse`
- `Matter.MouseConstraint`

```tsx
let mouse = Mouse.create(render.canvas);

        let mouseConstraint = MouseConstraint.create(engine, {
            mouse: mouse,
            constraint: {
                stiffness: 0.2,
                render: {
                    visible: false,
                },
            },
        });
	
//removing the mousewheel event to stop canvas from scrolling

//(works exactly like CSS propery: overflow-hidden)
        mouseConstraint.mouse.element.removeEventListener(
            "mousewheel",
            mouseConstraint.mouse.mousewheel
        );
        mouseConstraint.mouse.element.removeEventListener(
            "DOMMouseScroll",
            mouseConstraint.mouse.mousewheel
        );
//finally adding the mouse to the composite

Composite.add(engine.world, mouseConstraint);
```

That’s it! Your interactive pill animation is ready.

![GIF of Interactive Pill Animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwvjxwauqj4vbep1xbnp.gif)


Integrate this animation into your next website and take the user experience to a next level.

Leave a like if you find this valuable.

A blog by **Team createxp**

<aside>
 👉 Want help in developing high-performing websites, connect with us through our website **[www.createxp.in](http://www.createxp.in/)**

</aside>