# Create Interactive Pill Animation Using Matter.js

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 maximizing user delight and creating a visually stunning digital experience. Among the many animations, our team's favorite was the Interactive Pill Animation.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1706964294731/14f2135f-d671-416a-8098-0212e6550fe5.gif align="center")

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/)

<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">Start by adding Matter.JS</div>
</div>

```typescript
// yarn

yarn add matter-js

//npm

npm i matter-js
```

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

```typescript
// yarn

yarn add -D @types/matter-js

//npm

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

<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">Create a scene</div>
</div>

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'.

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

Create a wrapper for the scene

```javascript
<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

```typescript
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.

<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">Activating Matter.js Engine</div>
</div>

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.

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

<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">Render Objects</div>
</div>

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.

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

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

In Many iOS device rendered matter bodies are pixelated. To fix that, Matter.js lets us set the PixelRatio.

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

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

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Create Sprites</div>
</div>

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 this [list](https://brm.io/matter-js/docs/classes/Bodies.html)
    
* ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1706965920888/b4f2bf7c-a3f3-4a32-97ae-eda5b2971bec.png align="left")
    
* Each body has 4 parameters
    
    `Matter.Bodies.circle(x, y, radius, [options], [maxSides]) → [Body]`
    
* Each body has option in which you can select the fillStyle, strokeStyles and other properties
    
    ```typescript
    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 all the pill assets and defined it in a json file as shown
    
    ```typescript
    const pillData:{
        name: string;
        url: string;
        height: number;
        width: number;
    }[]
    ```
    

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Render the pills</div>
</div>

To render the pills, paste the following code.

```typescript
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 stored 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.**

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

After this run the renderer by calling the **Renderer method**

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

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Add boundary to the section</div>
</div>

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

```typescript
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.

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

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Add Interactivity</div>
</div>

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

* `Matter.Mouse`
    
* `Matter.MouseConstraint`
    

```typescript
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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1706976049674/7f6bb60e-dfeb-4d1e-b911-1cc318b3db28.gif align="center")

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**

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Want help in developing high-performing websites, connect with us through our website 👉 <a target="_blank" rel="noopener noreferrer nofollow" href="http://www.createxp.in" style="pointer-events: none">www.createxp.in</a></div>
</div>
