吉他谱如何自动播放?个人乐谱播放网站YUERGS搭建
- 背景
- 介绍
- 网站布局
- 技术栈
- 代码结构
- 吉他谱文件结构
- 滚动播放
背景
我是一个吉他弹唱爱好者,我的吉他谱都是自己在网络上收集到的图片,一般一首曲子都是好几张图片组成的。当我在弹吉他时,我希望图片可以按照一定的速度自动播放,可是目前现有的一些app,比如finger,在吉他谱达到一定数量后,没法继续添加乐谱,这让我决定自己动手做一个简单版本的吉他谱自动播放网站,成为了我练吉他必不可少的工具。
这个网站目前我部署在了gitee,网址是:https://hougiser.gitee.io/music-score/。
介绍
网站布局
大概可以猜出,我是陶喆的歌迷,嘿嘿
技术栈
vue3 + Vite + Vuetify
纯前端,不过图片的索引文件借助了node.js的能力来生成。
代码结构
根组件App.vue,调用了子组件Score.vue和Chord.vue,分别用于展示乐谱和查和弦。Score.vue组件调用了Viewer.vue组件,用于吉他谱的全品展示和自动播放。
下面我介绍下我吉他谱的文件结构
吉他谱文件结构
如上图所示,我将吉他谱放在public目录下,方便引用。一个根目录是data,下面每一首歌都按照歌手-歌名的命名方式,文件夹下放这首歌的吉他谱,按照顺序用数字命名。
我在每次打包时,会调用一段后端的代码,将这个文件目录转换为一个src/assets/directory.json文件,大致内容如下图:
可以看到,是一个嵌套结构,每一个item包含了一些固定字段,如类型、名称、标题、作者、是否加密、文件时间、文件大小、以及文件夹下的子文件。这些信息都是通过File方法进行提取,最终可以直接展示。
如果有一些谱子,你不希望展示给公众,你想加密,有什么方法吗?
答案是有的。我这里使用的方法是,通过crypto-js库,将一段文本,通过密匙进行加密,最后用户输入正确密码,可以解密出对应文本,匹配正确则展示对应乐谱。这种方法的好处是简单,但是想要破解难度估计也不大,不过对于乐谱这种没有太多价值的东西,应该没有人想花时间破解这玩意儿。
还有一些手段,比如对图片像素进行加密,比如异或加密方法,我们知道,一个数字和另一个数字异或两次就等于其自身:
a === a^b^b // true
因此,可以利用一个数字密码,对每个像素先进行一次异或运算,然后在解密时,再异或一次,得到原图。不过需要考虑像素值超过255,这个可以再考虑如何解决。
滚动播放
我这边实现了图片放大缩小、滚动速度调整和全屏的功能。
放大缩小功能很简单,用一个容器,将图片包在里面,宽度100%,用flex朝下的布局方式顺序布局。而容器居中定位即可。缩放本质是改变容器的宽度。
滚动播放如何实现?
也很简单,定时调整容器元素的scrollTop,利用requestAnimationFrame可以重绘的特性,利用一些计数方法来控制滚动速度。为了让网页滚动得比较流畅,最好每次重绘时,滚动条高度+1像素,像素差过大,不够丝滑。代码如下:
const speedList = [5, 4, 3, 2, 1];
export default {
methods: {
// 调整滚动速度
speedUp() {
this.speed = (this.speed + 1) % speedList.length;
},
// 滚动
moveDown() {
if (requestIns) {
cancelAnimationFrame(requestIns);
requestIns = 0;
} else {
this.run();
}
},
// 重绘
run() {
times++;
if (times > speedList[this.speed]) {
times = 0;
this.$refs.viewContainer.scrollTop += 1;
if (
this.$refs.viewContainer.scrollTop ===
this.$refs.viewContainer.scrollHeight -
this.$refs.viewContainer.clientHeight
) {
this.moveDown();
return;
}
}
requestIns = requestAnimationFrame(this.run);
},
}
}
全屏功能利用现有的fullscreen API,代码如下:
fullScreen() {
if (!document.fullscreenElement) {
this.$refs.scoreContainer.requestFullscreen();
} else {
document?.exitFullscreen();
}
},
欢迎交流~