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.
Let's dive deep into the technicalities behind making this draggable pill-interaction animation using Matter.js and Next.js
Breaking Down The Animation
- Pills dropping from above when the section comes into view.
- 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();
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",
},
});
💡 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; }
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 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,
]);
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.
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,
]);
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);
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
Top comments (1)
github help