mirror of
https://github.com/imsyy/home.git
synced 2025-05-22 22:20:14 +09:00
进行一些改进
1.修复 pinia 无法重置变量的问题。 2.修复 weather.vue 中的某些错误。 3.补全遗漏的语音提示。 4.逐字样式修改,添加逐字上色增强效果,但默认不开启,因为这坨山还有 N 个 BUG 要修,仅仅能跑,一点不稳定。 5.修复(?)歌词解析时因 API 返回玄学内容造成的异常。 6.逐字解析逻辑修改。 ... More fixes needed
This commit is contained in:
parent
dd81d31748
commit
7d02b4da23
24
package.json
24
package.json
@ -18,15 +18,15 @@
|
|||||||
"aplayer": "^1.10.1",
|
"aplayer": "^1.10.1",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"element-plus": "^2.8.7",
|
"element-plus": "^2.8.8",
|
||||||
"fetch-jsonp": "^1.3.0",
|
"fetch-jsonp": "^1.3.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"pinia": "^2.2.5",
|
"pinia": "^2.2.6",
|
||||||
"pinia-plugin-persistedstate": "^4.1.2",
|
"pinia-plugin-persistedstate": "^4.1.3",
|
||||||
"pinia-plugin-persistedstate-2": "^2.0.24",
|
"pinia-plugin-persistedstate-2": "^2.0.27",
|
||||||
"swiper": "^11.1.14",
|
"swiper": "^11.1.15",
|
||||||
"three": "^0.170.0",
|
"three": "^0.170.0",
|
||||||
"vue": "^3.5.12"
|
"vue": "^3.5.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@icon-park/vue-next": "^1.4.2",
|
"@icon-park/vue-next": "^1.4.2",
|
||||||
@ -36,15 +36,15 @@
|
|||||||
"@vicons/material": "^0.12.0",
|
"@vicons/material": "^0.12.0",
|
||||||
"@vicons/tabler": "^0.12.0",
|
"@vicons/tabler": "^0.12.0",
|
||||||
"@vicons/utils": "^0.1.4",
|
"@vicons/utils": "^0.1.4",
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^5.2.0",
|
||||||
"eslint": "^9.14.0",
|
"eslint": "^9.15.0",
|
||||||
"eslint-plugin-vue": "^9.30.0",
|
"eslint-plugin-vue": "^9.31.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"sass": "^1.80.6",
|
"sass": "^1.81.0",
|
||||||
"terser": "^5.36.0",
|
"terser": "^5.36.0",
|
||||||
"unplugin-auto-import": "^0.18.3",
|
"unplugin-auto-import": "^0.18.5",
|
||||||
"unplugin-vue-components": "^0.27.4",
|
"unplugin-vue-components": "^0.27.4",
|
||||||
"vite": "^5.4.10",
|
"vite": "^5.4.11",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-pwa": "^0.20.5"
|
"vite-plugin-pwa": "^0.20.5"
|
||||||
}
|
}
|
||||||
|
1008
pnpm-lock.yaml
generated
1008
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -47,7 +47,7 @@ const changeBg = (type) => {
|
|||||||
bgUrl.value = "https://api.vvhan.com/api/wallpaper/views";
|
bgUrl.value = "https://api.vvhan.com/api/wallpaper/views";
|
||||||
} else if (type == 3) {
|
} else if (type == 3) {
|
||||||
bgUrl.value = "https://api.vvhan.com/api/wallpaper/acg";
|
bgUrl.value = "https://api.vvhan.com/api/wallpaper/acg";
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 图片加载完成
|
// 图片加载完成
|
||||||
|
@ -41,7 +41,11 @@
|
|||||||
:key="store.playerLrc.length != 0 ? `lrc-line-${store.playerLrc[0][2]}` : `lrc-line-null`">
|
:key="store.playerLrc.length != 0 ? `lrc-line-${store.playerLrc[0][2]}` : `lrc-line-null`">
|
||||||
<music-one theme="filled" size="18" fill="#efefef" />
|
<music-one theme="filled" size="18" fill="#efefef" />
|
||||||
<span class="yrc-box">
|
<span class="yrc-box">
|
||||||
<span class="yrc-1 lrc-text text-hidden">
|
<span class="yrc-2 lrc-text text-hidden" id="yrc-2-wrap">
|
||||||
|
<span v-for="i in store.playerLrc" :key="`lrc-over-char-${i[2]}-${i[3]}`" v-html="i[4]">
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="yrc-1 lrc-text text-hidden" id="yrc-1-wrap">
|
||||||
<span v-for="i in store.playerLrc" :key="`lrc-char-${i[2]}-${i[3]}`"
|
<span v-for="i in store.playerLrc" :key="`lrc-char-${i[2]}-${i[3]}`"
|
||||||
:style="`opacity: ${i[1] ? '1' : '0.6'}`"
|
:style="`opacity: ${i[1] ? '1' : '0.6'}`"
|
||||||
:class="`yrc-char ${i[0] ? 'fade-in' : 'fade-in-start'} ${i[0] > 1.5 ? 'long-tone' : 'fade-in-start'}`"
|
:class="`yrc-char ${i[0] ? 'fade-in' : 'fade-in-start'} ${i[0] > 1.5 ? 'long-tone' : 'fade-in-start'}`"
|
||||||
@ -87,14 +91,14 @@ const siteUrl = computed(() => {
|
|||||||
// 判断协议前缀
|
// 判断协议前缀
|
||||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||||
return "//" + url;
|
return "//" + url;
|
||||||
}
|
};
|
||||||
return url;
|
return url;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
// 逐字模块1
|
||||||
.yrc-char {
|
.yrc-char {
|
||||||
// 逐字部分
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
transform: translateY(1px);
|
transform: translateY(1px);
|
||||||
@ -174,8 +178,32 @@ const siteUrl = computed(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 逐字模块2
|
||||||
|
#yrc-2-wrap>span {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#yrc-2-wrap {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
width: auto;
|
||||||
|
opacity: 0.5;
|
||||||
|
text-shadow: 0 0 4px rgba(255, 255, 255, 0.8);
|
||||||
|
font-family: MiSans-Regular;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition:
|
||||||
|
opacity 0.3s linear,
|
||||||
|
color 0.5s linear,
|
||||||
|
transform 0.3s linear,
|
||||||
|
width 0.3s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 逐行部分
|
||||||
.lrc-char {
|
.lrc-char {
|
||||||
// 逐行部分
|
|
||||||
display: inline;
|
display: inline;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
@ -187,6 +215,8 @@ const siteUrl = computed(() => {
|
|||||||
color 0.5s linear;
|
color 0.5s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// End
|
||||||
|
|
||||||
#footer {
|
#footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -196,7 +226,7 @@ const siteUrl = computed(() => {
|
|||||||
line-height: 46px;
|
line-height: 46px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
font-size: 14px;
|
font-size: 16px;
|
||||||
// 文字不换行
|
// 文字不换行
|
||||||
word-break: keep-all;
|
word-break: keep-all;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -211,6 +241,8 @@ const siteUrl = computed(() => {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
z-index: 1;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
.lrc-all {
|
.lrc-all {
|
||||||
width: 98%;
|
width: 98%;
|
||||||
@ -218,6 +250,7 @@ const siteUrl = computed(() => {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
.lrc-text {
|
.lrc-text {
|
||||||
margin: 0 8px;
|
margin: 0 8px;
|
||||||
@ -250,9 +283,6 @@ const siteUrl = computed(() => {
|
|||||||
.yrc-2 {
|
.yrc-2 {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
overflow: hidden;
|
|
||||||
float: left;
|
|
||||||
text-align: left;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ const siteLinksList = computed(() => {
|
|||||||
for (let i = 0; i < siteLinks.length; i += 6) {
|
for (let i = 0; i < siteLinks.length; i += 6) {
|
||||||
const subArr = siteLinks.slice(i, i + 6);
|
const subArr = siteLinks.slice(i, i + 6);
|
||||||
result.push(subArr);
|
result.push(subArr);
|
||||||
}
|
};
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ const jumpLink = (data) => {
|
|||||||
if (typeof $openList === "function") $openList();
|
if (typeof $openList === "function") $openList();
|
||||||
} else {
|
} else {
|
||||||
window.open(data.link, "_blank");
|
window.open(data.link, "_blank");
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ const siteUrl = computed(() => {
|
|||||||
if (url.startsWith("http://") || url.startsWith("https://")) {
|
if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||||
const urlFormat = url.replace(/^(https?:\/\/)/, "");
|
const urlFormat = url.replace(/^(https?:\/\/)/, "");
|
||||||
return urlFormat.split(".");
|
return urlFormat.split(".");
|
||||||
}
|
};
|
||||||
return url.split(".");
|
return url.split(".");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ const changeBox = () => {
|
|||||||
const vstyle = import.meta.env.VITE_TTS_Style;
|
const vstyle = import.meta.env.VITE_TTS_Style;
|
||||||
SpeechLocal("分辨率不足.mp3");
|
SpeechLocal("分辨率不足.mp3");
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听状态变化
|
// 监听状态变化
|
||||||
@ -95,7 +95,7 @@ watch(
|
|||||||
} else {
|
} else {
|
||||||
descriptionText.hello = import.meta.env.VITE_DESC_HELLO;
|
descriptionText.hello = import.meta.env.VITE_DESC_HELLO;
|
||||||
descriptionText.text = import.meta.env.VITE_DESC_TEXT;
|
descriptionText.text = import.meta.env.VITE_DESC_TEXT;
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
@ -13,10 +13,8 @@ import APlayer from "@worstone/vue-aplayer";
|
|||||||
import { Speech, stopSpeech, SpeechLocal } from "@/utils/speech";
|
import { Speech, stopSpeech, SpeechLocal } from "@/utils/speech";
|
||||||
import { decodeYrc } from "../utils/decodeYrc";
|
import { decodeYrc } from "../utils/decodeYrc";
|
||||||
|
|
||||||
let lastTimestamp = Date.now();
|
|
||||||
let webglRenderer;
|
|
||||||
|
|
||||||
const store = mainStore();
|
const store = mainStore();
|
||||||
|
let lastTimestamp = Date.now();
|
||||||
|
|
||||||
// 获取播放器 DOM
|
// 获取播放器 DOM
|
||||||
const player = ref(null);
|
const player = ref(null);
|
||||||
@ -175,8 +173,12 @@ const onPause = () => {
|
|||||||
store.setPlayerState(player.value.audioRef.paused);
|
store.setPlayerState(player.value.audioRef.paused);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let nowLineStart = -1
|
||||||
// 音频时间更新事件
|
// 音频时间更新事件
|
||||||
function showYrc() {
|
function showYrc() {
|
||||||
|
// 至于为什么所有源的逐字都叫 YRC 呢...因为逐字功能本来是打算写网易云音乐独占的,但好像有些偏心了(bushi)...看了一下 qrc 和 yrc 没什么大区别,就顺带捏一起了。但是就懒得改变量名了!
|
||||||
|
try {
|
||||||
|
// 至于为什么要 try 呢?问得好!因为网易云接口时不时会返回一些令人费解的东西,比如没有时间轴、时间轴为负数([20720,-4200])、时间轴乱码,这些东西会造成模块直接卡死,除非刷新页面。暂时没有那么多纠错逻辑,为了防止模块死掉,就先加个 try 在这里复活自己。咕咕咕!
|
||||||
if (player.value == null) {
|
if (player.value == null) {
|
||||||
return requestAnimationFrame(showYrc);
|
return requestAnimationFrame(showYrc);
|
||||||
}
|
}
|
||||||
@ -213,12 +215,20 @@ function showYrc() {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// 接入 AMLL TTML Database
|
// 接入 AMLL TTML Database
|
||||||
const songIdMatch = yrcUrl.match(/netease.*?id=(.*?)&/);
|
const songUrlInf = new URLSearchParams(new URL(yrcUrl).search)
|
||||||
const songId = songIdMatch ? songIdMatch[1] : null;
|
const songId = songUrlInf.get('id')
|
||||||
|
const songServer = songUrlInf.get("server");
|
||||||
if (!songId) {
|
if (!songId) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const amllUrl = `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.yrc`;
|
const songUrlInfUrl = {
|
||||||
|
'netease': `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.yrc`,
|
||||||
|
'tencent': `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.qrc`
|
||||||
|
};
|
||||||
|
if (!['netease', 'tencent'].includes(songServer)) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const amllUrl = songUrlInfUrl[songServer]
|
||||||
return fetch(amllUrl)
|
return fetch(amllUrl)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@ -245,15 +255,22 @@ function showYrc() {
|
|||||||
}
|
}
|
||||||
let lrc = lyrics[lyricIndex][1];
|
let lrc = lyrics[lyricIndex][1];
|
||||||
if (lrc === "Loading") {
|
if (lrc === "Loading") {
|
||||||
lrc = "歌词加载中";
|
lrc = "歌词加载中...";
|
||||||
} else if (lrc === "Not available") {
|
} else if (lrc === "Not available") {
|
||||||
if (store.playerYrcATDB) {
|
if (store.playerYrcATDB) {
|
||||||
// 哈哈哈又是你()
|
// 哈哈哈又是你()
|
||||||
const lrcUrlw = aplayer.audio[aplayer.index]["lrc"];
|
const songUrlInfw = new URLSearchParams(new URL(yrcUrl).search)
|
||||||
const songIdMatchlrc = lrcUrlw.match(/netease.*?id=(.*?)&/);
|
const songIdlrc = songUrlInfw.get('id')
|
||||||
const songIdlrc = songIdMatchlrc ? songIdMatchlrc[1] : null;
|
const songServerlrc = songUrlInfw.get("server");
|
||||||
if (songIdlrc) {
|
if (songIdlrc) {
|
||||||
const amllUrllrc = `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songIdlrc}.lrc`;
|
const songUrlInfwurl = {
|
||||||
|
'netease': `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songIdlrc}.lrc`,
|
||||||
|
'tencent': `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songIdlrc}.lrc`
|
||||||
|
};
|
||||||
|
if (!['netease', 'tencent'].includes(songServerlrc)) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const amllUrllrc = songUrlInfwurl[songServerlrc].replace('${songIdlrc}', songIdlrc);
|
||||||
fetch(amllUrllrc)
|
fetch(amllUrllrc)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status === 404 || !response.ok) {
|
if (response.status === 404 || !response.ok) {
|
||||||
@ -274,51 +291,80 @@ function showYrc() {
|
|||||||
const output = [[true, 1, lyricIndex, 0, lrc]];
|
const output = [[true, 1, lyricIndex, 0, lrc]];
|
||||||
if (store.playerLrc.toString() != output.toString()) {
|
if (store.playerLrc.toString() != output.toString()) {
|
||||||
store.setPlayerLrc(output);
|
store.setPlayerLrc(output);
|
||||||
}
|
};
|
||||||
return requestAnimationFrame(showYrc);
|
return requestAnimationFrame(showYrc);
|
||||||
}
|
};
|
||||||
// 逐字模块
|
// 逐字模块
|
||||||
if (store.playerYrcShowPro) {
|
|
||||||
if (!webglRenderer) {
|
|
||||||
const canvas = document.getElementById("lyricsCanvas");
|
|
||||||
webglRenderer = new WebGLLyricsRenderer(canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const now = player.value.audioStatus.playedTime * 1000;
|
const now = player.value.audioStatus.playedTime * 1000;
|
||||||
const yrcFiltered = store.yrcTemp.filter((i) => i[0] < now);
|
const yrcFiltered = store.yrcTemp.filter((i) => i[0] < now);
|
||||||
const yrc1 = document.querySelector(".yrc-1")
|
let animationTmp = [];
|
||||||
if (yrc1 == null) {
|
|
||||||
return requestAnimationFrame(showYrc);
|
|
||||||
}
|
|
||||||
const yrcLyric =
|
const yrcLyric =
|
||||||
yrcFiltered.length > 0
|
yrcFiltered.length > 0
|
||||||
? yrcFiltered.splice(-1)[0][2].map((it) => {
|
? yrcFiltered.slice(-1)[0][2].map((it) => {
|
||||||
const [[start, duration], word, line, row] = it;
|
const [[start, duration], word, line, row] = it;
|
||||||
const isCurrent = now >= start && now <= start + duration;
|
const isCurrent = now >= start && now <= start + duration;
|
||||||
const isSungLyrics = start + duration < now;
|
const isSungLyrics = start + duration < now;
|
||||||
if (!isCurrent) {
|
|
||||||
return [isCurrent, isSungLyrics, line, row, word, "auto"];
|
return [isCurrent, isSungLyrics, line, row, word, "auto"];
|
||||||
}
|
|
||||||
const thisDom = yrc1.querySelector(`#lrc-char-${line}-${row}`)
|
|
||||||
if (thisDom == null) {
|
|
||||||
return [isCurrent, isSungLyrics, line, row, word, "auto"];
|
|
||||||
}
|
|
||||||
const x = thisDom.offsetWidth * (now - start) / duration
|
|
||||||
if (x == null || x == NaN) {
|
|
||||||
return [isCurrent, isSungLyrics, line, row, word, "auto"];
|
|
||||||
}
|
|
||||||
return [isCurrent, isSungLyrics, line, row, word, `${x}px`]
|
|
||||||
})
|
})
|
||||||
: [[true, 1, 0, 0, `${store.playerTitle} - ${store.playerArtist}`]];
|
: [[true, 1, 0, 0, `${store.playerTitle} - ${store.playerArtist}`]];
|
||||||
|
|
||||||
if (store.playerLrc.toString() != yrcLyric.toString()) {
|
if (store.playerLrc.toString() != yrcLyric.toString()) {
|
||||||
store.setPlayerLrc(yrcLyric);
|
store.setPlayerLrc(yrcLyric);
|
||||||
}
|
};
|
||||||
if (store.playerYrcShowPro) {
|
if (store.playerYrcShowPro) {
|
||||||
webglRenderer.render(yrcLyric);
|
// 这个增强模块虽然能跑,但还有 N 个漏洞要修。对于这些概率性事件,还毫无头绪()
|
||||||
}
|
if (yrcFiltered.length === 0) {
|
||||||
|
return requestAnimationFrame(showYrc);
|
||||||
|
};
|
||||||
|
const lineStart = yrcFiltered.slice(-1)[0][0];
|
||||||
|
if (nowLineStart == lineStart) {
|
||||||
|
return requestAnimationFrame(showYrc);
|
||||||
|
};
|
||||||
|
const yrc2 = document.getElementsByClassName("yrc-box")[0];
|
||||||
|
if (yrc2 == undefined) {
|
||||||
|
return requestAnimationFrame(showYrc);
|
||||||
|
};
|
||||||
|
const outputDom = yrc2.querySelectorAll("#yrc-2-wrap span");
|
||||||
|
const inputDom = yrc2.querySelectorAll("#yrc-1-wrap span");
|
||||||
|
if (inputDom.length == 0 || outputDom.length == 0) {
|
||||||
|
return requestAnimationFrame(showYrc);
|
||||||
|
};
|
||||||
|
const nowLineWord = yrcFiltered.slice(-1)[0][2];
|
||||||
|
for (let i = 0; i < nowLineWord.length; i++) {
|
||||||
|
const [[start, duration], _a, _b, _c] = nowLineWord[i];
|
||||||
|
const intputItem = inputDom[i];
|
||||||
|
if (!intputItem || intputItem.hasAttribute('data-start')) {
|
||||||
|
return requestAnimationFrame(showYrc);
|
||||||
|
};
|
||||||
|
const computedStyle = window.getComputedStyle(intputItem);
|
||||||
|
const width = parseFloat(computedStyle.width);
|
||||||
|
const outputItem = outputDom[i];
|
||||||
|
const animateOptions = {
|
||||||
|
delay: Math.max(0, start - now),
|
||||||
|
duration: duration,
|
||||||
|
fill: "forwards",
|
||||||
|
easing: "linear",
|
||||||
|
};
|
||||||
|
outputItem.style.transform = "translateY(-1px)"
|
||||||
|
const outputAnimate = outputItem.animate(
|
||||||
|
[
|
||||||
|
{ width: 0, },
|
||||||
|
{ width: `${width}px` },
|
||||||
|
],
|
||||||
|
animateOptions,
|
||||||
|
);
|
||||||
|
animationTmp.push(outputAnimate);
|
||||||
|
outputAnimate.onfinish = () => {
|
||||||
|
outputItem.style.transform = "translateY(1px)";
|
||||||
|
animationTmp = animationTmp.filter(a => a !== outputAnimate);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
nowLineStart = yrcFiltered.slice(-1)[0][0];
|
||||||
|
};
|
||||||
requestAnimationFrame(showYrc);
|
requestAnimationFrame(showYrc);
|
||||||
}
|
} catch (error) {
|
||||||
|
requestAnimationFrame(showYrc);
|
||||||
|
};
|
||||||
|
};
|
||||||
requestAnimationFrame(showYrc);
|
requestAnimationFrame(showYrc);
|
||||||
|
|
||||||
// 切换播放暂停事件
|
// 切换播放暂停事件
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
<el-switch v-model="playerYrcShow" inline-prompt :active-icon="CheckSmall" :inactive-icon="CloseSmall" />
|
<el-switch v-model="playerYrcShow" inline-prompt :active-icon="CheckSmall" :inactive-icon="CloseSmall" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="playerLrcShow && playerYrcShow" class="item">
|
<div v-if="playerLrcShow && playerYrcShow" class="item">
|
||||||
<span class="text">逐字效果增强开关(更高的性能要求)</span>
|
<span class="text">逐字效果增强开关</span>
|
||||||
<el-switch v-model="playerYrcShowPro" inline-prompt :active-icon="CheckSmall" :inactive-icon="CloseSmall" />
|
<el-switch v-model="playerYrcShowPro" inline-prompt :active-icon="CheckSmall" :inactive-icon="CloseSmall" />
|
||||||
</div>
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
剩余 {{ item.remaining }} {{ tag === "day" ? "小时" : "天" }}
|
剩余 {{ item.remaining }} {{ tag === "day" ? "小时" : "天" }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<el-progress :text-inside="true" :stroke-width="20" :percentage="parseFloat(item.percentage)" />
|
<el-progress :text-inside="true" :stroke-width="20" :percentage="Number(item.percentage)" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 建站日期 -->
|
<!-- 建站日期 -->
|
||||||
<div v-if="store.siteStartShow" class="capsule-item start">
|
<div v-if="store.siteStartShow" class="capsule-item start">
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { getAdcode, getWeather, getOtherWeather } from "@/api";
|
import { getAdcode, getWeather, getOtherWeather } from "@/api";
|
||||||
import { Error } from "@icon-park/vue-next";
|
import { Error } from "@icon-park/vue-next";
|
||||||
|
import { mainStore } from "@/store";
|
||||||
|
import { Speech, stopSpeech, SpeechLocal } from "@/utils/speech";
|
||||||
|
const store = mainStore();
|
||||||
|
|
||||||
// 高德开发者 Key
|
// 高德开发者 Key
|
||||||
const mainKey = import.meta.env.VITE_WEATHER_KEY;
|
const mainKey = import.meta.env.VITE_WEATHER_KEY;
|
||||||
|
39
src/main.js
39
src/main.js
@ -1,6 +1,7 @@
|
|||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import "@/style/style.scss";
|
import "@/style/style.scss";
|
||||||
import App from "@/App.vue";
|
import App from "@/App.vue";
|
||||||
|
import { mainStore } from "@/store";
|
||||||
import { Speech, stopSpeech, SpeechLocal } from "@/utils/speech";
|
import { Speech, stopSpeech, SpeechLocal } from "@/utils/speech";
|
||||||
// 引入 pinia
|
// 引入 pinia
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
@ -22,30 +23,32 @@ window.addEventListener("beforeunload", () => {
|
|||||||
// 这堆代码原本的意义是在于强制刷新这些本不需要被 pinia 缓存的变量,不知为什么这些变量只会在关闭页面重新输入域名访问才能恢复,导致刷新页面部分模块短时间内出现异常。
|
// 这堆代码原本的意义是在于强制刷新这些本不需要被 pinia 缓存的变量,不知为什么这些变量只会在关闭页面重新输入域名访问才能恢复,导致刷新页面部分模块短时间内出现异常。
|
||||||
// 但是貌似这堆代码也没能解决问题...罢了,先暂且留着叭()
|
// 但是貌似这堆代码也没能解决问题...罢了,先暂且留着叭()
|
||||||
const store = mainStore();
|
const store = mainStore();
|
||||||
store.imgLoadStatus = false; // 壁纸加载状态
|
Object.assign(store, {
|
||||||
store.innerWidth = null; // 当前窗口宽度
|
imgLoadStatus: false, // 壁纸加载状态
|
||||||
store.musicIsOk = false; // 音乐是否加载完成
|
innerWidth: null, // 当前窗口宽度
|
||||||
store.musicOpenState = false; // 音乐面板开启状态
|
musicIsOk: false, // 音乐是否加载完成
|
||||||
store.backgroundShow = false; // 壁纸展示状态
|
musicOpenState: false, // 音乐面板开启状态
|
||||||
store.boxOpenState = false; // 盒子开启状态
|
backgroundShow: false, // 壁纸展示状态
|
||||||
store.mobileOpenState = false; // 移动端开启状态
|
boxOpenState: false, // 盒子开启状态
|
||||||
store.mobileFuncState = false; // 移动端功能区开启状态
|
mobileOpenState: false, // 移动端开启状态
|
||||||
store.setOpenState = false; // 设置页面开启状态
|
mobileFuncState: false, // 移动端功能区开启状态
|
||||||
store.playerState = false; // 当前播放状态
|
setOpenState: false, // 设置页面开启状态
|
||||||
store.playerTitle = null; // 当前播放歌曲名
|
playerState: false, // 当前播放状态
|
||||||
store.playerArtist = null; // 当前播放歌手名
|
playerTitle: null, // 当前播放歌曲名
|
||||||
store.playerLrc = [[true, "猫猫正在翻找歌词..."]]; // 当前播放歌词
|
playerArtist: null, // 当前播放歌手名
|
||||||
store.yrcIndex = -1; // 逐字歌词进度存储
|
playerLrc: [[true, "歌词加载中..."]], // 当前播放歌词
|
||||||
store.yrcTemp = []; // 逐字歌词缓存
|
yrcIndex: -1, // 逐字歌词进度存储
|
||||||
store.yrcEnable = true;
|
yrcTemp: [], // 逐字歌词缓存
|
||||||
store.yrcLoading = false;
|
yrcEnable: true,
|
||||||
|
yrcLoading: false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(pinia);
|
app.use(pinia);
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
|
||||||
// PWA
|
// PWA
|
||||||
navigator.serviceWorker.addEventListener("controllerchange", () => {
|
navigator.serviceWorker.addEventListener("controllerchange", async () => {
|
||||||
// 弹出更新提醒
|
// 弹出更新提醒
|
||||||
console.log("站点已更新,刷新后生效");
|
console.log("站点已更新,刷新后生效");
|
||||||
ElMessage("站点已更新,刷新后生效");
|
ElMessage("站点已更新,刷新后生效");
|
||||||
|
@ -24,10 +24,10 @@ export const mainStore = defineStore("main", {
|
|||||||
playerAutoplay: true, // 是否自动播放
|
playerAutoplay: true, // 是否自动播放
|
||||||
playerLoop: "all", // 循环播放 "all", "one", "none"
|
playerLoop: "all", // 循环播放 "all", "one", "none"
|
||||||
playerOrder: "random", // 循环顺序 "list", "random"
|
playerOrder: "random", // 循环顺序 "list", "random"
|
||||||
webSpeech: false, // 网页语音交互总开关(包含播报歌名功能)
|
webSpeech: true, // 网页语音交互总开关(包含播报歌名功能)
|
||||||
playerSpeechName: true, // 播报歌名
|
playerSpeechName: true, // 播报歌名
|
||||||
playerYrcShow: true, // 逐字歌词解析总开关
|
playerYrcShow: true, // 逐字歌词解析总开关
|
||||||
playerYrcShowPro: false, // 逐字效果增强开关(更高的性能要求)
|
playerYrcShowPro: false, // 逐字效果增强开关
|
||||||
playerYrcATDB: true, // 允许接入 AMLL TTML Database
|
playerYrcATDB: true, // 允许接入 AMLL TTML Database
|
||||||
yrcIndex: -1, // 逐字歌词进度存储
|
yrcIndex: -1, // 逐字歌词进度存储
|
||||||
yrcTemp: [], // 逐字歌词缓存
|
yrcTemp: [], // 逐字歌词缓存
|
||||||
|
@ -110,7 +110,7 @@ export function stopSpeech() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SpeechLocal(fileName) {
|
export function SpeechLocal(fileName) {
|
||||||
// 考虑到生成延迟,所以加了这个,仅必要模块调用 api 实时生成,其它模块使用预先生成好的音频
|
// 考虑到生成延迟,所以加了这个,仅必要模块调用 api 实时生成,其它模块使用预先生成好的音频。记得根据需求更换自己的音频文件哇!
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
reject(new Error("No file name provided"));
|
reject(new Error("No file name provided"));
|
||||||
|
Loading…
Reference in New Issue
Block a user