吉他初学者学习网站搭建系列(2)——如何实现一个乐谱播放器

吉他谱如何自动播放?个人乐谱播放网站YUERGS搭建

  • 背景
  • 介绍
    • 网站布局
    • 技术栈
    • 代码结构
    • 吉他谱文件结构
    • 滚动播放

背景

我是一个吉他弹唱爱好者,我的吉他谱都是自己在网络上收集到的图片,一般一首曲子都是好几张图片组成的。当我在弹吉他时,我希望图片可以按照一定的速度自动播放,可是目前现有的一些app,比如finger,在吉他谱达到一定数量后,没法继续添加乐谱,这让我决定自己动手做一个简单版本的吉他谱自动播放网站,成为了我练吉他必不可少的工具。

这个网站目前我部署在了gitee,网址是:https://hougiser.gitee.io/music-score/。

介绍

网站布局


大概可以猜出,我是陶喆的歌迷,嘿嘿

技术栈

vue3 + Vite + Vuetify
纯前端,不过图片的索引文件借助了node.js的能力来生成。

代码结构

吉他初学者学习网站搭建系列(2)——如何实现一个乐谱播放器_第1张图片

根组件App.vue,调用了子组件Score.vue和Chord.vue,分别用于展示乐谱和查和弦。Score.vue组件调用了Viewer.vue组件,用于吉他谱的全品展示和自动播放。

下面我介绍下我吉他谱的文件结构

吉他谱文件结构

吉他初学者学习网站搭建系列(2)——如何实现一个乐谱播放器_第2张图片
如上图所示,我将吉他谱放在public目录下,方便引用。一个根目录是data,下面每一首歌都按照歌手-歌名的命名方式,文件夹下放这首歌的吉他谱,按照顺序用数字命名。

我在每次打包时,会调用一段后端的代码,将这个文件目录转换为一个src/assets/directory.json文件,大致内容如下图:
吉他初学者学习网站搭建系列(2)——如何实现一个乐谱播放器_第3张图片
可以看到,是一个嵌套结构,每一个item包含了一些固定字段,如类型、名称、标题、作者、是否加密、文件时间、文件大小、以及文件夹下的子文件。这些信息都是通过File方法进行提取,最终可以直接展示。

如果有一些谱子,你不希望展示给公众,你想加密,有什么方法吗?
答案是有的。我这里使用的方法是,通过crypto-js库,将一段文本,通过密匙进行加密,最后用户输入正确密码,可以解密出对应文本,匹配正确则展示对应乐谱。这种方法的好处是简单,但是想要破解难度估计也不大,不过对于乐谱这种没有太多价值的东西,应该没有人想花时间破解这玩意儿。

还有一些手段,比如对图片像素进行加密,比如异或加密方法,我们知道,一个数字和另一个数字异或两次就等于其自身:

a === a^b^b // true

因此,可以利用一个数字密码,对每个像素先进行一次异或运算,然后在解密时,再异或一次,得到原图。不过需要考虑像素值超过255,这个可以再考虑如何解决。

滚动播放

吉他初学者学习网站搭建系列(2)——如何实现一个乐谱播放器_第4张图片
我这边实现了图片放大缩小、滚动速度调整和全屏的功能。
放大缩小功能很简单,用一个容器,将图片包在里面,宽度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();
      }
    },

欢迎交流~

你可能感兴趣的:(前端,吉他初学者网站,vue)