Skip to main content

Command Palette

Search for a command to run...

Create Interactive Pill Animation Using Matter.js

A Guide To Building Awwwards Worthy Website Animations

Updated
β€’5 min read
Create Interactive Pill Animation Using Matter.js
C

A team of designers & developers that love to create digital experiences that make you go 'wow'!

S

Building createxp

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

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
πŸ‘‰
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>();

Create a wrapper for the scene

<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

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.

// create an engine
let engine = Engine.create();
πŸ‘‰
Render 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",
            },
});

In Many iOS device rendered matter bodies are pixelated. 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 pixelation 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 this 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 all the pill assets and defined it in a json file as shown

      const pillData:{
          name: string;
          url: string;
          height: number;
          width: number;
      }[]
    
πŸ’‘
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,
                        },
                    },
                }
            );
        });

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.

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

After this run the renderer by calling the Renderer method

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

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.

πŸ’‘
Add Interactivity

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

  • 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);

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

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