Animating three.js font from fontLoader()

Viewed 37

I caught a bug when trying to deploy my site and the three.js canvas would not load (for some reason worked on dev but that's neither here nor there)

The problem is that I am trying to animate text that was loaded using fontLoader.load(). For example, I add text to the scene using the following:

fontLoader.load(
  'node_modules/three/examples/fonts/droid/droid_serif_regular.typeface.json',
  (droidFont) => {
    const textGeometry = new TextGeometry('Scroll to Start', {
      size: 5,
      height: 1,
      font: droidFont,
      bevelSize: 5,
      bevelThickness: 2,
    
    });
    const introTexture = new THREE.TextureLoader().load('suntexture.png');
    const textMaterial = new THREE.MeshBasicMaterial({map: introTexture, transparent:true, opacity: .5});
    cosnt introText = new THREE.Mesh(textGeometry, textMaterial);
    introText.position.set(-5, 37, -140);
    scene.add(introText);
  }
);

Then, I want it to gently oscillate on the screen so as to not appear static, to do this I would include something like this in my animation function (called at the end of main.js):

function introAnimate() {
    introText.position.y += (Math.sin(clock.getElapsedTime())/62.8);
    introText.rotation.y += (Math.cos(clock.getElapsedTime())/700);
}

The problem with this is that the console (on dev/preview) says that introText is not defined, I'm assuming because it was declared in a function. I tried to fix this by first declaring them as var or const (didn't work), then adding globalThis. or window. (ie window.introText). But the problem persists.

To be honest, I am surprised the npm run dev version ran correctly in the first place given this reference error.

I have seen some versions of text animation using three.js flow, but I am interested in triggering certain animations on scroll, and rotating/changing other properties that I don't think flow can do. Any suggestions on how to address this would be much appreciated.

1 Answers

I think your problem is that you're running introAnimate() before your font finishes loading.

fontLoader.load() runs asynchronously, meaning it starts loading, and the rest of your code (introAnimate() for instance) keeps running until your font finally loads at which point it calls the callback.

Locally on in your dev environment the font might load instantly, but in production the font takes relatively longer time to download.

And until then, if introAnimate() tries to run, it'll fail because the callback to declare the window.introText was not called yet.

The solution would be to declare window.introText = null before calling fontLoader.load(), overwrite it once it's initialized, and in your introAnimate() check if introText is not null before doing anything:

function introAnimate() {
    if (introText === null) return;

    introText.position.y += (Math.sin(clock.getElapsedTime())/62.8);
    introText.rotation.y += (Math.cos(clock.getElapsedTime())/700);
}
Related