Matter.JS elements crash endlessly after page reload

75 views Asked by At

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

0

There are 0 answers