fork(fix): Clone AMLL 并修复 BUG

- 将AMLL Clone到本以地进行修复和优化(emm虽然这很不优雅但是暂时无时间做子模块和Fork)
- 修复在当前播放歌词行不可见的视口Seek会出现滚动偏移的问题
This commit is contained in:
lqtmcstudio
2026-06-07 00:02:14 +08:00
parent 783d2c3dee
commit 72f4510dc8
458 changed files with 86075 additions and 1665 deletions

View File

@@ -0,0 +1,76 @@
## 0.5.1 (2026-05-17)
### Patch Changes
- **fix:** 修复含对唱时歌词错误提前导致的多行高亮 ([#521](https://github.com/amll-dev/applemusic-like-lyrics/pull/521))
- **refactor:** 引入歌词组来包装主歌词和背景人声 & 前置背景人声 ([#531](https://github.com/amll-dev/applemusic-like-lyrics/pull/531))
- **chore:** 更正 package.json 协议声明 ([#534](https://github.com/amll-dev/applemusic-like-lyrics/pull/534))
仓库根目录的 LICENSE 文件为 AGPL v3.0 协议,但是 package.json 中的 `license` 字段为 `GPL-3.0`。经与原开发者确认package.json 中的 `license` 字段有误。仓库与其所有产出的 npm 包均应为 AGPL v3 only 协议SPDX: `AGPL-3.0-only`。因此,更正各包 `package.json``license` 字段为 `AGPL-3.0-only`
- **chore(core):** 优化类型定义 ([#519](https://github.com/amll-dev/applemusic-like-lyrics/pull/519))
### Contributors
- apoint123 [@apoint123](https://github.com/apoint123)
- Linho [@Linho1219](https://github.com/Linho1219)
## 0.5.0 (2026-05-12)
### Minor Changes
- **refactor:** 整理核心播放器代码结构,将抽象接口部分集中到统一目录 ([#508](https://github.com/amll-dev/applemusic-like-lyrics/pull/508))
- **refactor:** 整理核心播放器抽象类中时间线、滚动与单行布局部分的结构与状态管理 ([#509](https://github.com/amll-dev/applemusic-like-lyrics/pull/509))
### Patch Changes
- **fix:** 修复 setCurrentTime 在提供 isSeek 标志时,实际排版未遵守标志导致布局异常漂移的问题 ([#509](https://github.com/amll-dev/applemusic-like-lyrics/pull/509))
- **fix:** 修复在同一行时间内拖拽进度条时逐字动画不同步的问题 ([#509](https://github.com/amll-dev/applemusic-like-lyrics/pull/509))
- **fix:** 修复暂停状态下点击行跳转时仍播放逐字动画的问题 ([#509](https://github.com/amll-dev/applemusic-like-lyrics/pull/509))
### Contributors
- Linho [@Linho1219](https://github.com/Linho1219)
## 0.4.2 (2026-05-01)
### Patch Changes
- **feat(core):** 平衡行长度时优先在标点处换行 ([#503](https://github.com/amll-dev/applemusic-like-lyrics/pull/503))
- **fix:** 修复背景行注音高度错误 ([#497](https://github.com/amll-dev/applemusic-like-lyrics/pull/497))
- **fix(core):** 修正平衡行长度时的行宽度计算 ([#502](https://github.com/amll-dev/applemusic-like-lyrics/pull/502))
### Contributors
- apoint123 [@apoint123](https://github.com/apoint123)
- Linho [@Linho1219](https://github.com/Linho1219)
## 0.4.1 (2026-04-23)
### Patch Changes
- **fix:** 在各绑定中暴露歌词优化选项 ([#492](https://github.com/amll-dev/applemusic-like-lyrics/pull/492))
- **fix(vue):** 修复掩码模式错误的类型 ([#496](https://github.com/amll-dev/applemusic-like-lyrics/pull/496))
- **refactor(core):** 重构平均行长度实现 ([#494](https://github.com/amll-dev/applemusic-like-lyrics/pull/494))
### Contributors
- apoint123 [@apoint123](https://github.com/apoint123)
## 0.4.0 (2026-04-14)
### Minor Changes
- **chore:** 移除 canvas 歌词渲染器 ([#476](https://github.com/amll-dev/applemusic-like-lyrics/pull/476))
### Patch Changes
- **refactor:** 重构核心库测试组织模式 ([3db83c93](https://github.com/amll-dev/applemusic-like-lyrics/commit/3db83c93))
- **docs:** 修正 optimize-lyric.ts 和 OptimizeLyricOptions 里 cleanUnintentionalOverlaps 的文档和注释 ([75a8c0bb](https://github.com/amll-dev/applemusic-like-lyrics/commit/75a8c0bb))
- **chore:** 更换工具链 ([#476](https://github.com/amll-dev/applemusic-like-lyrics/pull/476))
- **chore:** 在项目范围内启用 isolatedDeclarations ([#480](https://github.com/amll-dev/applemusic-like-lyrics/pull/480))
### Contributors
- apoint123 [@apoint123](https://github.com/apoint123)
- Linho [@Linho1219](https://github.com/Linho1219)
- MoYingJi [@MoYingJi](https://github.com/MoYingJi)

View File

@@ -0,0 +1,52 @@
# AMLL for Vue
[English](./README.md) / 简体中文
> 警告:此为个人项目,且尚未完成开发,可能仍有大量问题,所以请勿直接用于生产环境!
![AMLL-Vue](https://img.shields.io/badge/Vue-%2342d392?label=Apple%20Music-like%20Lyrics&labelColor=%23FB5C74)
[![npm](https://img.shields.io/npm/dt/%40applemusic-like-lyrics/vue)](https://www.npmjs.com/package/@applemusic-like-lyrics/vue)
[![npm](https://img.shields.io/npm/v/%40applemusic-like-lyrics%2Fvue)](https://www.npmjs.com/package/@applemusic-like-lyrics/vue)
AMLL 组件库的 Vue 绑定,你可以通过此库来更加方便地使用 AMLL 歌词组件。
详情可以访问 [Core 核心组件的 README.md](../core/README-CN.md)。
## 安装
安装使用的依赖(如果以下列出的依赖包没有安装的话需要自行安装):
```bash
npm install @pixi/app @pixi/core @pixi/display @pixi/filter-blur @pixi/filter-bulge-pinch @pixi/filter-color-matrix @pixi/sprite jss jss-preset-default # 使用 npm
yarn add @pixi/app @pixi/core @pixi/display @pixi/filter-blur @pixi/filter-bulge-pinch @pixi/filter-color-matrix @pixi/sprite jss jss-preset-default # 使用 yarn
```
安装 Vue 绑定需要使用的依赖(如果以下列出的依赖包没有安装的话需要自行安装):
```bash
npm install vue # 使用 npm
yarn add vue # 使用 yarn
```
安装本体框架:
```bash
npm install @applemusic-like-lyrics/vue # 使用 npm
yarn add @applemusic-like-lyrics/vue # 使用 yarn
```
## 使用方式摘要
由于 Vue 组件不方便生成 API 文档,所以还请自行查阅类型定义文件确定用法。
(或者参考 React 绑定,二者属性和引用方式完全一致)
一个测试用途的程序可以在 [../playground/vue/src/test.ts](../playground/vue/src/test.ts) 里找到。
```vue
<tamplate>
<LyricPlayer :lyric-lines="[]" :current-time="0" />
</tamplate>
<script setup lang="ts">
import { LyricPlayer } from "@applemusic-like-lyrics/vue";
</script>
```

View File

@@ -0,0 +1,52 @@
# AMLL for Vue
English / [简体中文](./README-CN.md)
> Warning: This is a personal project and is still under development. There may still be many issues, so please do not use it directly in production environments!
![AMLL-Vue](https://img.shields.io/badge/Vue-%2342d392?label=Apple%20Music-like%20Lyrics&labelColor=%23FB5C74)
[![npm](https://img.shields.io/npm/dt/%40applemusic-like-lyrics/vue)](https://www.npmjs.com/package/@applemusic-like-lyrics/vue)
[![npm](https://img.shields.io/npm/v/%40applemusic-like-lyrics%2Fvue)](https://www.npmjs.com/package/@applemusic-like-lyrics/vue)
Vue binding for the AMLL component library, which allows you to use AMLL lyric components more conveniently.
For more details, please visit [Core component README.md](../core/README.md).
## Installation
Install the required dependencies (if the dependencies listed below are not installed, you need to install them yourself):
```bash
npm install @pixi/app @pixi/core @pixi/display @pixi/filter-blur @pixi/filter-bulge-pinch @pixi/filter-color-matrix @pixi/sprite jss jss-preset-default # using npm
yarn add @pixi/app @pixi/core @pixi/display @pixi/filter-blur @pixi/filter-bulge-pinch @pixi/filter-color-matrix @pixi/sprite jss jss-preset-default # using yarn
```
Install the dependencies required for Vue binding (if the dependencies listed below are not installed, you need to install them yourself):
```bash
npm install vue # using npm
yarn add vue # using yarn
```
Install the framework:
```bash
npm install @applemusic-like-lyrics/vue # using npm
yarn add @applemusic-like-lyrics/vue # using yarn
```
## Usage Summary
Since Vue components are not convenient for generating API documentation, please refer to the type definition files to determine usage.
(Or refer to the React binding, as both have identical properties and reference methods)
A test program can be found in [../playground/vue/src/test.ts](../playground/vue/src/test.ts).
```vue
<tamplate>
<LyricPlayer :lyric-lines="[]" :current-time="0" />
</tamplate>
<script setup lang="ts">
import { LyricPlayer } from "@applemusic-like-lyrics/vue";
</script>
```

View File

@@ -0,0 +1,71 @@
{
"name": "@applemusic-like-lyrics/vue",
"version": "0.5.1",
"description": "AMLL 组件库的 Vue 绑定",
"repository": {
"url": "https://github.com/amll-dev/applemusic-like-lyrics.git",
"directory": "packages/vue",
"type": "git"
},
"type": "module",
"license": "AGPL-3.0-only",
"nx": {
"tags": [
"library"
],
"targets": {
"nx-release-publish": {
"executor": "@nx/js:release-publish",
"dependsOn": []
}
}
},
"files": [
"dist"
],
"scripts": {
"typecheck": "tsgo -b",
"build-only": "tsdown",
"build": "run-p typecheck \"build-only {@}\" --",
"build:dev": "tsdown",
"fmt": "biome format --write ./src",
"dev": "nx run @applemusic-like-lyrics/playground-vue:dev"
},
"main": "./dist/amll-vue.cjs",
"module": "./dist/amll-vue.mjs",
"typings": "./dist/amll-vue.d.mts",
"exports": {
".": {
"import": {
"types": "./dist/amll-vue.d.mts",
"default": "./dist/amll-vue.mjs"
},
"require": {
"types": "./dist/amll-vue.d.cts",
"default": "./dist/amll-vue.cjs"
}
}
},
"dependencies": {
"@applemusic-like-lyrics/core": "file:../core"
},
"peerDependencies": {
"vue": "*"
},
"devDependencies": {
"@applemusic-like-lyrics/core": "file:../core",
"@biomejs/biome": "^2.4.8",
"@pixi/app": "^7.4.3",
"@pixi/core": "^7.4.3",
"@pixi/display": "^7.4.3",
"@pixi/filter-blur": "^7.4.3",
"@pixi/filter-color-matrix": "^7.4.3",
"@pixi/sprite": "^7.4.3",
"@rolldown/plugin-babel": "^0.2.3",
"@vue/babel-plugin-jsx": "^2.0.1",
"tsdown": "^0.22.0",
"typedoc": "^0.28.17",
"typedoc-plugin-markdown": "^4.11.0",
"vue": "^3.5.30"
}
}

View File

@@ -0,0 +1,182 @@
import {
type AbstractBaseRenderer,
type BaseRenderer,
BackgroundRender as CoreBackgroundRender,
MeshGradientRenderer,
} from "@applemusic-like-lyrics/core";
import {
defineComponent,
type ExtractPublicPropTypes,
onMounted,
onUnmounted,
type PropType,
type Ref,
ref,
type ShallowRef,
useTemplateRef,
watchEffect,
} from "vue";
/**
* 背景渲染组件的引用
*/
export interface BackgroundRenderRef {
/**
* 背景渲染实例引用
*/
bgRender?: Ref<AbstractBaseRenderer | undefined>;
/**
* 将背景渲染实例的元素包裹起来的 DIV 元素实例
*/
wrapperEl: Readonly<ShallowRef<HTMLDivElement | null>>;
}
const backgroundRenderProps = {
/**
* 设置背景专辑资源
*/
album: {
type: [String, Object] as PropType<
string | HTMLImageElement | HTMLVideoElement
>,
required: false,
},
/**
* 设置专辑资源是否为视频
*/
albumIsVideo: {
type: Boolean,
required: false,
},
/**
* 设置当前背景动画帧率,如果为 `undefined` 则默认为 `30`
*/
fps: {
type: Number,
required: false,
},
/**
* 设置当前播放状态,如果为 `undefined` 则默认为 `true`
*/
playing: {
type: Boolean,
required: false,
},
/**
* 设置当前动画流动速度,如果为 `undefined` 则默认为 `2`
*/
flowSpeed: {
type: Number,
required: false,
},
/**
* 设置背景是否根据“是否有歌词”这个特征调整自身效果,例如有歌词时会变得更加活跃
*
* 部分渲染器会根据这个特征调整自身效果
*
* 如果不确定是否需要赋值或无法知晓是否包含歌词,请传入 true 或不做任何处理(默认值为 true
*/
hasLyric: {
type: Boolean,
required: false,
},
/**
* 设置低频的音量大小,范围在 80hz-120hz 之间为宜,取值范围在 [0.0-1.0] 之间
*
* 部分渲染器会根据音量大小调整背景效果(例如根据鼓点跳动)
*
* 如果无法获取到类似的数据,请传入 undefined 或 1.0 作为默认值,或不做任何处理(默认值即 1.0
*/
lowFreqVolume: {
type: Number,
required: false,
},
/**
* 设置当前渲染缩放比例,如果为 `undefined` 则默认为 `0.5`
*/
renderScale: {
type: Number,
required: false,
},
/**
* 设置渲染器,如果为 `undefined` 则默认为 `MeshGradientRenderer`
* 默认渲染器有可能会随着版本更新而更换
*/
renderer: {
type: Object as PropType<{
new (...args: ConstructorParameters<typeof BaseRenderer>): BaseRenderer;
}>,
required: false,
},
} as const;
export type BackgroundRenderProps = ExtractPublicPropTypes<
typeof backgroundRenderProps
>;
export const BackgroundRender = defineComponent({
name: "BackgroundRender",
props: backgroundRenderProps,
setup(props, { expose }) {
const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper-ref");
const bgRenderRef = ref<AbstractBaseRenderer>();
onMounted(() => {
if (wrapperRef.value) {
bgRenderRef.value = CoreBackgroundRender.new(
props.renderer ?? MeshGradientRenderer,
);
const el = bgRenderRef.value.getElement();
el.style.width = "100%";
el.style.height = "100%";
wrapperRef.value.appendChild(el);
}
});
onUnmounted(() => {
if (bgRenderRef.value) {
bgRenderRef.value.dispose();
}
});
watchEffect(() => {
if (props.album)
bgRenderRef.value?.setAlbum(props.album, props.albumIsVideo);
});
watchEffect(() => {
if (props.fps) bgRenderRef.value?.setFPS(props.fps);
});
watchEffect(() => {
if (props.playing) bgRenderRef.value?.pause();
else bgRenderRef.value?.resume();
});
watchEffect(() => {
if (props.flowSpeed) bgRenderRef.value?.setFlowSpeed(props.flowSpeed);
});
watchEffect(() => {
if (props.renderScale)
bgRenderRef.value?.setRenderScale(props.renderScale);
});
watchEffect(() => {
if (props.lowFreqVolume)
bgRenderRef.value?.setLowFreqVolume(props.lowFreqVolume);
});
watchEffect(() => {
if (props.hasLyric !== undefined)
bgRenderRef.value?.setHasLyric(props.hasLyric ?? true);
});
expose<BackgroundRenderRef>({
bgRender: bgRenderRef,
wrapperEl: wrapperRef,
});
return () => <div style="display: contents;" ref="wrapper-ref" />;
},
});

View File

@@ -0,0 +1,387 @@
import {
type BaseRenderer,
LyricPlayer as CoreLyricPlayer,
type LyricLine,
type LyricLineMouseEvent,
type LyricPlayerBase,
MaskObsceneWordsMode,
type OptimizeLyricOptions,
type spring,
} from "@applemusic-like-lyrics/core";
import {
computed,
defineComponent,
type ExtractPublicPropTypes,
onMounted,
onUnmounted,
type PropType,
type Ref,
ref,
type ShallowRef,
type SlotsType,
Teleport,
useTemplateRef,
watch,
watchEffect,
} from "vue";
const lyricPlayerProps = {
/**
* 是否禁用歌词播放组件,默认为 `false`,歌词组件启用后将会开始逐帧更新歌词的动画效果,并对传入的其他参数变更做出反馈。
*
* 如果禁用了歌词组件动画,你也可以通过引用取得原始渲染组件实例,手动逐帧调用其 `update` 函数来更新动画效果。
*/
disabled: {
type: Boolean,
default: false,
},
/**
* 是否演出部分效果,目前会控制播放间奏点的动画的播放暂停与否,默认为 `true`
*/
playing: {
type: Boolean,
default: true,
},
/**
* 设置歌词行的对齐方式,如果为 `undefined` 则默认为 `center`
*
* - 设置成 `top` 的话将会向目标歌词行的顶部对齐
* - 设置成 `bottom` 的话将会向目标歌词行的底部对齐
* - 设置成 `center` 的话将会向目标歌词行的垂直中心对齐
*/
alignAnchor: {
type: String as PropType<"top" | "bottom" | "center">,
default: "center",
},
/**
* 设置默认的歌词行对齐位置,相对于整个歌词播放组件的大小位置,如果为 `undefined`
* 则默认为 `0.5`
*
* 可以设置一个 `[0.0-1.0]` 之间的任意数字,代表组件高度由上到下的比例位置
*/
alignPosition: {
type: Number,
default: 0.5,
},
/**
* 设置是否使用物理弹簧算法实现歌词动画效果,默认启用
*
* 如果启用,则会通过弹簧算法实时处理歌词位置,但是需要性能足够强劲的电脑方可流畅运行
*
* 如果不启用,则会回退到基于 `transition` 的过渡效果,对低性能的机器比较友好,但是效果会比较单一
*/
enableSpring: {
type: Boolean,
default: true,
},
/**
* 设置是否启用歌词行的模糊效果,默认为 `true`
*/
enableBlur: {
type: Boolean,
default: true,
},
/**
* 设置是否使用物理弹簧算法实现歌词动画效果,默认启用
*
* 如果启用,则会通过弹簧算法实时处理歌词位置,但是需要性能足够强劲的电脑方可流畅运行
*
* 如果不启用,则会回退到基于 `transition` 的过渡效果,对低性能的机器比较友好,但是效果会比较单一
*/
enableScale: {
type: Boolean,
default: true,
},
/**
* 设置是否隐藏已经播放过的歌词行,默认不隐藏
*/
hidePassedLines: {
type: Boolean,
default: false,
},
/**
* 设置歌词中不雅用语的掩码模式,默认为 `MaskObsceneWordsMode.Disabled`,即不掩码
*/
maskObsceneWordsMode: {
type: String as PropType<MaskObsceneWordsMode>,
default: MaskObsceneWordsMode.Disabled,
},
/**
* 设置歌词优化选项
*/
optimizeOptions: {
type: Object as PropType<OptimizeLyricOptions>,
required: false,
},
/**
* 设置当前播放歌词,要注意传入后这个数组内的信息不得修改,否则会发生错误
*/
lyricLines: {
type: Object as PropType<LyricLine[]>,
required: false,
},
/**
* 设置当前播放进度,单位为毫秒且**必须是整数**,此时将会更新内部的歌词进度信息
* 内部会根据调用间隔和播放进度自动决定如何滚动和显示歌词,所以这个的调用频率越快越准确越好
*/
currentTime: {
type: Number,
default: 0,
},
/**
* 设置文字动画的渐变宽度,单位以歌词行的主文字字体大小的倍数为单位,默认为 0.5,即一个全角字符的一半宽度
*
* 如果要模拟 Apple Music for Android 的效果,可以设置为 1
*
* 如果要模拟 Apple Music for iPad 的效果,可以设置为 0.5
*
* 如果想要近乎禁用渐变效果,可以设置成非常接近 0 的小数(例如 `0.0001` ),但是**不可以为 0**
*/
wordFadeWidth: {
type: Number,
default: 0.5,
},
/**
* 设置所有歌词行在横坐标上的弹簧属性,包括重量、弹力和阻力。
*
* @param params 需要设置的弹簧属性,提供的属性将会覆盖原来的属性,未提供的属性将会保持原样
*/
linePosXSpringParams: {
type: Object as PropType<Partial<spring.SpringParams>>,
required: false,
},
/**
* 设置所有歌词行在​纵坐标上的弹簧属性,包括重量、弹力和阻力。
*
* @param params 需要设置的弹簧属性,提供的属性将会覆盖原来的属性,未提供的属性将会保持原样
*/
linePosYSpringParams: {
type: Object as PropType<Partial<spring.SpringParams>>,
required: false,
},
/**
* 设置所有歌词行在​缩放大小上的弹簧属性,包括重量、弹力和阻力。
*
* @param params 需要设置的弹簧属性,提供的属性将会覆盖原来的属性,未提供的属性将会保持原样
*/
lineScaleSpringParams: {
type: Object as PropType<Partial<spring.SpringParams>>,
required: false,
},
/**
* 设置渲染器,如果为 `undefined` 则默认为 `MeshGradientRenderer`
* 默认渲染器有可能会随着版本更新而更换
*/
lyricPlayer: {
type: Object as PropType<{
new (...args: ConstructorParameters<typeof BaseRenderer>): BaseRenderer;
}>,
required: false,
},
} as const;
/**
* 歌词播放组件的属性
*/
export type LyricPlayerProps = ExtractPublicPropTypes<typeof lyricPlayerProps>;
const lyricPlayerEmits = {
lineClick: (_: LyricLineMouseEvent) => true,
lineContextmenu: (_: LyricLineMouseEvent) => true,
} as const;
/**
* 歌词播放组件的事件
*/
export type LyricPlayerEmits = typeof lyricPlayerEmits;
/**
* 歌词播放组件的引用
*/
export interface LyricPlayerRef {
/**
* 歌词播放实例
*/
lyricPlayer: Ref<LyricPlayerBase | undefined>;
/**
* 将歌词播放实例的元素包裹起来的 DIV 元素实例
*/
wrapperEl: Readonly<ShallowRef<HTMLDivElement | null>>;
}
export const LyricPlayer = defineComponent({
name: "LyricPlayer",
props: lyricPlayerProps,
emits: lyricPlayerEmits,
slots: Object as SlotsType<{
"bottom-line": () => void;
}>,
setup(props, { expose, emit, attrs, slots }) {
const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper-ref");
const playerRef = ref<CoreLyricPlayer>();
const lineClickHandler = (e: Event) =>
emit("lineClick", e as LyricLineMouseEvent);
const lineContextMenuHandler = (e: Event) =>
emit("lineContextmenu", e as LyricLineMouseEvent);
onMounted(() => {
const wrapper = wrapperRef.value;
if (wrapper) {
playerRef.value = new CoreLyricPlayer();
wrapper.appendChild(playerRef.value.getElement());
playerRef.value.addEventListener("line-click", lineClickHandler);
playerRef.value.addEventListener(
"line-contextmenu",
lineContextMenuHandler,
);
}
});
onUnmounted(() => {
if (playerRef.value) {
playerRef.value.removeEventListener("line-click", lineClickHandler);
playerRef.value.removeEventListener(
"line-contextmenu",
lineContextMenuHandler,
);
playerRef.value.dispose();
}
});
watchEffect((onCleanup) => {
if (!props.disabled) {
let canceled = false;
let lastTime = -1;
const onFrame = (time: number) => {
if (canceled) return;
if (lastTime === -1) {
lastTime = time;
}
playerRef.value?.update(time - lastTime);
lastTime = time;
requestAnimationFrame(onFrame);
};
requestAnimationFrame(onFrame);
onCleanup(() => {
canceled = true;
});
}
});
watchEffect(() => {
if (props.playing !== undefined) {
if (props.playing) {
playerRef.value?.resume();
} else {
playerRef.value?.pause();
}
} else playerRef.value?.resume();
});
watchEffect(() => {
if (props.alignAnchor !== undefined)
playerRef.value?.setAlignAnchor(props.alignAnchor);
});
watchEffect(() => {
if (props.hidePassedLines !== undefined)
playerRef.value?.setHidePassedLines(props.hidePassedLines);
});
watchEffect(() => {
if (props.maskObsceneWordsMode !== undefined)
playerRef.value?.setMaskObsceneWords(props.maskObsceneWordsMode);
else playerRef.value?.setMaskObsceneWords(MaskObsceneWordsMode.Disabled);
});
watchEffect(() => {
if (props.alignPosition !== undefined)
playerRef.value?.setAlignPosition(props.alignPosition);
});
watchEffect(() => {
if (props.enableSpring !== undefined)
playerRef.value?.setEnableSpring(props.enableSpring);
else playerRef.value?.setEnableSpring(true);
});
watchEffect(() => {
if (props.enableBlur !== undefined)
playerRef.value?.setEnableBlur(props.enableBlur);
else playerRef.value?.setEnableBlur(true);
});
watchEffect(() => {
if (props.enableScale !== undefined)
playerRef.value?.setEnableScale(props.enableScale);
else playerRef.value?.setEnableScale(true);
});
watch(
[playerRef, () => props.lyricLines, () => props.optimizeOptions],
([player, lyricLines, optimizeOptions]) => {
if (!player) return;
if (optimizeOptions !== undefined) {
player.setOptimizeOptions(optimizeOptions);
}
if (lyricLines !== undefined) {
player.setLyricLines(lyricLines);
} else {
player.setLyricLines([]);
}
if (props.currentTime !== undefined) {
player.setCurrentTime(props.currentTime, true);
}
},
{ immediate: true },
);
watchEffect(() => {
if (props.currentTime !== undefined)
playerRef.value?.setCurrentTime(props.currentTime);
});
watchEffect(() => {
if (props.wordFadeWidth !== undefined)
playerRef.value?.setWordFadeWidth(props.wordFadeWidth);
});
watchEffect(() => {
if (props.linePosXSpringParams !== undefined)
playerRef.value?.setLinePosXSpringParams(props.linePosXSpringParams);
});
watchEffect(() => {
if (props.linePosYSpringParams !== undefined)
playerRef.value?.setLinePosYSpringParams(props.linePosYSpringParams);
});
watchEffect(() => {
if (props.lineScaleSpringParams !== undefined)
playerRef.value?.setLineScaleSpringParams(props.lineScaleSpringParams);
});
const bottomLineEl = computed(() =>
playerRef.value?.getBottomLineElement(),
);
expose<LyricPlayerRef>({
lyricPlayer: playerRef,
wrapperEl: wrapperRef,
});
return () => (
<div ref="wrapper-ref" {...attrs}>
{bottomLineEl.value && (
<Teleport to={bottomLineEl.value}>
{slots["bottom-line"]?.()}
</Teleport>
)}
</div>
);
},
});

View File

@@ -0,0 +1,2 @@
export * from "./BackgroundRender";
export * from "./LyricPlayer";

View File

@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
// 让 tsgo 解析 dist 下的类型声明而非源码,避免在 core/src 下生成 .d.ts 文件
"paths": {
"@applemusic-like-lyrics/core": ["../core/dist/amll-core.d.mts"]
}
}
}

View File

@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src"],
"compilerOptions": {
// vue 的类型系统非常复杂,基本无法手动定义出来
"isolatedDeclarations": false,
"jsxImportSource": "vue",
"jsx": "preserve"
}
}

View File

@@ -0,0 +1,17 @@
import pluginBabel from "@rolldown/plugin-babel";
import { defineConfig } from "tsdown";
import { baseConfig } from "../../tsdown.base.ts";
export default defineConfig({
...baseConfig,
entry: { "amll-vue": "./src/index.ts" },
dts: {
tsgo: true,
tsconfig: "./tsconfig.dts.json",
},
plugins: [
pluginBabel({
plugins: ["@vue/babel-plugin-jsx"],
}),
],
});