Search code examples
javascriptsvgfont-awesomematter.js

How to use an SVG image in Matter.js?


What I'm trying to do: Use a Font Awesome SVG - sleigh - in matter.js.

I've tried this:

Matter.Bodies.fromVertices(500, 50 , Matter.Svg.pathToVertices("sleigh.svg"))

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M612.7 350.7l-9.3-7.4c-6.9-5.5-17-4.4-22.5 2.5l-10 12.5c-5.5 6.9-4.4 17 2.5 22.5l9.3 7.4c5.9 4.7 9.2 11.7 9.2 19.2 0 13.6-11 24.6-24.6 24.6H48c-8.8 0-16 7.2-16 16v16c0 8.8 7.2 16 16 16h516c39 0 73.7-29.3 75.9-68.3 1.4-23.8-8.7-46.3-27.2-61zM32 224c0 59.6 40.9 109.2 96 123.5V400h64v-48h192v48h64v-48c53 0 96-43 96-96v-96c17.7 0 32-14.3 32-32s-14.3-32-32-32h-96v64c0 35.3-28.7 64-64 64h-20.7c-65.8 0-125.9-37.2-155.3-96-29.4-58.8-89.6-96-155.3-96H32C14.3 32 0 46.3 0 64s14.3 32 32 32v128z"/></svg>

But got:

matter.js:4624 matter-js: Svg.pathToVertices: SVGPathSeg not defined, a polyfill is required.

matter.js:7554 Uncaught TypeError: Cannot read property 'numberOfItems' of undefined

What am I doing wrong?

(P. S: I'm pretty new to matter.js, so sorry if this is a silly question…)


Solution

  • Getting this working can be a bit of a fuss because, as the docs point out, there are a couple of external scripts necessary to polyfill SVGPathSeg and to decompose a 2D polygon into convex pieces. Version frustrations ensue.

    The versions that worked for me after referencing the official example and this outdated Codepen example are shown below in the snippet.

    Worth noting, the Matter.Bodies.fromVertices docs state: "if the vertices are concave, they will be decomposed if poly-decomp.js is available. Note that this process is not guaranteed to support complex sets of vertices (e.g. those with holes may fail)" which probably accounts for the extra line you see in the rendered body.

    Furthermore, MJS' built-in renderer is mainly for prototyping purposes, so the fact that there's an extra line on the body likely won't matter if you're animating the original SVG element on the page. You may also try the premium font-awesome filled sleighs, but I don't have access to them.

    const engine = Matter.Engine.create();
    const runner = Matter.Runner.create();
    const render = Matter.Render.create({
      element: document.body,
      engine: engine
    });
    const bodies = [
      Matter.Bodies.rectangle(400, 210, 810, 60, {isStatic: true}),
      ...[...document.querySelectorAll("svg path")].map(path => {
        const body = Matter.Bodies.fromVertices(
          100, 80, Matter.Svg.pathToVertices(path), {}, true
        );
        Matter.Body.scale(body, 0.2, 0.2);
        return body;
      })
    ];
    Matter.Composite.add(engine.world, bodies);
    Matter.Runner.run(runner, engine);
    Matter.Render.run(render);
    svg {display: none;}
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/pathseg.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/decomp.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.js"></script>
    <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="sleigh" class="svg-inline--fa fa-sleigh fa-w-20" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M612.7 350.7l-9.3-7.4c-6.9-5.5-17-4.4-22.5 2.5l-10 12.5c-5.5 6.9-4.4 17 2.5 22.5l9.3 7.4c5.9 4.7 9.2 11.7 9.2 19.2 0 13.6-11 24.6-24.6 24.6H48c-8.8 0-16 7.2-16 16v16c0 8.8 7.2 16 16 16h516c39 0 73.7-29.3 75.9-68.3 1.4-23.8-8.7-46.3-27.2-61zM32 224c0 59.6 40.9 109.2 96 123.5V400h64v-48h192v48h64v-48c53 0 96-43 96-96v-96c17.7 0 32-14.3 32-32s-14.3-32-32-32h-96v64c0 35.3-28.7 64-64 64h-20.7c-65.8 0-125.9-37.2-155.3-96-29.4-58.8-89.6-96-155.3-96H32C14.3 32 0 46.3 0 64s14.3 32 32 32v128z"></path></svg>

    To reproduce your error, comment out

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/pathseg.js"></script>