JSVue

分享一个开源歌词编辑器vue-lrc-editor

原创2026-09-08艾思特码记约 14 分钟阅读
分享一个开源歌词编辑器vue-lrc-editor

某些歌词显示与演唱对不上时,可以方便使用。之前为了编辑一下歌词,度娘了好久都没有一款合适的编辑器,然后查找appstore,每款好用的基本上都需要收费,github, gitee都翻了没有找到合适的。


于是我和AI一起搞了一个歌词编辑器: vue-lrc-editor。


特性

  • 本地播放 — 音频文件通过 URL.createObjectURL 加载,不上传服务器
  • 进度拖拽 — 音频播放进度条,实时同步当前时间
  • 逐行打点 — 播放到对应位置时一键插入 [mm:ss.xx],支持自动跳到下一行连续打点
  • 时间戳可编辑 — 点击时间标签可直接修改分 / 秒 / 厘秒,失焦或回车保存,Esc 取消
  • 一行多时间 — 同一行可添加多个播放时间戳,自动按时间排序
  • 歌词编辑 — 添加 / 编辑 / 删除歌词行,textarea 自适应高度,未选中时也能读完整行
  • 点击选中 — 点击任意行即选中该行(整行卡片均为可点击区域)
  • 上 / 下插入空行 — 在任意行上方或下方快速插入空行
  • 行排序 — 上下移动调整歌词顺序
  • 文本导入 — 粘贴多行文本,按行批量生成歌词(覆盖模式)
  • LRC 导入 / 导出 — 支持标准 LRC 格式(含元数据 ti/ar/al/offset
  • 查看 LRC 输出 — 底部工具栏一键弹窗预览导出内容,支持复制
  • 实时高亮 — 播放时自动高亮当前歌词行
  • 美化界面 — 基于 Element Plus + Tailwind CSS,开箱即用


环境要求

本库以 peerDependency 形式依赖以下包,请确保你的项目已安装:

最低版本
vue^3.3.0
element-plus^2.14.0
@element-plus/icons-vue^2.3.2


安装

若你的项目尚未安装上述 peerDependency:

npm install

快速开始

组件内部使用了 Element Plus 及其图标,因此除本库样式外,还需引入 Element Plus 自带样式(图标包无需手动注册,组件内部已按需引入)。
<script setup lang="ts">
import { LrcEditor } from 'vue-lrc-editor'
import 'vue-lrc-editor/dist/style.css'     // 本库样式(Tailwind 编译产物)
import 'element-plus/dist/index.css'        // Element Plus 组件样式(peerDependency)
</script>

<template>
  <!-- 容器需有明确高度,编辑器会撑满父级 -->
  <div class="h-screen">
    <LrcEditor />
  </div>
</template>

组件 API

LrcEditor(推荐入口)

集成了音频播放与歌词编辑的完整编辑器,开箱即用。

Props:无。

通过 ref 暴露的方法

方法返回说明
exportLrc()string导出 LRC 格式字符串
parseLrc(text)LyricData解析 LRC 文本为结构化数据
importText(text)number按行导入纯文本歌词,返回新增行数
getMeta()LrcMetaData获取元数据
getLines()LrcLine[]获取全部歌词行
getSelectedLine()LrcLine | null获取当前选中行
<script setup lang="ts">
import { ref } from 'vue'
import { LrcEditor } from 'vue-lrc-editor'

const editorRef = ref<InstanceType<typeof LrcEditor>>()

function viewOutput() {
  console.log(editorRef.value?.exportLrc())
}
</script>

<template>
  <LrcEditor ref="editorRef" />
</template>

AudioPlayer

音频加载与播放控制面板。

Props

名称类型说明
selectedLineLrcLine | null当前选中行,用于「插入时间」按钮定位目标行

Emits

事件参数说明
audio-loadedfile: File选择音频文件后触发
time-updatetime: number播放进度更新(每 100ms)
select-lineline: LrcLine请求选中某行
insert-timeline, time在指定行、指定时间插入时间戳

暴露statetogglePlayseekTosetVolumeinsertCurrentTime

LyricList

歌词列表与底部工具栏(添加行 / 文本导入 / 导入 LRC / 查看 LRC 输出 / 导出 LRC)。

Props

名称类型说明
songNamestring导出文件名(缺省回退到 LRC 标题 / lyrics

Emits

事件参数说明
need-current-timelineId: string单行请求插入当前播放时间,由父组件提供

暴露setActiveLineselectLinescrollLineIntoViewgetSelectedLinemetalinesselectedIdinsertTimeStampaddLineAtexportLrcparseLrcimportTextclear

LyricLine

单行歌词卡片(通常由 LyricList 内部渲染,也可单独使用)。

PropsindextextstampsisSelectedisActive

Emitsupdate:textselectdeletemove('up'|'down')insert-stampdelete-stamp(uid)update-stamp(uid, seconds)insert-aboveinsert-below


Composables

useLyrics()

歌词数据与操作的响应式核心。


const {
  meta,            // Ref<LrcMetaData>
  lines,           // Ref<LrcLine[]>
  selectedId,      // Ref<string>
  addLine,         // (text?, timeStamp?) => LrcLine
  addLineAt,       // (relativeId, 'before'|'after') => LrcLine | null
  deleteLine,      // (id) => void
  moveLine,        // (id, 'up'|'down') => void
  insertTimeStamp, // (lineId, seconds) => void
  deleteTimeStamp, // (lineId, uid) => void
  updateTimeStamp, // (lineId, uid, seconds) => void —— 修改并自动重排
  exportLrc,       // () => string
  parseLrc,        // (text) => LyricData
  importText,      // (text) => number
  clear            // () => void
} = useLyrics()

useAudio()

音频播放 composable,纯前端,文件不上传服务器。

const {
  state,           // Ref<AudioState>:{ playing, currentTime, duration, volume, fileName }
  loadFile,        // (file: File) => void
  play,            // () => void
  pause,           // () => void
  togglePlay,      // () => void
  seekTo,          // (sec) => void
  setVolume,       // (vol) => void
  onTimeUpdate     // Ref<(time: number) => void> —— 设置后随播放进度回调,用于高亮当前行
} = useAudio()

类型定义


interface LrcTimeStamp {
  seconds: number;    // 时间(秒,精确到小数点后 2 位)
  uid: string;        // 唯一标识(区分同一行的多个时间戳)
}

interface LrcLine {
  id: string;
  text: string;
  stamps: LrcTimeStamp[];  // 一个行可以有多个时间戳
  order: number;           // 排序序号
}

interface LrcMetaData {
  title?: string;     // [ti:xxx]
  artist?: string;    // [ar:xxx]
  album?: string;     // [al:xxx]
  offset?: number;    // [offset:xxx]
}

interface LyricData {
  meta: LrcMetaData;
  lines: LrcLine[];
}



以上类型及工具函数 fmtTime / parseTimeStr 均从包入口导出,可直接 import 使用。


LRC 格式示例

[ti:歌曲名]
[ar:歌手]
[al:专辑]
[offset:0]
[00:00.00]第一句歌词
[00:05.00][00:08.00]同一行两个时间戳
[00:12.00]第三句

编辑器交互说明

  • 逐行打点:选中一行 → 播放音频 → 唱到该句时点「插入时间」,时间戳落到当前选中行并自动跳到下一行,连续打点无需手动换行。
  • 编辑时间戳:点击行内的 [mm:ss.xx] 标签,进入分 / 秒 / 厘秒三段编辑;输满两位自动跳到下一段,Enter 或失焦保存(自动钳制到合法范围),Esc 取消;标签右侧 × 删除该时间戳。
  • 行操作:鼠标悬停行卡片右侧出现操作组 —— 插入当前播放时间、上方 / 下方插入空行、上移、下移、删除。
  • 点击选中:点击行卡片任意位置即选中该行。
  • 查看 LRC 输出:底部工具栏点「查看 LRC 输出」弹窗预览导出内容,支持一键复制(「导出 LRC」则直接下载 .lrc 文件)。
  • 文本导入:点「文本导入」展开输入框,每行一句,点「按行导入」以覆盖模式生成歌词。


开发


npm install
npm run dev         # 开发预览(localhost:5173)
npm run typecheck   # 仅类型检查(vue-tsc --noEmit)
npm run build       # 构建发布产物(vue-tsc 类型检查 + vite 库构建,输出到 dist/)
npm run build:html  # 单文件 HTML:把 dev 预览内联成单个 html/index.html,双击即可打开
  • 库构建(npm run build)输出 UMD / ESM 产物及样式到 dist/,供 npm 消费。
  • 单文件构建(npm run build:html)将 vue / element-plus / 图标 / 源码 / 样式全部内联进一个独立 index.html,零外部依赖,便于离线分发或演示。