I'm building a texture out of text in Three.JS. This requires drawing text onto a 2D canvas and then copying it into the texture, placing it in the scene, potentially displacing it or changing its size.

Drawing to a 2D Canvas

I think drawing to a canvas that has an absolute position in the DOM would be the easiest way to figure out what is generated on the canvas before pasting it onto the scene. Unfortunately, I'm lazy and haven't done this yet. I set it to scene.background from its texture form as I go along, which works but not very well.

Moving on...

I've chosen to use Sprites which is a plane (or image) that always face the camera when they're placed in Three.JS.

Creating a canvas

const canvas = document.createElement('canvas') // this line creates the canvas using the DOM's method, but does not add it to the scene
const context = canvas.getContext('2d') // then we say what type it is. It's going to be a flat picture, so 2d. We assign it to context to modify its values

Making the canvas have the same size as the text

By default, a canvas is 300 width x 150 height.

If we put in text, it may or may not exceed the side of these dimensions. If it is under, when we apply the texture to our sprite, it will be left-adjusted due to the extra space that will be on the right. If it is over, the sprite will be cut off.

context is referencing the canvas's 2d space and can measure the supposed size text will take up with .measureText('text to measure'). It will return an object with a key width with a value of a number.

So here, we can take the font, as a string like '120px Arial' and do:

const font = fontsize + 'px ' + fontface;
context.font = font;
const width = context.measureText(message).width;
canvas.width = width
canvas.height = fontsize + 40
context.font = font;

That last line where we set the font again is necessary because the context gets reset when the canvas size gets changed! Dumb.

Now all we need to do is:

  • set what color to fill the text with,
  • write the text
  • create a texture out of the text
  • set needsUpdate to true so the texture renders. Not sure why.
  • map the texture to a new sprite
  • set the sprite where you want it (do this before scaling)
  • scale the sprite to the size you want
context.fillStyle = "rgba(225, 225, 225, 1.0)"; // font color
context.fillText(message, 0, fontsize - 20);
// canvas contents will be used for a texture
const texture = new THREE.Texture(canvas)
texture.needsUpdate = true;
const spriteMaterial = new THREE.SpriteMaterial(
  { map: texture });
const sprite = new THREE.Sprite(spriteMaterial);
sprite.position.set(0,0,10)
sprite.scale.set(10, 5, 1);

Footnote:

I'm making a note of all the methods I use in this little blurb to see how useful this is. After the colon on a method is its return value if I know it.

Methods useddocument.createElement('canvas') : canvasElement

canvas.getContext('2d') : CanvasRenderingContext2D

canvas.width

canvas.height

context.measureText('asdfdsfs').width : number

context.text

context.fillStyle

context.fillText

texture.needsUpdate

sprite.position.set()

sprite.scale.set()