Why does VexFlow seem to render nothing in my Svelte app?

51 Views Asked by At

I added vexflow to my package.json added the Github example code to my Svelte component.

I figured out that I need to check if (browser) so that it doesn't attempt to render server-side. Without this it errored with ReferenceError: document is not defined at Factory.initRenderer.

Now, past that point, I see no errors, but there is still no music showing. The output div shows nothing in it: nothing visible, no DOM elements. Yet I can see with the console.log that the code is being run and that Vex Factory, EasyScore, System are defined.

What am I missing?

<script>
  import Vex from 'vexflow';
  import { browser } from '$app/environment';

  const { Factory, EasyScore, System } = Vex.Flow;

  if (browser) {
    const vf = new Factory({
      renderer: { elementId: 'output', width: 500, height: 200 },
    });

    const score = vf.EasyScore();
    const system = vf.System();

    system
      .addStave({
        voices: [
          score.voice(score.notes('C#5/q, B4, A4, G#4', { stem: 'up' })),
          score.voice(score.notes('C#4/h, C#4', { stem: 'down' })),
        ],
      })
      .addClef('treble')
      .addTimeSignature('4/4');

    vf.draw();
    console.log(Factory, EasyScore, System);
  }
</script>

<div id="output"></div>
1

There are 1 best solutions below

0
On

Found my answer! The following console logs were instructive:

    console.log('script')
    console.log(document.getElementById('output').innerHTML)
  }
</script>
<div id="output">{console.log('render')}</div>

They reveal that the following things are happening:

  • Server first renders the div. I see render logged in the server-side output.
  • Browser runs the script. I see script logged in the browser console.
  • The "output" element logged as containing an <svg> tag which was created by Vexflow.
  • Browser renders the div again. This overwrites and replaces it and so the Vexflow content is destroyed!

To deal around this, I wrapped the script in a setTimeout. With that, I saw the music rendered.

<script>
  import Vex from 'vexflow';
  import { browser } from '$app/environment';

  const { Factory, EasyScore, System } = Vex.Flow;

  if (browser) {
    setTimeout(() => {
      const vf = new Factory({
        renderer: { elementId: 'output', width: 500, height: 200 },
      });

      const score = vf.EasyScore();
      const system = vf.System();

      system
        .addStave({
          voices: [
            score.voice(score.notes('C#5/q, B4, A4, G#4', { stem: 'up' })),
            score.voice(score.notes('C#4/h, C#4', { stem: 'down' })),
          ],
        })
      .addClef('treble')
      .addTimeSignature('4/4');

      vf.draw();
    });
  }
</script>

<div id="output"></div>