Matter-js: Is there anyway to render text in Matter.js?

Created on 28 Nov 2016  路  9Comments  路  Source: liabru/matter-js

question

Most helpful comment

If you're wondering how to implement this, here's a quick example to get you off the ground. Make your own custom copy of MatterRenderer (as liabru stated). Then in the render.bodies function there is a loop that is responsible for rendering each individual body (should be around line 616) include the following at the end immediately after c.globalAlpha = 1; (around line 706)

c.globalAlpha=1;
//Here's the custom part
if(part.render.text)
{
    //30px is default font size
    var fontsize = 30;
    //arial is default font family
    var fontfamily = part.render.text.family || "Arial"; 
    //white text color by default
    var color = part.render.text.color || "#FFFFFF";

    if(part.render.text.size)
        fontsize = part.render.text.size;
    else if(part.circleRadius)
        fontsize = part.circleRadius/2;

    var content = "";
    if(typeof part.render.text == "string")
        content = part.render.text;
    else if(part.render.text.content)
        content = part.render.text.content;

    c.textBaseline="middle";
    c.textAlign="center";
    c.fillStyle=color;
    c.font = fontsize+'px '+fontfamily;
    c.fillText(content,part.position.x,part.position.y);
}

Then in your code instead of importing Matter.Renderer import your custom renderer, and use as follows :

//an example using a circle
Bodies.circle(x,y,circleSize,{
    restitution:0.95,
    friction:0.05,
    density:0.0005,
    render:{
        fillStyle:"#C44D58",
        text:{
            content:"Test",
            color:"blue",
            size:16,
            family:"Papyrus",
        },
    },
});

Or you can just set render.text to a string and the rest will be set to the defaults we defined in the custom renderer.

This is an overly simplified solution and the following issues still exist

  • There is no wrapping or automatic sizing of text - we can't measure text so we can't guarantee it won't overflow.
  • For similar reasons we can't be sure the text will be centered perfectly.

All 9 comments

Not with the built in debug renderer, if you need to do anything fancy I'd suggest creating your own, see Rendering. A place to start is to copy the code of Matter.Render.

@KennethCooney Do you fix this problem?

If you're just looking for a way to render a simple UI or instructions you could also just layer another canvas element on top of the canvas the Matter.Render is using and draw text on that. I'm using typescript and wasn't really keen on the idea making my own render class. I get it's somewhat of a hacky work around but this was a simple enough solution and accomplished what I need.

I stumbled upon this question/answer multiple times while searching google for an easy solution. Maybe this will help someone else in the same situation...

If you're wondering how to implement this, here's a quick example to get you off the ground. Make your own custom copy of MatterRenderer (as liabru stated). Then in the render.bodies function there is a loop that is responsible for rendering each individual body (should be around line 616) include the following at the end immediately after c.globalAlpha = 1; (around line 706)

c.globalAlpha=1;
//Here's the custom part
if(part.render.text)
{
    //30px is default font size
    var fontsize = 30;
    //arial is default font family
    var fontfamily = part.render.text.family || "Arial"; 
    //white text color by default
    var color = part.render.text.color || "#FFFFFF";

    if(part.render.text.size)
        fontsize = part.render.text.size;
    else if(part.circleRadius)
        fontsize = part.circleRadius/2;

    var content = "";
    if(typeof part.render.text == "string")
        content = part.render.text;
    else if(part.render.text.content)
        content = part.render.text.content;

    c.textBaseline="middle";
    c.textAlign="center";
    c.fillStyle=color;
    c.font = fontsize+'px '+fontfamily;
    c.fillText(content,part.position.x,part.position.y);
}

Then in your code instead of importing Matter.Renderer import your custom renderer, and use as follows :

//an example using a circle
Bodies.circle(x,y,circleSize,{
    restitution:0.95,
    friction:0.05,
    density:0.0005,
    render:{
        fillStyle:"#C44D58",
        text:{
            content:"Test",
            color:"blue",
            size:16,
            family:"Papyrus",
        },
    },
});

Or you can just set render.text to a string and the rest will be set to the defaults we defined in the custom renderer.

This is an overly simplified solution and the following issues still exist

  • There is no wrapping or automatic sizing of text - we can't measure text so we can't guarantee it won't overflow.
  • For similar reasons we can't be sure the text will be centered perfectly.

@antontsvil Thanks for your idea, it works for me. But I wonder if matter.js should provide a Render Middleware Mechanism(like middleware in koa or redux), which programer can acquire ctx and perform their own render logic.

@lcx-seima you can use the beforeRender and afterRender events on the renderer, you can get the context at any time from render.canvas or just use render.context. You could also extend the renderer using your own plugin. Generally though the built in renderer is only intended for debugging so it is advised to write your own renderer for anything more complex.

I want to create an animation of word tiles that behave like solid bodies. So move around bouncing off each other. But I need to be able to easily populate the tiles with different strings. Would like to use matter.js to manage the movement. Is this possible?

You could just set each letter as a texture (pngs), and use transparent rectangles as "hit boxes" around each letter. seems overkill to expect this library to have a body.text engine, when fonts can be so weird and unpredictable.

I might be late to the party, but if your project is using P5.js, this is an easy solution to render text while using Matter.js.

Based on P5-matter of @shiffman AKA CodingTrain I made a quick demo.

Codepen Demo

馃拵 Tip: The text is rendered in a same-sized invisible rectangle, this rectangle is recreated at Bodies.rectangle(x, y, 90, 60, params); So if you change the text, the width of Bodies.rectangle needs to be updated too. I can imagine if you have multiple strings of text from an array, this being a dynamic value calculated on the width of the text being passed in makeCircle(). This way you can have multiple words floating around without overlap.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cluber22 picture cluber22  路  3Comments

253153 picture 253153  路  3Comments

Zhaopengyang picture Zhaopengyang  路  3Comments

liabru picture liabru  路  3Comments

christianmalek picture christianmalek  路  4Comments