作为一个VUE新手,我接了一个任务,是写一个可以动态生成的五线谱,通过后台传递数据,每次显示的五线谱不同,我不识谱啊,这就很离谱。
通过一顿Google搜索,我找到了一个不错的JS/TS生成工具,Vexflow 它可以通过Canvas 或 SVG生成五线谱。
说啥也没用,反正都不会用,先安装!
npm i vexflow
先从简单教程开始,如果跳过简单教程的话可以直接看API:
我们先考虑五线谱是怎么组成的,五条线,小节,开始符号(我不知道叫啥),和一大堆音符。
首先看文档中的内容,如何生成第一小节:
准备工作,声明一个Renderer(渲染器)来承载这个SVG,我对这个类进行了一个简单的封装,渲染器的初始化放在了构造函数中。
constructor(element: string, width: number, height: number, scale: number, color: string = '#e6b626') {
this.lineColor = color; //设置线的颜色
this.fillColor = color; //设置音符的颜色
const div = document.querySelector(element) as HTMLDivElement;
const renderer = new Renderer(div, Renderer.Backends.SVG);//初始化渲染器
renderer.resize(width, height); //设置大小
this._context = renderer.getContext();
this._context.setFillStyle(this.fillColor);
this._context.setStrokeStyle(this.lineColor);
this._context.scale(scale, scale); //设置缩放X,Y
}
element作为一个承载的对象,我这里使用的是一个div来承载,程序会在div中渲染一个SVG。
创建一个新的行:
treble代表高音谱号,在网上可以找到一些开头的例子如图。
高音谱号使用颜色填充,这个音普是没有间隔的一个小节,如果希望整个音普之间都没有间隔,可以给260这个参数调整的稍微大一些,他就会变的很长。
Y轴的意义是调整新行的位置。
public getNewLine(y: number, context: RenderContext = this._context) {
const newStave = new Stave(0, y, 260); //X轴,Y轴,长度
//高音谱号treble 使用颜色填充
newStave.addClef("treble").setStyle({ fillStyle: this.fillColor, strokeStyle: this.lineColor });
return newStave.setContext(context).draw();
}
但是对于一个五线谱来说应该是分小节的,在我小学音乐的认知中,好像4分之4拍是在一个小节中有4个音符。
那么我就要开始在这个新的行后面跟上后续的小节:
/**
* 在指定的stave后生成一个新的小节,返回这个新小节的stave
* @param stave 第一小节的stave对象
* @param context 图谱的上下文
*/
public getLineBar(stave: Stave, context: RenderContext = this._context) {
const newStave = new Stave(stave.getWidth() + stave.getX(), stave.getY(), 260);
newStave.setStyle({ fillStyle: this.fillColor, strokeStyle: this.lineColor });
return newStave.setContext(context).draw();
}
stave.getWidth()+stave.getX() 可以获得到当前这个stave(五线谱)的结束位置,在这后面追加五线谱。
最后,在五线谱上需要出现音符:
/**
* 生成普通音符
* @param stave 要生成音符的小节五线谱
* @param notes 要生成的音符
* @param context 图谱的上下文
* @param beats 节拍 默认4
* @param value 节奏值 默认4
*/
public AddNoteToStave(stave: Stave, notes: Array
const voice = new Voice({ num_beats: beats, beat_value: value });
voice.addTickables(notes);
new Formatter().joinVoices([voice]).format([voice], 210);
voice.draw(context, stave);
return voice;
}
这个示例我给的默认值是4/4拍的,我觉得可能4/4拍的节奏比较普遍吧?反正我也不懂,先给上。
这里.format([voice], 210); 是指音符之间的间距,通过加大这个值,可以让五线谱中的音符间距加大,但是五线谱不会变化。
这里的notes(音符)是一个StaveNote对象:
const notes1 = [
// A quarter-note C.
new StaveNote({ keys: ["c/4"], duration: "q" })
.addModifier(new Annotation("这是").setVerticalJustification(AnnotationVerticalJustify.BOTTOM), 0),
// A quarter-note D.
new StaveNote({ keys: ["d/4"], duration: "q" }),
// A quarter-note rest. Note that the key (b/4) specifies the vertical
// position of the rest.
new StaveNote({ keys: ["b/4"], duration: "qr" }),
// A C-Major chord.
new StaveNote({ keys: ["c/4", "e/4", "g/4"], duration: "q" })
.addModifier(new Annotation("测试").setVerticalJustification(AnnotationVerticalJustify.BOTTOM), 0),
];
我最不明白的就是这个,不过有个对比图可以看一下。
我认为Keys指的是这个键位,但是我分不清多瑞咪,所以给我看也没啥意义。
重点是想说一下.addModifier(new Annotation("测试")
.setVerticalJustification(AnnotationVerticalJustify.BOTTOM)
这两段方法的意义在于可以再音符的下面增加文字,如果是歌词和音符之间有紧密关系的话,可以再这里增加一个文字,AnnotationVerticalJustify.BOTTOM是指在音符的下方出现。
目前我已知的就,我现在还不知道(♪)和跑跑(♫♬)如何处理,我也在继续研究中,如果有更好的方式我会继续补充。
文章链接
发表评论