forked from miao-moe/QZMusic_PC
feat:
- 引入@applemusiclikelyrics/lyric - 优化播放页返回按钮和拖拽
This commit is contained in:
Binary file not shown.
8
package-lock.json
generated
8
package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@applemusic-like-lyrics/core": "^0.2.0",
|
"@applemusic-like-lyrics/core": "^0.2.0",
|
||||||
|
"@applemusic-like-lyrics/lyric": "^0.3.0",
|
||||||
"@applemusic-like-lyrics/vue": "^0.2.0",
|
"@applemusic-like-lyrics/vue": "^0.2.0",
|
||||||
"@iconify/vue": "^5.0.0",
|
"@iconify/vue": "^5.0.0",
|
||||||
"@pixi/app": "^7.4.3",
|
"@pixi/app": "^7.4.3",
|
||||||
@@ -63,6 +64,13 @@
|
|||||||
"@pixi/sprite": "*"
|
"@pixi/sprite": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@applemusic-like-lyrics/lyric": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@applemusic-like-lyrics/lyric/-/lyric-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-ZGyBheZZfqjQmGnEUciNKCARwkqIP39ONZirJE+NOQjQ47TYlZz4tlLBRH/uRfq5qviYyJ1S9Q+pZxlYoyHWVw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "GPL-3.0"
|
||||||
|
},
|
||||||
"node_modules/@applemusic-like-lyrics/vue": {
|
"node_modules/@applemusic-like-lyrics/vue": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@applemusic-like-lyrics/vue/-/vue-0.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@applemusic-like-lyrics/vue/-/vue-0.2.0.tgz",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@applemusic-like-lyrics/core": "^0.2.0",
|
"@applemusic-like-lyrics/core": "^0.2.0",
|
||||||
|
"@applemusic-like-lyrics/lyric": "^0.3.0",
|
||||||
"@applemusic-like-lyrics/vue": "^0.2.0",
|
"@applemusic-like-lyrics/vue": "^0.2.0",
|
||||||
"@iconify/vue": "^5.0.0",
|
"@iconify/vue": "^5.0.0",
|
||||||
"@pixi/app": "^7.4.3",
|
"@pixi/app": "^7.4.3",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="fullscreen-player" :class="{ active: isPlayerFullScreen }">
|
<div class="fullscreen-player" :class="{ active: isPlayerFullScreen }">
|
||||||
<div class="player-content">
|
<div class="player-content">
|
||||||
<div class="dismiss-area" @click="toggleFullScreen">
|
<div class="dismiss-area" @click="toggleFullScreen">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
<svg class="dismiss-button" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<polyline points="6 9 12 15 18 9"></polyline>
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -181,8 +181,8 @@ const BackgroundRender = defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (props.playing) bgRenderRef.value?.pause();
|
if (props.playing) bgRenderRef.value?.resume();
|
||||||
else bgRenderRef.value?.resume();
|
else bgRenderRef.value?.pause();
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
@@ -224,6 +224,7 @@ const loudness = computed(() => playerStore.loudness);
|
|||||||
|
|
||||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
||||||
let animationId: number | null = null;
|
let animationId: number | null = null;
|
||||||
|
let currentData: number[] = new Array(32).fill(0); // For temporal smoothing
|
||||||
|
|
||||||
const drawSpectrum = () => {
|
const drawSpectrum = () => {
|
||||||
if (!canvasRef.value) return;
|
if (!canvasRef.value) return;
|
||||||
@@ -231,7 +232,7 @@ const drawSpectrum = () => {
|
|||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
|
|
||||||
// Adjust canvas size to display resolution
|
// Adjust canvas size
|
||||||
const dpr = window.devicePixelRatio || 1;
|
const dpr = window.devicePixelRatio || 1;
|
||||||
const rect = canvas.getBoundingClientRect();
|
const rect = canvas.getBoundingClientRect();
|
||||||
canvas.width = rect.width * dpr;
|
canvas.width = rect.width * dpr;
|
||||||
@@ -240,28 +241,72 @@ const drawSpectrum = () => {
|
|||||||
|
|
||||||
const width = rect.width;
|
const width = rect.width;
|
||||||
const height = rect.height;
|
const height = rect.height;
|
||||||
const barCount = 32;
|
|
||||||
const barWidth = (width / barCount) * 0.5; // Gap = Bar Width
|
|
||||||
const gap = (width - (barCount * barWidth)) / (barCount + 1);
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, width, height);
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
const data = playerStore.spectrum;
|
const targetData = playerStore.spectrum;
|
||||||
if (!data || data.length === 0) return;
|
if (!targetData) return;
|
||||||
|
|
||||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
|
// 1. Temporal Smoothing (LERP)
|
||||||
|
// Adjust speed (0.1 - 0.3) for desired smoothness
|
||||||
for (let i = 0; i < Math.min(data.length, barCount); i++) {
|
const lerpSpeed = 0.15;
|
||||||
const value = data[i];
|
for (let i = 0; i < 32; i++) {
|
||||||
const barHeight = value * height * 0.6; // Scale down a bit
|
const target = targetData[i] || 0;
|
||||||
const x = gap + i * (barWidth + gap);
|
currentData[i] = currentData[i] + (target - currentData[i]) * lerpSpeed;
|
||||||
const y = height - barHeight;
|
|
||||||
|
|
||||||
// Draw rounded bar
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.roundRect(x, y, barWidth, barHeight, 4);
|
|
||||||
ctx.fill();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Draw Smooth Wave
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, height);
|
||||||
|
|
||||||
|
const pointCount = currentData.length;
|
||||||
|
// Use a subset if desired, but 32 is fine.
|
||||||
|
// We want the wave to be centered or span the width.
|
||||||
|
const step = width / (pointCount - 1);
|
||||||
|
|
||||||
|
// Start point
|
||||||
|
let x = 0;
|
||||||
|
let y = height - (currentData[0] * height * 0.5); // Scale 0.5
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
|
||||||
|
for (let i = 0; i < pointCount - 1; i++) {
|
||||||
|
const xCurr = i * step;
|
||||||
|
const yCurr = height - (currentData[i] * height * 0.5);
|
||||||
|
|
||||||
|
const xNext = (i + 1) * step;
|
||||||
|
const yNext = height - (currentData[i + 1] * height * 0.5);
|
||||||
|
|
||||||
|
// Control point for quadratic curve (midpoint)
|
||||||
|
const xMid = (xCurr + xNext) / 2;
|
||||||
|
const yMid = (yCurr + yNext) / 2;
|
||||||
|
|
||||||
|
ctx.quadraticCurveTo(xCurr, yCurr, xMid, yMid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to last point
|
||||||
|
const lastX = (pointCount - 1) * step;
|
||||||
|
const lastY = height - (currentData[pointCount - 1] * height * 0.5);
|
||||||
|
ctx.lineTo(lastX, lastY);
|
||||||
|
|
||||||
|
// Close path to bottom
|
||||||
|
ctx.lineTo(width, height);
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
// Style
|
||||||
|
const gradient = ctx.createLinearGradient(0, height - 200, 0, height);
|
||||||
|
gradient.addColorStop(0, 'rgba(255, 255, 255, 0.4)');
|
||||||
|
gradient.addColorStop(1, 'rgba(255, 255, 255, 0.05)');
|
||||||
|
|
||||||
|
ctx.fillStyle = gradient;
|
||||||
|
|
||||||
|
// Add blur effect
|
||||||
|
ctx.shadowBlur = 20;
|
||||||
|
ctx.shadowColor = 'rgba(255, 255, 255, 0.5)';
|
||||||
|
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Reset shadow for next frame (performance)
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const loop = () => {
|
const loop = () => {
|
||||||
@@ -334,16 +379,19 @@ const toggleFullScreen = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dismiss-area {
|
.dismiss-area {
|
||||||
padding: 24px;
|
padding: 2.5%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: rgba(255, 255, 255, 0.5);
|
color: rgba(255, 255, 255, 0.5);
|
||||||
transition: color 0.2s;
|
transition: color 0.2s;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
}
|
||||||
|
.dismiss-button {
|
||||||
|
-webkit-app-region: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dismiss-area:hover {
|
.dismiss-area:hover {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,18 @@ const testSong: Song = {
|
|||||||
source: 'wy',
|
source: 'wy',
|
||||||
type: 'Remote'
|
type: 'Remote'
|
||||||
};
|
};
|
||||||
|
const testSong2: Song = {
|
||||||
|
id: '1979007503',
|
||||||
|
name: '一半一半',
|
||||||
|
artist: '洛天依Official',
|
||||||
|
picUrl: 'http://p1.music.126.net/G02hs1vJYJir359bx8wGhg==/109951167851086939.jpg?param=130y130',
|
||||||
|
url: '',
|
||||||
|
duration: '02:43',
|
||||||
|
source: 'wy',
|
||||||
|
type: 'Remote'
|
||||||
|
}
|
||||||
|
|
||||||
// Auto Play
|
// Auto Play
|
||||||
playerStore.playSong(testSong).then(() => {
|
playerStore.playSong(testSong).then(() => {
|
||||||
playerStore.setPlaylist([testSong]);
|
playerStore.setPlaylist([testSong,testSong2]);
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user