当前位置:首页 > VUE

vue实现歌词同步

2026-02-18 10:56:43VUE

实现歌词同步的基本思路

歌词同步的核心是将歌词文本与音频播放时间轴匹配,动态高亮当前播放的歌词行。通常需要解析歌词文件(如LRC格式),监听音频时间变化,并计算当前应显示的歌词行。

解析歌词文件

LRC格式的歌词包含时间标签和文本内容,例如:

[00:00.00]歌曲名
[00:01.50]第一行歌词
[00:04.20]第二行歌词

解析时可将歌词转换为对象数组,包含时间(秒)和文本:

function parseLrc(lrcText) {
  return lrcText.split('\n').filter(line => {
    const timeTag = line.match(/^\[(\d{2}):(\d{2})\.(\d{2})\]/);
    return timeTag && line.length > 10;
  }).map(line => {
    const parts = line.match(/^\[(\d{2}):(\d{2})\.(\d{2})\](.*)/);
    const min = parseInt(parts[1]);
    const sec = parseInt(parts[2]);
    const ms = parseInt(parts[3]);
    const text = parts[4].trim();
    return {
      time: min * 60 + sec + ms / 100,
      text
    };
  });
}

监听音频播放进度

使用HTML5的<audio>元素或第三方音频库(如howler.js)获取当前播放时间:

data() {
  return {
    currentTime: 0,
    audio: null
  }
},
mounted() {
  this.audio = new Audio('song.mp3');
  this.audio.addEventListener('timeupdate', () => {
    this.currentTime = this.audio.currentTime;
  });
}

计算当前歌词行

根据当前播放时间找到对应的歌词行:

computed: {
  currentLineIndex() {
    const { lyrics } = this;
    for (let i = 0; i < lyrics.length; i++) {
      if (this.currentTime < lyrics[i].time) {
        return i - 1;
      }
    }
    return lyrics.length - 1;
  }
}

渲染歌词列表

在模板中渲染歌词列表,并高亮当前行:

<div class="lyrics-container">
  <div 
    v-for="(line, index) in lyrics"
    :key="index"
    :class="{ 'active': index === currentLineIndex }"
  >
    {{ line.text }}
  </div>
</div>

添加平滑滚动效果

通过CSS和JavaScript实现歌词自动居中滚动:

vue实现歌词同步

watch: {
  currentLineIndex(newVal) {
    const container = this.$refs.lyricsContainer;
    const activeLine = container.children[newVal];
    if (activeLine) {
      container.scrollTo({
        top: activeLine.offsetTop - container.offsetHeight / 2,
        behavior: 'smooth'
      });
    }
  }
}

扩展功能建议

  • 双语歌词支持:解析双语LRC文件,中英文对照显示
  • 逐字高亮:进一步拆分每行歌词为单字,实现卡拉OK式效果
  • 动态背景:根据歌词情绪变化背景颜色或动画
  • 播放控制:添加进度条拖动、播放速度调节等功能

完整组件示例

<template>
  <div>
    <audio ref="audio" controls :src="audioUrl"></audio>
    <div class="lyrics-container" ref="lyricsContainer">
      <div
        v-for="(line, index) in lyrics"
        :key="index"
        :class="{ 'active': index === currentLineIndex }"
      >
        {{ line.text }}
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      audioUrl: 'path/to/audio.mp3',
      lrcText: `[00:01.50]第一行歌词
                [00:04.20]第二行歌词`,
      currentTime: 0
    };
  },
  computed: {
    lyrics() {
      return this.parseLrc(this.lrcText);
    },
    currentLineIndex() {
      for (let i = 0; i < this.lyrics.length; i++) {
        if (this.currentTime < this.lyrics[i].time) {
          return i - 1;
        }
      }
      return this.lyrics.length - 1;
    }
  },
  mounted() {
    const audio = this.$refs.audio;
    audio.addEventListener('timeupdate', () => {
      this.currentTime = audio.currentTime;
    });
  },
  methods: {
    parseLrc(lrcText) {
      return lrcText.split('\n').filter(line => {
        const timeTag = line.match(/^\[(\d{2}):(\d{2})\.(\d{2})\]/);
        return timeTag && line.length > 10;
      }).map(line => {
        const parts = line.match(/^\[(\d{2}):(\d{2})\.(\d{2})\](.*)/);
        const min = parseInt(parts[1]);
        const sec = parseInt(parts[2]);
        const ms = parseInt(parts[3]);
        const text = parts[4].trim();
        return {
          time: min * 60 + sec + ms / 100,
          text
        };
      });
    }
  },
  watch: {
    currentLineIndex(newVal) {
      const container = this.$refs.lyricsContainer;
      const activeLine = container.children[newVal];
      if (activeLine) {
        container.scrollTo({
          top: activeLine.offsetTop - container.offsetHeight / 2,
          behavior: 'smooth'
        });
      }
    }
  }
};
</script>

<style>
.lyrics-container {
  height: 300px;
  overflow-y: auto;
  text-align: center;
}
.lyrics-container div {
  padding: 10px;
  transition: all 0.3s ease;
}
.lyrics-container .active {
  color: #42b983;
  font-weight: bold;
  transform: scale(1.05);
}
</style>

标签: 歌词同步vue
分享给朋友:

相关文章

vue实现方法

vue实现方法

Vue 实现方法 Vue 是一种流行的前端框架,用于构建用户界面和单页应用。以下是几种常见的 Vue 实现方法: 创建 Vue 实例 通过 new Vue() 创建一个 Vue 实例,传入配置对象,…

vue实现oauth

vue实现oauth

Vue 实现 OAuth 的步骤 安装必要的依赖 确保项目中安装了 axios 和 vue-router。OAuth 通常需要发送 HTTP 请求和处理路由跳转。 npm install axios…

vue实现动画

vue实现动画

Vue 实现动画的方法 Vue 提供了多种方式来实现动画效果,包括内置的过渡系统、第三方动画库以及直接操作 CSS 或 JavaScript 动画。 使用 Vue 内置过渡系统 Vue 的 <…

js 实现vue

js 实现vue

实现 Vue 的核心功能 在 JavaScript 中实现 Vue 的核心功能需要模拟数据绑定、虚拟 DOM 和响应式系统。以下是一个简化版的实现思路: 响应式系统 通过 Object.define…

vue实现banner

vue实现banner

Vue实现Banner轮播 使用Vue实现Banner轮播可以通过第三方库如swiper或vue-awesome-swiper,也可以手动实现基础轮播效果。以下是两种常见方法: 使用vue-…

vue实现spa

vue实现spa

Vue 实现 SPA(单页应用)的步骤 创建 Vue 项目 使用 Vue CLI 快速初始化一个 Vue 项目,确保安装了 Vue Router 作为路由管理工具。运行以下命令创建项目: vue c…