forked from 0xfe/vexflow
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from PolymerLabs/easyscore-basics
Wrap Stave and Voice in web components
- Loading branch information
Showing
7 changed files
with
3,733 additions
and
385 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// EasyScore API Example | ||
|
||
import Vex from './src/index.js'; | ||
const VF = Vex.Flow; | ||
|
||
// Create an SVG renderer and attach it to the DIV element named "boo". | ||
var vf = new VF.Factory({renderer: {elementId: null}}); | ||
var score = vf.EasyScore(); | ||
var system = vf.System(); | ||
|
||
system.addStave({ | ||
voices: [score.voice(score.notes('C#5/q, B4, A4, G#4'))] | ||
}).addClef('treble').addTimeSignature('4/4'); | ||
|
||
vf.draw(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<html> | ||
<head> | ||
<title>Vexflow Component</title> | ||
<script type="module" src='./wc-src/vf-score.js'></script> | ||
<script type="module" src='./wc-src/vf-stave.js'></script> | ||
<script type="module" src='./wc-src/vf-voice.js'></script> | ||
</head> | ||
<body> | ||
<!-- For testing the EasyScore API example | ||
<div id="boo"></div> | ||
<script type="module" src="./app.js"></script> --> | ||
|
||
<vf-score> | ||
<vf-stave clef='treble' timeSig='4/4' keySig='Bb'> | ||
<vf-voice autoBeam>C5/q, (C4 Eb4 G4)/q, A4, G4/16, A4, B4, C5</vf-voice> | ||
<vf-voice stem='up'>C#4/h, C#4</vf-voice> | ||
</vf-stave> | ||
</vf-score> | ||
</body> | ||
</html> |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,5 +67,8 @@ | |
"notation", | ||
"guitar", | ||
"tablature" | ||
] | ||
], | ||
"dependencies": { | ||
"es-dev-server": "^1.54.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import Vex from '../src/index.js'; | ||
|
||
const template = document.createElement('template'); | ||
template.innerHTML = ` | ||
<div id='vf-score'> | ||
<slot></slot> | ||
</div> | ||
` | ||
|
||
export class VFScore extends HTMLElement { | ||
constructor() { | ||
super(); | ||
|
||
this.attachShadow({ mode:'open' }); | ||
this.shadowRoot.appendChild(document.importNode(template.content, true)); | ||
|
||
this.addEventListener('vfVoiceReady', this.setFactory); | ||
this.addEventListener('vfStaveReady', this.setFactory); | ||
} | ||
|
||
connectedCallback() { | ||
this.setupVexflow(this.getAttribute('width') || 500, this.getAttribute('height') || 200); | ||
this.setupFactory(); | ||
} | ||
|
||
setupVexflow(width, height) { | ||
const div = this.shadowRoot.getElementById('vf-score'); | ||
const renderer = new Vex.Flow.Renderer(div, Vex.Flow.Renderer.Backends.SVG); | ||
renderer.resize(width, height); | ||
this.context = renderer.getContext(); | ||
} | ||
|
||
setupFactory() { | ||
this.vf = new Vex.Flow.Factory({renderer: {elementId: null}}); | ||
this.vf.setContext(this.context); | ||
} | ||
|
||
/** Sets the factory instance of the component that dispatched the event */ | ||
setFactory = () => { | ||
event.target.vf = this.vf; | ||
} | ||
} | ||
|
||
window.customElements.define('vf-score', VFScore); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import Vex from '../src/index.js'; | ||
import './vf-score'; | ||
|
||
const template = document.createElement('template'); | ||
template.innerHTML = ` | ||
<slot></slot> | ||
`; | ||
|
||
export class VFStave extends HTMLElement { | ||
constructor() { | ||
super(); | ||
|
||
// Defaults | ||
this.voices = []; | ||
this.beams = []; | ||
this._vf = undefined; | ||
|
||
this.attachShadow({ mode:'open' }); | ||
this.shadowRoot.appendChild(document.importNode(template.content, true)); | ||
|
||
this.addEventListener('notesCreated', this.addVoice); | ||
this.addEventListener('vfVoiceReady', this.setScore); | ||
} | ||
|
||
connectedCallback() { | ||
this.clef = this.getAttribute('clef'); | ||
this.timeSig = this.getAttribute('timeSig'); | ||
this.keySig = this.getAttribute('keySig'); | ||
|
||
const vfStaveReadyEvent = new CustomEvent('vfStaveReady', { bubbles: true }); | ||
this.dispatchEvent(vfStaveReadyEvent); | ||
|
||
this.shadowRoot.querySelector('slot').addEventListener('slotchange', this.registerVoices); | ||
} | ||
|
||
disconnectedCallback() { | ||
this.shadowRoot.querySelector('slot').removeEventListener('slotchange', this.registerVoices); | ||
} | ||
|
||
set vf(value) { | ||
this._vf = value; | ||
this.setupStave(); | ||
} | ||
|
||
setupStave() { | ||
this.score = this._vf.EasyScore(); | ||
this.score.set({ | ||
clef: this.clef || 'treble', | ||
time: this.timeSig || '4/4' | ||
}); | ||
|
||
this.stave = this._vf.Stave( { x: 10, y: 40, width: 400 }); | ||
|
||
if (this.clef) { | ||
this.stave.addClef(this.clef); | ||
} | ||
|
||
if (this.timeSig) { | ||
this.stave.addTimeSignature(this.timeSig); | ||
} | ||
|
||
if (this.keySig) { | ||
this.stave.addKeySignature(this.keySig); | ||
} | ||
|
||
this.stave.draw(); | ||
} | ||
|
||
/** slotchange event listener */ | ||
registerVoices = () => { | ||
const voiceSlots = this.shadowRoot.querySelector('slot').assignedElements().filter( e => e.nodeName === 'VF-VOICE'); | ||
this.numVoices = voiceSlots.length; | ||
|
||
if (this.voices.length === this.numVoices) { | ||
this.formatAndDrawVoices(); | ||
} | ||
} | ||
|
||
/** Event listener when vf-voice returns notes */ | ||
addVoice = (e) => { | ||
const notes = e.detail.notes; | ||
const beams = e.detail.beams; | ||
const voice = this.createVoiceFromNotes(notes); | ||
|
||
this.voices.push(voice); | ||
this.beams = this.beams.concat(beams); | ||
|
||
// Make sure all voices are created first, then format & draw to make sure alignment is correct | ||
if (this.voices.length === this.numVoices) { | ||
this.formatAndDrawVoices(); | ||
} | ||
} | ||
|
||
createVoiceFromNotes(staveNotes) { | ||
return this.score.voice(staveNotes); | ||
} | ||
|
||
formatAndDrawVoices() { | ||
var formatter = new Vex.Flow.Formatter() | ||
formatter.joinVoices(this.voices); | ||
formatter.formatToStave(this.voices, this.stave); | ||
this._vf.draw(); | ||
} | ||
|
||
|
||
/** Sets the score instance of the component that dispatched the event */ | ||
setScore = () => { | ||
event.target.score = this.score; | ||
} | ||
|
||
} | ||
|
||
window.customElements.define('vf-stave', VFStave); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import Vex from '../src/index.js'; | ||
|
||
const template = document.createElement('template'); | ||
template.innerHTML = ` | ||
`; | ||
|
||
export class VFVoice extends HTMLElement { | ||
constructor() { | ||
super(); | ||
|
||
this.attachShadow({ mode:'open' }); | ||
this.shadowRoot.appendChild(document.importNode(template.content, true)); | ||
|
||
// Defaults | ||
this.stem = 'up'; | ||
this.autoBeam = false; | ||
this.notes = []; | ||
this.beams = []; | ||
|
||
this._vf = undefined; | ||
this._score = undefined; | ||
} | ||
|
||
connectedCallback() { | ||
this.stem = this.getAttribute('stem') || this.stem; | ||
this.autoBeam = this.hasAttribute('autoBeam'); | ||
this.notesText = this.textContent.trim(); | ||
|
||
const vfVoiceReadyEvent = new CustomEvent('vfVoiceReady', { bubbles: true }); | ||
this.dispatchEvent(vfVoiceReadyEvent); | ||
} | ||
|
||
set vf(value) { | ||
this._vf = value; | ||
this.createNotes(); | ||
} | ||
|
||
set score(value) { | ||
this._score = value; | ||
this.createNotes(); | ||
} | ||
|
||
createNotes() { | ||
if (this._vf && this._score) { | ||
const notes = this.createNotesFromText(); | ||
this.notes.push(...notes); | ||
if (this.autoBeam) { | ||
this.beams.push(...this.autoGenerateBeams(notes)); | ||
} | ||
|
||
const notesAndBeamsCreatedEvent = new CustomEvent('notesCreated', { bubbles: true, detail: { notes: this.notes, beams: this.beams } }); | ||
this.dispatchEvent(notesAndBeamsCreatedEvent); | ||
} | ||
} | ||
|
||
createNotesFromText() { | ||
this._score.set({ stem: this.stem }); | ||
const staveNotes = this._score.notes(this.notesText); | ||
return staveNotes; | ||
} | ||
|
||
autoGenerateBeams(notes) { | ||
const beams = Vex.Flow.Beam.generateBeams(notes); | ||
beams.forEach( beam => { | ||
this._vf.renderQ.push(beam); | ||
}) | ||
return beams; | ||
} | ||
} | ||
|
||
window.customElements.define('vf-voice', VFVoice); |