I'm using matter.js in a Next.js project. And I need help. When my Technology block, which is reused on different pages of the application, is located at the top of the page when loading the page, the blocks fall normally into the canvas. But if this block is located below and is visible at least half on the screen, the blocks fall endlessly and my laptop takes off like a jet plane.
There is a peculiarity of use: I move Dom elements according to Bodies coordinates.
The same thing happens if I switch pages. I get the impression that Bodies memorize their last location and after reloading the page continue to fall from their last location without looking at the ground. I've tried clearing all refs completely and resetting them to zero, but it doesn't help. What am I missing?
const { useRef, useEffect } = React
.technologies {
display: block;
padding-top: 100px;
// max-height: 742px;
overflow: hidden;
background-color: var(--transparent-bg);
}
.technologies_wrapper {
display: flex;
flex-direction: column;
// justify-content: flex-end;
}
.technologies_listContainer {
position: relative;
width: 100%;
height: 542px;;
// background-color: green;
overflow: hidden;
}
.technologies_title {
font-size: 2.9rem;
text-transform: uppercase;
text-align: center;
@media (min-width: 1024px) {
font-size: 2.4rem;
}
@media (min-width: 1920px) {
font-size: 171px;
}
}
.technologies_list {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100px;
background-color: red;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
z-index: 1;
@media (min-width: 768px) {
max-height: 542px;
}
}
.technologies_listItem {
display: inline-flex;
align-items: center;
width: min-content;
height: min-content;
position: absolute;
}
.technologies_listItem_link {
display: block;
white-space: nowrap;
padding: 2.5vw 9.5vw;
border: 1px solid var(--primary-text);
border-radius: 100px;
font-size: 0.9rem;
font-weight: 500;
color: inherit;
font-size: 24px;
padding: 15px 50px;
@media (min-width: 547px) {
font-size: 0.7rem;
padding: 2vw 8.5vw;
}
@media (min-width: 768px) {
font-size: 0.5rem;
padding: 1.5vw 6.5vw;
}
@media (min-width: 1024px) {
font-size: 0.4rem;
padding: 1.2vw 4vw;
}
@media (min-width: 1366px) {
font-size: 0.35rem;
padding: 0.8vw 2.7vw;
}
@media (min-width: 1920px) {
font-size: 24px;
padding: 15px 50px;
}
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
<script type="text/babel" data-presets="es2017,react">
// For component Techologies DATA
const CONFIG = {
title: "technologies",
technologies: {
data: [
{ id: 1, title: "PHP", link: "php" },
{ id: 2, title: "HTML 5", link: "html5" },
{ id: 3, title: "CSS 3", link: "css3" },
],
},
};
// FUNCTION HELPERS
const render = (objects) => {
objects.forEach((item) => {
const selector = item.id.toString();
const element = document.getElementById(selector);
// console.log(element.getBoundingClientRect());
const { x, y } = item.position;
element.style.left = `${x - element.offsetWidth / 2}px`;
element.style.top = `${y - element.offsetHeight / 2}px`;
element.style.transform = `rotate(${item.angle}rad)`;
});
};
const createBody = (itemRef,itemId) => {
const { x, y, width, height } = itemRef.getBoundingClientRect();
const body = Bodies.rectangle(x, y, width, height, {
id: itemId,
chamfer: { radius: 30 },
});
return body;
};
// Aliases
const Bodies = Matter.Bodies;
const Body = Matter.Body;
const Composite = Matter.Composite;
const Engine = Matter.Engine;
const Mouse = Matter.Mouse;
const MouseConstraint = Matter.MouseConstraint;
const Render = Matter.Render;
const Runner = Matter.Runner;
const Vector = Matter.Vector;
// Component
function Technologies ({ data }) {
const { title, technologies } = data;
const containerRef = useRef(null);
const renderRef = useRef(null);
const runnerRef = useRef(null);
const requestRef = useRef(null);
const itemsRef = useRef([]);
const bodiesRefs = useRef([]);
const engineRef = useRef(Engine.create());
const groundRef = useRef(null);
const leftWallRef = useRef(null);
const rightWallRef = useRef(null);
useEffect(() => {
console.log('create render');
if (containerRef.current) {
// aliases
const container = containerRef.current;
const containerWidth = container.offsetWidth;
const containerHeight = container.offsetHeight;
//create Render (generate canvas)
renderRef.current = Render.create({
element: container,
engine: engineRef.current,
options: {
width: containerWidth,
height: containerHeight,
hasBounds: true,
},
});
groundRef.current = Bodies.rectangle(
containerWidth / 2,
containerHeight,
27184,
10,
{
isStatic: true,
id: 1001,
}
);
leftWallRef.current = Bodies.rectangle(
5 + 0.5,
containerHeight - containerHeight / 2,
10,
containerHeight * 5,
{
isStatic: true,
id: 1002,
}
);
rightWallRef.current = Bodies.rectangle(
containerWidth,
containerHeight - containerHeight / 2,
10,
containerHeight * 5,
{ isStatic: true, id: 1003 }
);
// const roof = Bodies.rectangle(containerWidth / 2, 70, 27184, 70, {
// isStatic: true,
// id: 1004,
// });
const mouse = Mouse.create(renderRef.current.canvas);
const mouseConstraint = MouseConstraint.create(engineRef.current, {
element: container,
constraint: {
stiffness: 0.2,
},
});
Composite.add(engineRef.current.world, [
groundRef.current,
leftWallRef.current,
rightWallRef.current,
// roof,
mouseConstraint,
]);
Render.run(renderRef.current);
runnerRef.current = Runner.create();
Runner.run(runnerRef.current, engineRef.current);
}
}, []);
const handleResize = () => {
if (!containerRef.current) return;
// aliases
const render = renderRef.current;
const container = containerRef.current;
const containerWidth = container.offsetWidth;
const containerHeight = container.offsetHeight;
render.bounds.max.x = containerWidth;
render.bounds.max.y = containerHeight;
render.canvas.width = containerWidth;
render.canvas.height = containerHeight;
render.options.width = containerWidth;
render.options.height = containerHeight;
// reposition ground
Body.setPosition(
groundRef.current,
Vector.create(containerWidth / 2, containerHeight)
);
// reposition right wall
Body.setPosition(
rightWallRef.current,
Vector.create(containerWidth, containerHeight / 2)
);
// reposition left wall
Body.setPosition(
leftWallRef.current,
Vector.create(0, containerHeight / 2)
);
const allBodies = Composite.allBodies(engineRef.current.world);
allBodies.forEach((body) => {
if (body.isStatic) return;
const { min, max } = body.bounds;
const domElement = itemsRef.current.find(
({ id }) => body.id === Number(id)
);
if (domElement) {
const { top, left, right, bottom } = domElement.getBoundingClientRect();
const elementWidth = right - left;
const elementHeight = bottom - top;
const bodyWidth = max.x - min.x;
const bodyHeight = max.y - min.y;
const scaleXFactor = elementWidth / bodyWidth / 1.05;
const scaleYFactor = elementHeight / bodyHeight / 1.05;
Body.scale(body, scaleXFactor, scaleYFactor);
}
});
};
useEffect(() => {
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
useEffect(() => {
bodiesRefs.current = technologies.data.map((item, idx) => {
const currentRef = itemsRef.current[idx];
const itemId = item.id;
const body = createBody(currentRef, itemId);
Composite.add(engineRef.current.world, body);
return body;
});
}, []);
const rerender = () => {
render(bodiesRefs.current);
Engine.update(engineRef.current);
requestRef.current = requestAnimationFrame(rerender);
};
useEffect(() => {
rerender();
return () => {
cancelAnimationFrame(requestRef.current);
Engine.clear(engineRef.current);
Runner.stop(runnerRef.current);
Render.stop(renderRef.current);
itemsRef.current.forEach((element) => {
element.removeAttribute("style");
});
bodiesRefs.current = [];
Composite.clear(engineRef.current.world, true);
containerRef.current = null;
};
}, []);
return (
<section className="technologies" data-is-desktop="true">
<div className="technologies_wrapper">
<div className="technologies_listContainer" ref={containerRef}>
<ul className="technologies_list">
{technologies.data.map(({ id, title, link }, idx) => {
const itemLink = `/technologies/${link}`;
const getRef = (element) => {
if (!element) return;
const matches = itemsRef.current.find(
(element) => Number(element.id) === id,
);
if (matches) return;
itemsRef.current.push(element);
};
const topPosition =
idx > 4
? (240 + idx) * -1
: idx > 10
? (340 + idx) * -1
: (150 + idx) * -1;
const leftPosition = idx * 50;
return (
<li
key={id}
ref={getRef}
id={id.toString()}
className="technologies_listItem"
style={{
top: `${topPosition}px`,
left: `${leftPosition}px`,
}}
data-is-rendered="false"
>
<a
href={itemLink}
className="technologies_listItem_link"
>
{title}
</a>
</li>
);
})}
</ul>
</div>
<h2 className="technologies_title">{title}</h2>
</div>
</section>
);
};
// App.jsx
function App() {
return(
<>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Consectetur
adipiscing elit pellentesque habitant morbi tristique senectus et.
Volutpat sed cras ornare arcu. Interdum velit euismod in pellentesque
massa placerat duis ultricies lacus. Elit duis tristique sollicitudin
nibh. Erat velit scelerisque in dictum non consectetur. Quam viverra
orci sagittis eu volutpat odio. Nec ullamcorper sit amet risus nullam
eget felis eget. Tempus urna et pharetra pharetra massa massa ultricies
mi quis. Volutpat commodo sed egestas egestas fringilla phasellus
faucibus. Mauris in aliquam sem fringilla. At tempor commodo ullamcorper
a lacus. Eget felis eget nunc lobortis mattis aliquam faucibus purus.
Neque egestas congue quisque egestas diam in arcu cursus. Gravida rutrum
quisque non tellus. Tristique senectus et netus et malesuada fames ac
turpis.
</p>
<p>
Tortor at risus viverra adipiscing at in tellus integer. Eget nunc
scelerisque viverra mauris in aliquam sem fringilla. Magna fermentum
iaculis eu non diam phasellus vestibulum. Ut tristique et egestas quis
ipsum suspendisse ultrices gravida. Laoreet id donec ultrices tincidunt
arcu non sodales neque. Augue eget arcu dictum varius duis at
consectetur. Dignissim suspendisse in est ante in nibh. Faucibus turpis
in eu mi bibendum neque. Amet dictum sit amet justo donec enim. Donec
pretium vulputate sapien nec sagittis aliquam malesuada bibendum arcu.
Lorem mollis aliquam ut porttitor leo a diam sollicitudin tempor. Aenean
euismod elementum nisi quis. Et leo duis ut diam. Aenean et tortor at
risus viverra adipiscing at in tellus. Enim sit amet venenatis urna
cursus eget nunc scelerisque viverra. Habitasse platea dictumst
vestibulum rhoncus est pellentesque elit. Duis at tellus at urna
condimentum mattis pellentesque. Sed id semper risus in hendrerit
gravida. Amet est placerat in egestas erat imperdiet sed euismod nisi.
Bibendum arcu vitae elementum curabitur vitae nunc.
</p>
<p>
Neque gravida in fermentum et sollicitudin ac orci. Nulla pharetra diam
sit amet. Rhoncus mattis rhoncus urna neque viverra justo nec ultrices.
Quisque egestas diam in arcu cursus euismod quis viverra. Est lorem
ipsum dolor sit amet consectetur adipiscing elit pellentesque. Varius
sit amet mattis vulputate. Mattis enim ut tellus elementum sagittis
vitae et. Vestibulum morbi blandit cursus risus at ultrices mi tempus
imperdiet. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames. Platea dictumst vestibulum rhoncus est pellentesque
elit. Tempor id eu nisl nunc mi ipsum. Dolor sed viverra ipsum nunc
aliquet bibendum enim facilisis gravida. Accumsan tortor posuere ac ut
consequat. Tristique nulla aliquet enim tortor at. Tempor id eu nisl
nunc mi ipsum faucibus vitae aliquet. Ut diam quam nulla porttitor massa
id neque aliquam vestibulum. Vel fringilla est ullamcorper eget nulla
facilisi etiam.{" "}
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Consectetur
adipiscing elit pellentesque habitant morbi tristique senectus et.
Volutpat sed cras ornare arcu. Interdum velit euismod in pellentesque
massa placerat duis ultricies lacus. Elit duis tristique sollicitudin
nibh. Erat velit scelerisque in dictum non consectetur. Quam viverra
orci sagittis eu volutpat odio. Nec ullamcorper sit amet risus nullam
eget felis eget. Tempus urna et pharetra pharetra massa massa ultricies
mi quis. Volutpat commodo sed egestas egestas fringilla phasellus
faucibus. Mauris in aliquam sem fringilla. At tempor commodo ullamcorper
a lacus. Eget felis eget nunc lobortis mattis aliquam faucibus purus.
Neque egestas congue quisque egestas diam in arcu cursus. Gravida rutrum
quisque non tellus. Tristique senectus et netus et malesuada fames ac
turpis.
</p>
<p>
Tortor at risus viverra adipiscing at in tellus integer. Eget nunc
scelerisque viverra mauris in aliquam sem fringilla. Magna fermentum
iaculis eu non diam phasellus vestibulum. Ut tristique et egestas quis
ipsum suspendisse ultrices gravida. Laoreet id donec ultrices tincidunt
arcu non sodales neque. Augue eget arcu dictum varius duis at
consectetur. Dignissim suspendisse in est ante in nibh. Faucibus turpis
in eu mi bibendum neque. Amet dictum sit amet justo donec enim. Donec
pretium vulputate sapien nec sagittis aliquam malesuada bibendum arcu.
Lorem mollis aliquam ut porttitor leo a diam sollicitudin tempor. Aenean
euismod elementum nisi quis. Et leo duis ut diam. Aenean et tortor at
risus viverra adipiscing at in tellus. Enim sit amet venenatis urna
cursus eget nunc scelerisque viverra. Habitasse platea dictumst
vestibulum rhoncus est pellentesque elit. Duis at tellus at urna
condimentum mattis pellentesque. Sed id semper risus in hendrerit
gravida. Amet est placerat in egestas erat imperdiet sed euismod nisi.
Bibendum arcu vitae elementum curabitur vitae nunc.
</p>
<p>
Neque gravida in fermentum et sollicitudin ac orci. Nulla pharetra diam
sit amet. Rhoncus mattis rhoncus urna neque viverra justo nec ultrices.
Quisque egestas diam in arcu cursus euismod quis viverra. Est lorem
ipsum dolor sit amet consectetur adipiscing elit pellentesque. Varius
sit amet mattis vulputate. Mattis enim ut tellus elementum sagittis
vitae et. Vestibulum morbi blandit cursus risus at ultrices mi tempus
imperdiet. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames. Platea dictumst vestibulum rhoncus est pellentesque
elit. Tempor id eu nisl nunc mi ipsum. Dolor sed viverra ipsum nunc
aliquet bibendum enim facilisis gravida. Accumsan tortor posuere ac ut
consequat. Tristique nulla aliquet enim tortor at. Tempor id eu nisl
nunc mi ipsum faucibus vitae aliquet. Ut diam quam nulla porttitor massa
id neque aliquam vestibulum. Vel fringilla est ullamcorper eget nulla
facilisi etiam.{" "}
</p>
<Technologies data={CONFIG} />
</>
);
};
// Render it
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.23.1/babel.min.js"></script>
How to fix this behavior? I've also tried correcting this behavior by running an animation when the Technology block is in the viewport. But no, it didn't help. This behavior persists. Only the block at the top of the page draws the fall normally, see codesandbox