0xfe / vexflow

A JavaScript library for rendering music notation and guitar tablature.
http://www.vexflow.com
Other
3.81k stars 657 forks source link

Use with React / React-native? #338

Closed davidfloegel closed 4 years ago

davidfloegel commented 8 years ago

Hi there,

Is it possible to use vexflow with react (react-native) at all? I've used this library before for a phonegap based app but switching to react and would love to keep using this one!

Cheers david

mturley commented 8 years ago

+1! Would be very nice. Was about to look into this myself.

marcoalkema commented 8 years ago

Doesn't React also use a canvas? I've been using VexFlow with Pux, which is an interface to React for the language Purescript and I'm drawing my notation on a Canvas through that. Unless I'm not fully comprehending the workings behind Pux, I assume it should be possible.

davidfloegel commented 8 years ago

@marcoalkema Using the canvas is not the issue - it's more including the library and rendering things.. Do you have an example of how you're doing it?

panarch commented 8 years ago

If you use react-dom, there's no problem at all to use vexflow. But if you consider react-native, it's totally different. Currently it is not possible because of a single issue which is about calculating text size.(only this is the thing, if vexflow removes this dom dependency, you can generate svg file and it means you can use vexflow with react-native directly)

If there's no other volunteers, I will may handle in the future. I'm currently considering to use this (https://github.com/nodebox/opentype.js & https://github.com/icons8/svg-path-bounding-box)

mlennon3 commented 8 years ago

I am trying to use vexflow with react-native. It's a struggle, as I'm having to do it within a WebView component. I can get a 'hello-world' type example to render, but I don't know how I can do things more dynamically. Very interested in this discussion.

mturley commented 8 years ago

Can we figure out what we'd need to change in Vexflow to get it working without the DOM? That sounds like a great option.

davidfloegel commented 8 years ago

Oh wow, quite a few responses!

It would be great if we could look into it. I think it would be a great feature!

panarch commented 8 years ago

It looks many of you want this kind of standalone feature, so I changed my mind to do earlier.

I spent today and... I'm happy to share this, now vexflow can successfully generate svg files without DOM! I used opentype.js to convert all text to svg path, and replaced getBBox to svg-path-bounding-box. And then, for rendering svg strings I used react. (I also considered jsdom rather than react but in my previous exp, it was too heavy and slow to use because it's purpose is covering all the dom functions, not performance.)

I added new context named SVGStringContext which extends SVGContext.

I'll clean up the codes and push to my fork and share with you tomorrow with details.

0xfe commented 8 years ago

That's great Taehoon. Thanks!

How about naming it TextSVGContext?

On Wed, May 4, 2016 at 2:24 PM, Taehoon Moon notifications@github.com wrote:

It looks many of you want this kind of standalone feature, so I changed my mind to do earlier.

I spent today and... I'm happy to share this, now vexflow can successfully generate svg files without DOM! I used opentype.js https://github.com/nodebox/opentype.js to convert all text to svg path, and replaced getBBox to svg-path-bounding-box https://github.com/icons8/svg-path-bounding-box. And then, for rendering svg strings I used react. (I also considered jsdom https://github.com/tmpvar/jsdom rather than react but in my previous exp, it was too heavy and slow to use because it's purpose is covering all the dom functions, not performance.)

I added new context named SVGStringContext which extends SVGContext.

  • Do you have any better name suggestion for the new context? It would be nice to use better naming but I don't have idea.

I'll clean up the codes and push to my fork and share with you tomorrow with details.

— You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub https://github.com/0xfe/vexflow/issues/338#issuecomment-216956198

Mohit Muthanna [mohit (at) muthanna (uhuh) com]

davidfloegel commented 8 years ago

Wicked!! That was fast. Any chance you can provide a basic codesample as well??

panarch commented 8 years ago

I push to my fork with "standalone" branch. https://github.com/panarch/vexflow/commits/standalone https://github.com/panarch/vexflow/commit/c6727efe1c53f483240bf843ec82544c450974d1 https://github.com/panarch/vexflow/commit/89c4341d1e67bc2de77f5b42aacc5cb0223fcd83

grunt concat && npm run generate:current will work.

But one more thing to fix. I'm currently using NotoSans & NotoSerif fonts to render texts but they don't have some symbols for music notations below,

Vex.Flow.unicode = {
  // Unicode accidentals
  "sharp": String.fromCharCode(parseInt('266F', 16)),
  "flat" : String.fromCharCode(parseInt('266D', 16)),
  "natural": String.fromCharCode(parseInt('266E', 16)),
  // Major Chord
  "triangle": String.fromCharCode(parseInt('25B3', 16)),
  // half-diminished
  "o-with-slash": String.fromCharCode(parseInt('00F8', 16)),
   // Diminished
  "degrees": String.fromCharCode(parseInt('00B0', 16)),
  "circle": String.fromCharCode(parseInt('25CB', 16))
};

Because of that, I found that a single test case broken "TextNote.TextNote_Superscript_and_Subscript"

screen shot 2016-05-05 at 7 34 22 pm screen shot 2016-05-05 at 7 34 30 pm

The first one is blessed image and second thing is rendered by current fork.

For rendering musical chord symbols properly, we need special font for chord which should have sharp, flat, other symbols and latin characters.

Anyone knows any of license-free font for rendering music chord notations?

panarch commented 8 years ago

@0xfe I like TextSVGContext, thanks! I renamed :)

mturley commented 8 years ago

@Taehoon, that first image is considered correct? Even though that first superscript has the +5 occluding with the other characters? Looks broken to me

0xfe commented 8 years ago

@mturley You're right, that first image is not correct, but it is blessed. It's probably an uncaught regression that originated before we had the visual regression tests. Given that the second image has the "+5" properly positioned, it seems like the bug related to measureText (as usual.)

davidfloegel commented 8 years ago

ah this is awesome! :) I still don't get how to include and render something in react though.. Can someone give me a hint?

mlennon3 commented 8 years ago

Yes this seems great, thanks for such fast response! A bit of example will really help to get started.

infojunkie commented 8 years ago

@panarch re open licensed font, how about Bravura? It implements SMuFL which includes chord symbols.

panarch commented 8 years ago

@infojunkie Yes it includes chord symbols but the issue is it does not have latin characters. May be these are good examples, http://www.myriad-online.com/resources/docs/melody/english/fonts.htm https://www.myfonts.com/fonts/tijs-krammer/chord-symbols/

@davidfloegel Yeah current state may not be kind enough to refer if you are not familiar with vexflow test scripts because I just applied it to inside current test scripts. I will add code examples after fixing current chord notation font issue. After that you can easily figure out how to use.

infojunkie commented 8 years ago

@panarch re: fonts, it's my understanding that engraving systems typically use two fonts: one for music symbols and another for text, so it would make sense for VexFlow to follow suit. The advantage is that the engraver has the flexibility to choose any regular text font, without being limited to those that also contain music symbols. That's the idea of Bravura, Norfolk, etc.

0xfe commented 8 years ago

I have no problem with using multiple fonts, so long as they're public domain and license unencumbered. Feel free to add new glyphs into src/fonts/ if you need to. (I would love to have full support for Bravura in VexFlow.)

infojunkie commented 8 years ago

@0xfe yes, supporting multiple fonts and maybe even allowing app developers to specify their own - that don't need to be shipped with VF.

infojunkie commented 8 years ago

Regarding the creation of a React component for VexFlow, I think @mlennon3 and others are wondering, as I do, about how to design such a component to be reusable: the VF tests are of course trivial to wrap in a React component (the DOM/SVG issue above notwithstanding), but how would a reusable component look like? In other words, what kind of API would a generic VF React component expose to allow the app developer to dynamically specify the music to display?

To give examples of dynamic VexFlow generation:

A reusable React component would ideally expose all core VF functionality (e.g., via props) and conform to the React rendering workflow.

panarch commented 8 years ago

@infojunkie @0xfe I got it. My description was not enough for this. The issue is... for running vexflow without web browser, we cannot get web browser's pleasant font system support. So the alternative is opentype.js, it's really nice but currently it does not support using multiple fonts feature. So developers should manually handle fonts. That's why I wanted to find the font which includes both latin characters and chord symbols. It looks not too hard to contribute opentype.js to support multiple fonts but... not for now.

So I think this new TextSVGContext may not fit for many of light users because it requires users to do additional configurations. Users should manage fonts themselves, not convenient as CanvasContext or SVGContext which can use directly without any settings. TextSVGContext would be good choice for developers who wants to use vexflow in node.js or something like react-native, or better rendering performance (Because no more synchronous getBBox calling is required)

panarch commented 8 years ago

Possible hotfix is that I can bring chord symbols like sharp, flat, etc from BravuraText and merge into current existing fonts(NotoSans, NotoSerif). So, if there looks hard to get proper chord notation font for now, I will modify existing fonts to fit into currently broken test case; "TextNote.TextNote_Superscript_and_Subscript"

SalahAdDin commented 8 years ago

:+1:

panarch commented 8 years ago

@infojunkie And about React component, I'm not sure that I understand correctly. Although new TextSVGContext uses React library but it is not for providing users to make React component easily or somewhat related to React. It just need to use some kind of virtual-dom system to generate svg string and I thought ReactDOM was the best so I just used. It's totally independent issue between TextSVGContext and reusable React component.

In case of making ideal React component which have all core VF functionality, I think it's hard for now. For making that..., what we should do before is that I think it is making Builder API. After we build a VexFlow builder api which is clearly divided between formatting and rendering. Then we can try making proper VF React component which take a rendering part of the builder.

infojunkie commented 8 years ago

@panarch re: React component, yes I agree VF itself lacks a clear programming model. Your idea of Builder API sounds good. I will be looking into the examples I mentioned above to emulate or expand on their approaches. Re: fonts, I guess it's an ongoing area of discussion for VF...

Thanks for TextSVGContext, I'm looking forward to using it in my upcoming app project!

dannyid commented 8 years ago

@davidfloegel I've actually just yesterday starting playing around with getting VexFlow to dynamically update using React. My approach so far is to just destroy all the VexFlow created SVGs in willComponentReceiveProps() and redraw the entire thing based on Redux state. Have a look and let me know if you have any ideas.

https://github.com/dannyid/music-tutor/blob/master/js/components/Tutor.js

davidfloegel commented 8 years ago

Any progress on the use with react native? :)

remon-nashid commented 7 years ago

As per this post in Vexflow google group, @panarch has thankfully introduced an SVG rendering context (TextSVGContext) that works with react-native.

The context could be found in Taehoon's fork of Vexflow repo https://github.com/panarch/vexflow/commits/standalone

I tried to run the included tools/demo.js on android emulator, after making few unrelated changes. Unfortunately, I hit an error at this line:

import ReactDOMServer from 'react-dom/server';

Error message: EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React. The error is not entirely clear to me, but I wonder how useful ReactDOM(Server) is in react-native. Same issue was reported here, and is still unresolved.

Could someone give that demo a try? Hope it works.

SalahAdDin commented 7 years ago

With the last ES2015 implementations i think is most easier use vexflow in react/react-native projects.

davidfloegel commented 7 years ago

No that doesn't work and won't work like it's implemented in the demo.

  1. The demo uses react-dom - react-native has no DOM
  2. The loading of the fonts doesn't work for me. If I remove the font loading then
  3. Vex.Flow.Render.getTextSVGContent is not a function

:(

SalahAdDin commented 7 years ago

Oh yes, this is a big problem.

panarch commented 7 years ago

2,3 : I used node.js file api so, you should replace file api which react-native provides.

1 : hmm... it looks hard to solve this by adding react-dom lib to react-native project. So, if referencing react of react-native from react-dom is not possible, how about bundling react + react-dom/server independently without using react-native? If all looks hard to try, replacing TextSVGContext's react to another lib can be also option, it will be simple way to avoid conflict.

davidfloegel commented 7 years ago

@panarch hmmm I could try point 2. and 3 the way you mentioned...

No idea about the first part though... Anyone?

remon-nashid commented 7 years ago

I could take a stab at point 1. To make sure I fully understand the problem: are we proposing substituting ReactDOMServer with a library that could actually draw SVG in react native, for example react-native-svg?

panarch commented 7 years ago

@remon-georgy Yes, exactly. react-native-svg you mention looks good to try.

infojunkie commented 7 years ago

react-native-svg looks great, but it lacks one important function that VexFlow renderers need: the ability to compute bounding boxes: https://github.com/react-native-community/react-native-svg/issues/89

panarch commented 7 years ago

@infojunkie It's ok, computing bounding box is the key that TextSVGContext provides,

opentype.js to convert all text to svg path, and replaced getBBox to svg-path-bounding-box.

davidfloegel commented 7 years ago

I've tried out a couple of things but I can't get it to work.. Anyone else here had any progress?

SalahAdDin commented 7 years ago

@davidfloegel I ask the same.

jasonpang commented 7 years ago

I found this demo: https://github.com/kirkness/react-vexflow-example. The direct source isn't on his GitHub repository, but if you download and run the project (don't forget to move it to the C:\ drive because the paths are hardcoded), you can use the provided source maps and view the original source code in Chrome.

He's got a pretty interesting setup e.g.:

render() {
    return (
        <div className='App'>

            <button onClick={this.shuffle.bind(this)}>

                Shuffle.
            </button>

            <VexFlow
                height={800}
                width={800}
                font={[ 'Arial', 10, '' ]}
                stave={this.stave}
                voice={this.voice}
            />
        </div>
    );
}
export default class VexFlow extends Component {

    constructor(props) {
        super(props);

        this._renderer = null;
        this._context = null;
    }

    componentDidMount() { this.handleProps() }
    componentDidUpdate() { this.handleProps() }

    handleProps() {

        this.clear();

        const {
            width = 500,
            height = 500,
            font = [ 'Arial', 10, '' ],
            fontColor = '#eed',
            stave,
            notes,
            voice,
        } = this.props;

        this._renderer = new VF.Renderer(
            this.refs.wrapper,
            VF.Renderer.Backends.SVG
        );

        this._renderer.resize(width, height);
        this._context = this._renderer.getContext();

        this._context
            .setFont(...font)
            .setBackgroundFillStyle(fontColor);

        stave.setContext(this._context).draw();

        voice.draw(this._context, stave);
    }

    clear() {
        this.refs.wrapper.innerHTML = '';
    }

    render() {

        return (
            <div ref={'wrapper'}></div>
        );
    }
}
SalahAdDin commented 7 years ago

@jasonpang I can not understand these example.

matt-gardner commented 7 years ago

I got a very simple example actually rendering using react-native. You can try it for yourself here: https://github.com/matt-gardner/react-native-vexflow-example. Just clone the repo, update submodules, and run react-native run-ios (haven't tried it on android, and I didn't update index.android.js - if you want to try on android, just copy the stuff I did from index.ios.js to index.android.js). It should work, assuming you've already set up react-native and have successfully run the demo app. I just very slightly modified the demo app, and the vexflow code, to get this working. Thanks to everyone here who posted stuff - I pieced together a lot of things from this thread to get this actually working.

There's one pretty big issue still: it appears either I don't know anything about how to render things properly with vexflow (which is pretty close to true), or there are some issues with placing things in the SVG. So while I got it to render, I could use some help with getting it to render correctly. I'm hoping that others here who are more familiar with vexflow and JS development can help out with that. Also, the modifications I made to vexflow are certainly not good javascript - I've mostly written scala and python for machine learning, not javascript or mobile apps. If someone wants to help package up the changes I made (along with @panarch's earlier changes) into a nice PR to vexflow so that we can support this in the main release, that'd be nice too.

I'm attaching a screenshot of the app running in the ios simulator, for proof that I got this running (and again, it's not pretty, but I finally got it to render, which I was pretty happy about). screen shot 2017-01-13 at 10 01 37 pm

matt-gardner commented 7 years ago

I spent some time today stepping through a bunch of the rendering code, trying to figure out what's going on. I made a tiny bit of progress (see screenshot below), finding a bit of user error in how I was calling the formatter, but there's still a long way to go. I have a couple of guesses about what could be going on here:

  1. My first guess was that drawing Paths in the SVG is not working correctly. Except the rest is getting drawn, so maybe that's not it...

  2. So my current best guess is that the offset computation isn't happening correctly.

I spent most of my time trying to trace all of the draw() calls to learn how things got rendered and see if I could find anything there, so I haven't yet looked at offset computation in detail. I'll try that next time I get time to look at this. It'd probably be a whole lot easier with some pointers from someone who's more familiar with the codebase, though...

screen shot 2017-01-18 at 10 31 49 pm

SalahAdDin commented 7 years ago

Why use other vexflow version? Is not the official version.

matt-gardner commented 7 years ago

Because upstream does not have an SVG context that can be used outside of a browser, as it has calls to document. That was what all of the work earlier in this thread was about - getting vexflow to the point where it doesn't need a browser. I actually got it running in a react-native app, though I'm still having some rendering issues.

Once it's working properly, I intend to submit a pull request to upstream vexflow with the changes necessary to get it to work.

matt-gardner commented 7 years ago

I spent some time tonight going through all of the calculations done to get x and y positions and so on of all of the various elements. All of those computations look correct - the x and y positions of the Rect for the ledger lines match the x and y positions for the corresponding note heads, and they are next to each other in the G component of the SVG. Yet somehow they are rendered very far apart from each other. This is baffling to me, and it makes me wonder if there is something up with react-native-svg. I also have no idea where the clef and the time signature went off to. Their starting x values are off the screen to the right, but the positions specified in the Path are correct, so I'm not sure that's the problem.

Also, it looks like there's a different scaling for x than there is for y in the SVG, and that might be related somehow. The y position for the first ledger line is 90, while the x value is around 60, yet it is farther over than it is down. I'm not sure what's going on there, either.

I think my next step is to become more familiar with SVG rendering, and how react-native-svg works, as it seems like the problem is somewhere in the connection between vexflow and react-native-svg.

davidfloegel commented 7 years ago

@matt-gardner I used to play around with react-native-svg to build my own very small library for rendering scales + chords.

It seemed to me (like you say) react-native-svg is a bit dodgy in terms of rendering. I did all the calculations for distance + position etc on paper but if I put them in to the react-native app it didn't seem to work out.

If you want to work on it together feel free to ping me a message! Would be great to get this ready soon! :)

matt-gardner commented 7 years ago

Success! At least a bit. There are probably still some issues, but I found the main one. My current screenshot:

screen shot 2017-01-22 at 3 38 10 pm

The problem is that the SVG in react-native-svg uses the x and y attributes in the element as an offset when reading numbers in a Path. So if x is 90, and Path.d has move commands that have numbers around 90, the Path object will actually get rendered near x=180. So I just set the x and y values to 0 whenever creating a Path element, and that magically fixed everything (well, all of the issues I know about; I haven't tried to do anything complicated with this yet). I updated the repo I pointed to above, so you can checkout the code and try this yourself. It appears to work.