paulrosen / abcjs

javascript for rendering abc music notation
Other
1.93k stars 284 forks source link

combine two staffs #908

Open loleek22 opened 1 year ago

loleek22 commented 1 year ago

First of all, I would like to thank you for the wonderful library! I wanted to implement displaying notes from two different div on the same staff. To do this, I am trying to combine two staffs into one. However, in some cases, as in the screenshot, this does not work (due to the different height of the svg?) Безымянный Is it possible to somehow combine them? Thank you! My code: `

let first = 'C' let second = '^b' ABCJS.renderAbc("firstStuff", first); ABCJS.renderAbc("secondStuff", second); `
paulrosen commented 1 year ago

If you're trying to do multiple voices then you should use the %%score line.

But if you are doing something fancy where they are actually two different pieces of music there are a couple of problems.

The first is what you identified - you can adjust the vertical yourself by looking for document.querySelectorAll('.abcjs-top-line'). That only works if you only have one line of music, though. The spacing will be different for each line depending on the content. You could pass oneSvgPerLine so that you can move the individual lines, though.

The second is that I don't know if you care about horizontal placement, but that depends on the content. The notes won't be lined up in time with each other. Even adding an accidental will change the spacing even if the same number of notes and note values are on both lines.

What's your use case?

loleek22 commented 1 year ago

Thank you for your answer! Actually, I am trying to implement the use of marking notes with different colors (I only need 4 colors). I am using .abcjs-note and .mark in css, so I can color the notes in two different colors depending on whether !mark! is next to the note or not. To add two more colors, I couldn't think of anything better than to combine two staffs. Perhaps there is a simpler way?

paulrosen commented 1 year ago

I've been thinking about expanding !mark! to allow an arbitrary parameter so you could have different types of marks. That might show up in a new version.

Is there anything else distinguishing about the notes? For instance if you want all of the G notes green then you can target all G notes with css. Or all the notes of measure 3. Look at the css classes and see if there's something to hold onto.

You can also do some post-processing. You are obviously controlling the ABC string that is being entered so you probably know which notes you want marked. After the renderAbc call you can directly manipulate the SVG. You can either do it directly or you can drill into the object that is returned. Something like:

const visualObj = abcjs.renderAbc(etc)
visualObj[0].lines.forEach(line => {
  // drill into the staff and voices to find the individual items.
  // There is lots of information, including where in the ABC string something comes from
  // and what svg element was created for it.
}
loleek22 commented 1 year ago

Thank you very much for your advice! I think if there are new !mark! features, that would be great! Especially since I have seen similar questions here. I followed your advice and delved into the returned object. It turned out like this:

visualObj[0].lines[0].staff[0].voices[0][1].pitches[0]

{pitch: 13, name: 'b', style: 'x', verticalPos: 13, highestVert: 13}

style: 'x' - distinctive in notes

I am new to abcjs and js and therefore I do not understand some things :) If it's not difficult for you, please tell me how exactly this selected note can change the color?

P.S. In CSS the color for .abcjs-note and .mark is already set, I'm trying to set '!style=x!' notes third color (like on picture) Безымянный2

paulrosen commented 1 year ago

I just did an experiment and was able to get all notes that are displayed as X and set their color like the following:

var obj = ABCJS.renderAbc("paper", abc, {add_classes:true});
obj[0].lines.forEach(line => {
    if (line.staff) {
        line.staff.forEach(staff => {
            staff.voices.forEach(voice => {
                voice.forEach(note => {
                    if (note.style === 'x') {
                        const element = note.abselem.elemset
                        if (element)
                            element[0].style.fill = "orange"
                    }
                })
            })
        })
    }
})
loleek22 commented 1 year ago

Thank you very much, Paul! It wasn't obvious to me. But it works!