liabru / matter-js

a 2D rigid body physics engine for the web ▲● ■
MIT License
16.72k stars 1.96k forks source link

Rendering lags on safari / iphone. #1304

Closed nancy-luu closed 2 months ago

nancy-luu commented 3 months ago

Hi community - I am currently rendering a collection of bodies falling upon page load on my landing page. The animation / rendering works as intended when viewing on my laptop on both safari and chrome.

For some reason when I look at the website on my iphone (iphone 14 pro) using safari the falling effect of the bodies is incredibly laggy.

Is there a way I can improve this performance?

For additional context (not sure if this affects my issue) - I am using react.js / next.js and deployed on vercel. And this is my code below:

`import { useEffect, useRef, useState } from 'react'; import Matter from 'matter-js';

import styles from '@/styles/Home.module.scss'

const ToyBox = ({ cw, ch }) => { const containerRef = useRef(null); const [shapesAdded, setShapesAdded] = useState(false);

useEffect(() => {
    const { Engine, Render, Runner, Composite, Common, Composites, MouseConstraint, Mouse, Bodies } = Matter;

    // Create engine
    const engine = Engine.create();
    const world = engine.world;

    engine.gravity.y = 3;

    // Create renderer only if shapes are added
    let render = null;

    if (shapesAdded) {
        render = Render.create({
            element: containerRef.current,
            engine: engine,
            options: {
                width: cw,
                height: ch,
                background: 'transparent',
                showAngleIndicator: false,
                wireframes: false,
                pixelRatio: 2, // Increase pixel ratio for higher resolution rendering
                antialias: true 
            }
        });

        Render.run(render);
    }

    // Create runner
    const runner = Runner.create();
    Runner.run(runner, engine);

    // Add bodies
    const offset = 10;
    const options = {
        isStatic: true,
        render: {
            fillStyle: 'transparent'
        }
    };

    world.bodies = [];

    Composite.add(world, [
        Bodies.rectangle(400, -offset, 800.5 + 2 * offset, 50.5, options),
        Bodies.rectangle(400, 600 + offset, 800.5 + 2 * offset, 50.5, options),
        Bodies.rectangle(800 + offset, 300, 50.5, 600.5 + 2 * offset, options),
        Bodies.rectangle(-offset, 300, 50.5, 600.5 + 2 * offset, options)
    ]);

    const shapesConfig = [
        { type: 'square', color: '#FFBC00', count: 1 },
        { type: 'rectangle', color: '#2B67F2', count: 2 },
        { type: 'rectangleTall', color: '#1DA554', count: 2 },
        { type: 'triangle', color: '#FC553C', count: 2 },
        { type: 'circle', color: '#9746FF', count: 1 },
    ];

    shapesConfig.forEach(({ type, color, count }) => {
        for (let i = 0; i < count; i++) {
            let body;
            switch (type) {
                case 'square':
                    body = Bodies.rectangle(150 * i + 100, 50, 140, 140, {
                        render: {
                            fillStyle: color,
                            strokeStyle: '#000000',
                            lineWidth: 5  
                        }
                    });
                    break;
                case 'rectangle':
                    body = Bodies.rectangle(150 * i + 100, 150, 200, 70, {
                        render: {
                            fillStyle: color,
                            strokeStyle: '#000000',
                            lineWidth: 5  
                        }
                    });
                    break;
                    case 'rectangleTall':
                        body = Bodies.rectangle(150 * i + 100, 250, 70, 200, {
                            render: {
                                fillStyle: color,
                                strokeStyle: '#000000',
                                lineWidth: 5  
                            }
                        });
                        break;
                    case 'triangle':
                        body = Bodies.polygon(150 * i + 100, 350, 3, 70, {
                            render: {
                                fillStyle: color,
                                strokeStyle: '#000000',
                                lineWidth: 5  
                            }
                        });
                        break;
                    case 'circle':
                        body = Bodies.circle(150 * i + 100, 450, 70, {
                            render: {
                                fillStyle: color,
                                strokeStyle: '#000000',
                                lineWidth: 5  
                            }
                        });
                        break;
                default:
                    break;
            }
            Composite.add(world, body);
        }
    });

    setShapesAdded(true);

    // Add mouse control
    if (render) {
        const mouse = Mouse.create(render.canvas);
        const mouseConstraint = MouseConstraint.create(engine, {
            mouse: mouse,
            constraint: {
                stiffness: 0.3,
                render: {
                    visible: false
                }
            }
        });

        Composite.add(world, mouseConstraint);
        render.mouse = mouse;
    }

    // Fit the render viewport to the scene
    if (render) {
        Render.lookAt(render, {
            min: { x: 0, y: 0 },
            max: { x: 800, y: 600 }
        });
    }

    // Cleanup function
    return () => {
        render && Render.stop(render);
        Runner.stop(runner);
    };
}, [shapesAdded, ch, cw]);

return (
    <div className={styles["toy-box"] }ref={containerRef}></div>
);

};

export default ToyBox; `

liabru commented 3 months ago

Thanks for sharing.

Could you provide a few more details on this?

nancy-luu commented 2 months ago

@liabru thanks for following up!

Could the slow first initial load / rendering / animation be related to the website being deployed on vercel? Thank you for any suggestions you can provide!

nancy-luu commented 2 months ago

@liabru

Was able to find a solution for this!

I had a lot of large image files in the index page of my site that seemed to have been contributing to the overall lag. I simply offloaded image hosting to a CDN that provided image optimization to reduce the bandwidth and server load on my hosting environment. This seemed to have freed up resources for matter.js to render smoother.