DEV Community

Cover image for Create Interactive Pill Animation Using Matter.js
createxp HQ for createxp

Posted on

Create Interactive Pill Animation Using Matter.js

Recently, our team at createxp 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

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

Start by adding Matter.JS

// yarn

yarn add matter-js

//npm

npm i matter-js
Enter fullscreen mode Exit fullscreen mode

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

// yarn

yarn add -D @types/matter-js

//npm

npm i -D @types/matter-js
Enter fullscreen mode Exit fullscreen mode

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

const matterRef = React.createRef<HTMLDivElement>();
Enter fullscreen mode Exit fullscreen mode

Create a wrapper for the scene

<div ref={matterRef}></div>
Enter fullscreen mode Exit fullscreen mode

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

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;
Enter fullscreen mode Exit fullscreen mode

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.

// create an engine
let engine = Engine.create();
Enter fullscreen mode Exit fullscreen mode

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.

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

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

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

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.

  • 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

    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

const pillData: {
    name : string;
url : string;
height : number;
width : number;
}
Enter fullscreen mode Exit fullscreen mode

Render the pills

To render the pills paste the following code.

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,
                        },
                    },
                }
            );
        });
Enter fullscreen mode Exit fullscreen mode

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.

Composite.add(engine.world, [
        ...renderPills,
]);
Enter fullscreen mode Exit fullscreen mode

After this run the renderer by calling the Renderer method

Runner.run(runner, engine);
Enter fullscreen mode Exit fullscreen mode

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

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 }
        );
Enter fullscreen mode Exit fullscreen mode

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

We also have to add all these to the composite.

// add all of the bodies to the world
        Composite.add(engine.world, [
            ...renderPills,
            ground,
            wallLeft,
            wallRight,
            roof,
        ]);
Enter fullscreen mode Exit fullscreen mode

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
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);
Enter fullscreen mode Exit fullscreen mode

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

GIF of Interactive Pill Animation

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

👉 Want help in developing high-performing websites, connect with us through our website www.createxp.in

Top comments (0)