refactor(directory): use VoiceElement instead of useAudio

This commit is contained in:
Haoyu Xu
2023-03-16 19:05:31 -04:00
parent f3aa867e00
commit 9b3237f46f
15 changed files with 324 additions and 157 deletions

View File

@@ -1 +1 @@
3.4.1
3.4.20

View File

@@ -1,4 +1,6 @@
showcase:
2023/03/15:
- Added background music
2023/03/03:
- Performance optimization
2023/02/26:
@@ -38,10 +40,12 @@ showcase:
2021/05/26:
- First commit
directory:
2023/03/15:
- Added background music
2023/03/03:
- Fixed Voice and VCs issues
- Fixed Voice and VLs issues
2023/03/02:
- Added Voice, VCs, Operator Logo
- Added Voice, VLs, Operator Logo
2023/03/01:
- Added Operator page
- Added Changelogs page

View File

@@ -34,6 +34,7 @@ localization:
ui_privacy_do_not_track: Send usage data
ui_music_title: <hr><h4>📝 Music</h4><hr>
ui_music_notice: <span><b>Please adjust the 'Offset' value if you notice audio cutoff</b></span>
ui_music_notice1: <span><b>'bg_rhodes_day.png' and 'operator_bg.png' use the same music, it is not a bug.</b></span>
ui_music_selection: Music
ui_music_volume: Volume
ui_music_offset: Offset
@@ -71,6 +72,7 @@ localization:
ui_privacy_do_not_track: 发送使用数据
ui_music_title: <hr><h4>🎵 音乐</h4><hr>
ui_music_notice: <span><b>如若发现音频截止,请调节 '弥补' 数值</b></span>
ui_music_notice1: <span><b>'bg_rhodes_day.png' 和 'operator_bg.png' 使用同样的音乐并非Bug</b></span>
ui_music_selection: 音乐
ui_music_volume: 音量
ui_music_offset: 弥补
@@ -233,6 +235,10 @@ properties:
value:
text: ui_music_notice
condition: music_title.value == true
- key: music_notice1
value:
text: ui_music_notice1
condition: music_title.value == true
- key: music_selection
value:
text: ui_music_selection

View File

@@ -5,3 +5,5 @@ VITE_DIRECTORY_FOLDER="_assets"
VITE_BACKGROUND_FOLDER="background"
VITE_AVAILABLE_OPERATORS=["chen","dusk","dusk_everything_is_a_miracle","ling","nearl","nian","nian_unfettered_freedom","phatom_focus","rosmontis","skadi","skadi_sublimation","w","w_wonder","specter","gavial","surtr_colorful_wonderland","lee_trust_your_eyes","texas_the_omertosa","nearl_relight","rosmontis_become_anew","passager_dream_in_a_moment","mizuki_summer_feast","chongyue","ling_it_does_wash_the_strings","pozemka_snowy_plains_in_words"]
VITE_ERROR_FILES={"files":[{"key":"build_char_128_plosis_epoque%233","paddings":{"left":-120,"right":150,"top":10,"bottom":0}},{"key":"build_char_128_plosis","paddings":{"left":-90,"right":100,"top":10,"bottom":0}}],"voice":"CN_034.ogg"}
VITE_MUSIC_FOLDER=music
VITE_MUSIC_MAPPING={"operator_bg.png":{"intro":"m_sys_void_intro.ogg","loop":"m_sys_void_loop.ogg"},"bg_anniversary_1.png":{"intro":"m_dia_nightoflongmen_intro.ogg","loop":"m_dia_nightoflongmen_loop.ogg"},"bg_iberia_1.png":{"intro":"m_sys_act18d3d0_intro.ogg","loop":"m_sys_act18d3d0_loop.ogg"},"bg_kazimierz_1.png":{"intro":"m_dia_street_intro.ogg","loop":"m_dia_street_loop.ogg"},"bg_main_victoria_1.png":{"intro":"m_avg_ghosthunter_intro.ogg","loop":"m_avg_ghosthunter_loop.ogg"},"bg_rhodes_day.png":{"intro":"m_sys_void_intro.ogg","loop":"m_sys_void_loop.ogg"},"bg_rhodes_night.png":{"intro":"m_sys_tech_intro.ogg","loop":"m_sys_tech_loop.ogg"},"bg_rogue_1.png":{"intro":null,"loop":"m_avg_rglk1secretevent_loop.ogg"},"bg_siesta_1.png":{"intro":"m_sys_ddd_intro.ogg","loop":"m_sys_ddd_loop.ogg"},"bg_ursus_1.png":{"intro":"m_avg_loneliness_intro.ogg","loop":"m_avg_loneliness_loop.ogg"},"bg_yan_1.png":{"intro":null,"loop":"m_sys_bitw_loop.ogg"}}

View File

@@ -1 +1 @@
1.0.18
1.0.22

View File

@@ -16,7 +16,11 @@ export default function Switch(props) {
return (
<section
className={`${classes.switch} ${on ? classes.active : ''}`}
onClick={() => props.handleOnClick()}
onClick={() => {
if (props.handleOnClick) {
props.handleOnClick(!on)
}
}}
>
<span className={classes.text}>{i18n(props.text)}</span>
<section className={classes.wrapper}>

View File

@@ -0,0 +1,53 @@
import React, {
useEffect,
useRef,
} from "react"
import PropTypes from 'prop-types';
export default function VoiceElement({
src,
replay,
handleAduioStateChange,
}) {
const audioRef = useRef(null)
useEffect(() => {
if (src) {
audioRef.current.src = src
audioRef.current.play()
} else {
audioRef.current.pause()
}
}, [src])
useEffect(() => {
if (replay) {
audioRef.current.currentTime = 0
audioRef.current.play()
}
}, [replay])
return (
<audio
ref={audioRef}
preload="auto"
autoPlay
onEnded={(e) => {
if (handleAduioStateChange) handleAduioStateChange(e, 'ended')
}}
onPlay={(e) => {
if (handleAduioStateChange) handleAduioStateChange(e, 'play')
}}
onPause={(e) => {
if (handleAduioStateChange) handleAduioStateChange(e, 'pause')
}}
>
<source type="audio/ogg" />
</audio>
)
}
VoiceElement.propTypes = {
src: PropTypes.string,
handleAduioStateChange: PropTypes.func,
replay: PropTypes.bool,
}

View File

@@ -1,69 +0,0 @@
import {
useState,
useEffect,
useRef,
} from "react"
import { useCallback } from "react"
const audioEl = new Audio()
let lastSrc = ''
export default function useAudio() {
const [isPlaying, _setIsPlaying] = useState(false)
const isPlayingRef = useRef(isPlaying)
const setIsPlaying = (data) => {
isPlayingRef.current = data
_setIsPlaying(data)
}
useEffect(() => {
audioEl.addEventListener('ended', () => setIsPlaying(false))
return () => {
audioEl.removeEventListener('ended', () => setIsPlaying(false))
}
}, [])
const play = useCallback(
(
link,
options = {
overwrite: false
},
callback = () => { }
) => {
if (!options.overwrite && link === lastSrc) return
audioEl.src = link
let startPlayPromise = audioEl.play()
if (startPlayPromise !== undefined) {
setIsPlaying(true)
startPlayPromise
.then(() => {
lastSrc = link
callback()
return
})
.catch((e) => {
console.log(e)
return
})
}
}, [])
const stop = useCallback(() => {
audioEl.pause()
setIsPlaying(false)
}, [])
const getSrc = useCallback(() => audioEl.src, [])
const resetSrc = useCallback(() => {
audioEl.src = ''
}, [])
return {
play,
stop,
getSrc,
resetSrc,
isPlaying,
isPlayingRef,
}
}

View File

@@ -17,7 +17,7 @@ import Switch from '@/component/switch';
import ReturnButton from "@/component/return_button";
import { Typewriter } from 'react-simple-typewriter'
import { useHeader } from '@/state/header';
import useAudio from '@/libs/voice';
import VoiceElement from '@/component/voice';
import spine from '!/libs/spine-player'
import '!/libs/spine-player.css'
import useUmami from '@parcellab/react-use-umami';
@@ -27,6 +27,7 @@ const config = JSON.parse(import.meta.env.VITE_ERROR_FILES)
const obj = config.files[Math.floor((Math.random() * config.files.length))]
const filename = obj.key.replace("#", "%23")
const padding = obj.paddings
let lastVoiceState = 'ended'
export default function Error() {
// eslint-disable-next-line no-unused-vars
@@ -40,9 +41,11 @@ export default function Error() {
const [spineDone, _setSpineDone] = useState(false)
const spineRef = useRef(null)
const [spineData, setSpineData] = useState(null)
const { play, stop } = useAudio()
const spineDoneRef = useRef(spineDone)
const voiceOnRef = useRef(voiceOn)
const [voiceSrc, setVoiceSrc] = useState(null)
const [voiceReplay, setVoiceReplay] = useState(false)
const [spinePlayer, setSpinePlayer] = useState(null)
const setSpineDone = (data) => {
spineDoneRef.current = data
@@ -60,26 +63,40 @@ export default function Error() {
useEffect(() => {
setTitle(content[0])
stop()
}, [content, setTitle, stop])
}, [content, setTitle])
useEffect(() => {
if (!voiceOn) {
stop()
setVoiceSrc(null)
} else {
setVoiceSrc(`/${import.meta.env.VITE_DIRECTORY_FOLDER}/error.ogg`)
if (spinePlayer) {
spinePlayer.animationState.setAnimation(0, "Interact", false, 0);
spinePlayer.animationState.addAnimation(0, "Relax", true, 0);
}
}, [voiceOn, stop])
}
}, [voiceOn])
useEffect(() => {
voiceOnRef.current = voiceOn
}, [voiceOn])
const playVoice = useCallback(() => {
play(`/${import.meta.env.VITE_DIRECTORY_FOLDER}/error.ogg`, { overwrite: true })
}, [play])
if (lastVoiceState === 'ended' && voiceSrc !== null) {
setVoiceReplay(true)
}
}, [voiceSrc])
const handleAduioStateChange = useCallback((e, state) => {
lastVoiceState = state
if (state === 'ended') {
setVoiceReplay(false)
}
}, [])
useEffect(() => {
if (spineRef.current?.children.length === 0 && spineData) {
new spine.SpinePlayer(spineRef.current, {
setSpinePlayer(new spine.SpinePlayer(spineRef.current, {
skelUrl: `./assets/${filename}.skel`,
atlasUrl: `./assets/${filename}.atlas`,
rawDataURIs: spineData,
@@ -99,7 +116,7 @@ export default function Error() {
showControls: false,
touch: false,
fps: 60,
defaultMix: 0,
defaultMix: 0.3,
success: (player) => {
let isPlayingInteract = false
player.animationState.addListener({
@@ -127,7 +144,7 @@ export default function Error() {
ani()
}
}
})
}))
}
}, [playVoice, spineData]);
@@ -163,6 +180,11 @@ export default function Error() {
className={`${classes.spine} ${spineDone ? classes.active : ''}`}
ref={spineRef}
/>
<VoiceElement
src={voiceSrc}
replay={voiceReplay}
handleAduioStateChange={handleAduioStateChange}
/>
</main>
</section>
);

View File

@@ -15,7 +15,7 @@ import {
} from '@/state/language'
import { useHeader } from '@/state/header';
import { useAppbar } from '@/state/appbar';
import useAudio from '@/libs/voice';
import VoiceElement from '@/component/voice';
import { useAtom } from 'jotai'
import { atomWithStorage } from 'jotai/utils';
import CharIcon from '@/component/char_icon';
@@ -24,6 +24,7 @@ import useUmami from '@parcellab/react-use-umami';
import Switch from '@/component/switch';
const voiceOnAtom = atomWithStorage('voiceOn', false)
let lastVoiceState = 'ended'
export default function Home() {
// eslint-disable-next-line no-unused-vars
@@ -36,7 +37,9 @@ export default function Home() {
} = useHeader()
const { config } = useConfig()
const [content, setContent] = useState([])
const { stop } = useAudio()
const [voiceOn] = useAtom(voiceOnAtom)
const [voiceSrc, setVoiceSrc] = useState(null)
const [voiceReplay, setVoiceReplay] = useState(false)
useEffect(() => {
setTitle('dynamic_compile')
@@ -48,15 +51,33 @@ export default function Home() {
key: 'skin'
}])
setHeaderIcon(null)
stop()
}, [setHeaderIcon, setTabs, setTitle, stop])
}, [setHeaderIcon, setTabs, setTitle])
useEffect(() => {
setContent(config?.operators || [])
}, [config])
const handleAduioStateChange = useCallback((e, state) => {
lastVoiceState = state
if (state === 'ended') {
setVoiceReplay(false)
}
}, [])
const isShown = useCallback((type) => currentTab === 'all' || currentTab === type, [currentTab])
const handleVoicePlay = useCallback((src) => {
if (!voiceOn) {
setVoiceSrc(null)
} else {
if (src === voiceSrc && lastVoiceState === 'ended') {
setVoiceReplay(true)
} else {
setVoiceSrc(src)
}
}
}, [voiceOn, voiceSrc])
return (
<section>
{
@@ -71,6 +92,7 @@ export default function Home() {
key={item.link}
item={item}
hidden={!isShown(item.type)}
handleVoicePlay={handleVoicePlay}
/>
)
})}
@@ -81,27 +103,17 @@ export default function Home() {
)
})
}
<VoiceSwitchElement />
<VoiceSwitchElement
src={voiceSrc}
handleAduioStateChange={handleAduioStateChange}
replay={voiceReplay}
/>
</section>
)
}
function OperatorElement({ item, hidden }) {
function OperatorElement({ item, hidden, handleVoicePlay }) {
const { textDefaultLang, language, alternateLang } = useLanguage()
const { play, stop, resetSrc } = useAudio()
const [voiceOn] = useAtom(voiceOnAtom)
const playVoice = useCallback(() => {
if (!voiceOn) return
play(`/${item.link}/assets/${JSON.parse(import.meta.env.VITE_VOICE_FOLDERS).main}/${import.meta.env.VITE_APP_VOICE_URL}`)
}, [voiceOn, play, item.link])
useEffect(() => {
if (!voiceOn) {
stop()
resetSrc()
}
}, [voiceOn, stop, resetSrc])
return useMemo(() => {
return (
@@ -111,7 +123,7 @@ function OperatorElement({ item, hidden }) {
hidden={hidden}
>
<section
onMouseEnter={() => playVoice()}
onMouseEnter={() => handleVoicePlay(`/${item.link}/assets/${JSON.parse(import.meta.env.VITE_VOICE_FOLDERS).main}/${import.meta.env.VITE_APP_VOICE_URL}`)}
>
<section className={classes['background-filler']} />
<section className={classes.outline} />
@@ -141,10 +153,10 @@ function OperatorElement({ item, hidden }) {
</section>
</NavLink>
)
}, [item, hidden, language, alternateLang, textDefaultLang, playVoice])
}, [item, hidden, language, alternateLang, textDefaultLang, handleVoicePlay])
}
function VoiceSwitchElement() {
function VoiceSwitchElement({ src, replay, handleAduioStateChange }) {
const [voiceOn, setVoiceOn] = useAtom(voiceOnAtom)
const {
setExtraArea,
@@ -163,7 +175,19 @@ function VoiceSwitchElement() {
])
}, [voiceOn, setExtraArea, setVoiceOn])
return null
return (
<VoiceElement
src={src}
replay={replay}
handleAduioStateChange={handleAduioStateChange}
/>
)
}
VoiceSwitchElement.propTypes = {
src: PropTypes.string,
replay: PropTypes.bool,
handleAduioStateChange: PropTypes.func,
}
function ImageElement({ item }) {

View File

@@ -18,19 +18,23 @@ import {
import { useHeader } from '@/state/header';
import { useAppbar } from '@/state/appbar';
import { useBackgrounds } from '@/state/background';
import useAudio from '@/libs/voice';
import VoiceElement from '@/component/voice';
import useUmami from '@parcellab/react-use-umami'
import spine from '!/libs/spine-player'
import '!/libs/spine-player.css'
import Border from '@/component/border';
import { useI18n } from '@/state/language';
import Switch from '@/component/switch';
import { atom, useAtom } from 'jotai'
const musicMapping = JSON.parse(import.meta.env.VITE_MUSIC_MAPPING)
const getVoiceFoler = (lang) => {
const folderObject = JSON.parse(import.meta.env.VITE_VOICE_FOLDERS)
const voiceFolder = folderObject.sub.find(e => e.lang === lang) || folderObject.sub.find(e => e.name === 'custom')
return `${folderObject.main}/${voiceFolder.name}`
}
const defaultSpineAnimation = 'Idle'
const backgroundAtom = atom(null)
const getTabName = (item, language) => {
if (item.type === 'operator') {
@@ -61,16 +65,18 @@ export default function Operator() {
const [spinePlayer, setSpinePlayer] = useState(null)
const [voiceLang, _setVoiceLang] = useState(null)
const { backgrounds } = useBackgrounds()
const [currentBackground, setCurrentBackground] = useState(null)
const [currentBackground, setCurrentBackground] = useAtom(backgroundAtom)
const [voiceConfig, setVoiceConfig] = useState(null)
const [subtitleLang, setSubtitleLang] = useState(null)
const [hideSubtitle, setHideSubtitle] = useState(true)
const { play, stop, getSrc, isPlaying, isPlayingRef } = useAudio()
const [subtitleObj, _setSubtitleObj] = useState(null)
const [currentVoiceId, setCurrentVoiceId] = useState(null)
const voiceLangRef = useRef(voiceLang)
const subtitleObjRef = useRef(subtitleObj)
const configRef = useRef(config)
const [voiceSrc, setVoiceSrc] = useState(null)
const [isVoicePlaying, _setIsVoicePlaying] = useState(false)
const isVoicePlayingRef = useRef(isVoicePlaying)
const setVoiceLang = (value) => {
voiceLangRef.current = value
@@ -82,18 +88,18 @@ export default function Operator() {
_setSubtitleObj(value)
}
const setIsVoicePlaying = (value) => {
isVoicePlayingRef.current = value
_setIsVoicePlaying(value)
}
useEffect(() => {
setExtraArea([])
stop()
}, [setExtraArea, stop])
}, [setExtraArea])
useEffect(() => {
if (!voiceLang) stop()
}, [stop, voiceLang])
useEffect(() => {
if (backgrounds) setCurrentBackground(backgrounds[0])
}, [backgrounds])
if (backgrounds.length > 0) setCurrentBackground(backgrounds[0])
}, [backgrounds, setCurrentBackground])
useEffect(() => {
setSpineData(null)
@@ -175,7 +181,7 @@ export default function Operator() {
showControls: false,
touch: false,
fps: 60,
defaultMix: 0,
defaultMix: 0.3,
success: (player) => {
let lastVoiceId = null
let currentVoiceId = null
@@ -189,17 +195,13 @@ export default function Operator() {
const id = voiceId()
currentVoiceId = id
setCurrentVoiceId(id)
play(
`/${configRef.current.link}/assets/${getVoiceFoler(voiceLangRef.current)}/${id}.ogg`,
() => {
setVoiceSrc(`/${configRef.current.link}/assets/${getVoiceFoler(voiceLangRef.current)}/${id}.ogg`)
lastVoiceId = currentVoiceId
}
)
}
}
}))
}
}, [config, spineData, setSpinePlayer, spineAnimation, play]);
}, [config, spineData, spineAnimation]);
useEffect(() => {
if (voiceConfig && voiceLang) {
@@ -212,13 +214,24 @@ export default function Operator() {
}
}, [subtitleLang, voiceConfig, voiceLang])
const handleAduioStateChange = useCallback((e, state) => {
switch (state) {
case 'play':
setIsVoicePlaying(true)
break
default:
setIsVoicePlaying(false)
break
}
}, [])
useEffect(() => {
if (subtitleLang) {
if (isPlaying) {
if (isVoicePlaying) {
setHideSubtitle(false)
} else {
const autoHide = () => {
if (isPlayingRef.current) return
if (isVoicePlayingRef.current) return
setHideSubtitle(true)
}
setTimeout(autoHide, 5 * 1000)
@@ -229,29 +242,35 @@ export default function Operator() {
} else {
setHideSubtitle(true)
}
}, [subtitleLang, isPlaying, isPlayingRef])
}, [subtitleLang, isVoicePlaying])
useEffect(() => {
if (voiceLang && isPlaying) {
if (voiceLang && isVoicePlaying) {
const audioUrl = `/assets/${getVoiceFoler(voiceLang)}/${currentVoiceId}.ogg`
if (getSrc() !== (window.location.href.replace(/\/$/g, '') + audioUrl)) {
play(`/${config.link}${audioUrl}`)
if (voiceSrc !== (window.location.href.replace(/\/$/g, '') + audioUrl)) {
setVoiceSrc(`/${config.link}${audioUrl}`)
}
}
}, [voiceLang, isPlaying, currentVoiceId, config, getSrc, play])
}, [voiceLang, isVoicePlaying, currentVoiceId, config, voiceSrc])
const playAnimationVoice = useCallback((animation) => {
if (voiceLangRef.current) {
let id = null
if (animation === 'Idle') id = 'CN_011'
if (animation === 'Interact') id = 'CN_034'
if (animation === 'Special') id = 'CN_042'
if (id) {
setCurrentVoiceId(id)
setVoiceSrc(`/${key}/assets/${getVoiceFoler(voiceLangRef.current)}/${id}.ogg`)
}
}
}, [key])
useEffect(() => {
if (voiceLang && config) {
let id = ''
if (spineAnimation === 'Idle') id = 'CN_011'
if (spineAnimation === 'Interact') id = 'CN_034'
if (spineAnimation === 'Special') id = 'CN_042'
setCurrentVoiceId(id)
play(
`/${config.link}/assets/${getVoiceFoler(voiceLang)}/${id}.ogg`
)
if (!voiceLang) {
setVoiceSrc(null)
}
}, [voiceLang, config, spineAnimation, play])
}, [voiceLang])
const spineSettings = [
{
@@ -260,8 +279,10 @@ export default function Operator() {
{
name: 'idle',
onClick: () => {
spinePlayer.animationState.setAnimation(0, "Idle", true, 0)
setSpineAnimation('Idle')
const animation = "Idle"
playAnimationVoice(animation)
spinePlayer.animationState.setAnimation(0, animation, true, 0)
setSpineAnimation(animation)
},
activeRule: () => {
return spineAnimation === 'Idle'
@@ -269,8 +290,10 @@ export default function Operator() {
}, {
name: 'interact',
onClick: () => {
spinePlayer.animationState.setAnimation(0, "Interact", true, 0)
setSpineAnimation('Interact')
const animation = "Interact"
playAnimationVoice(animation)
spinePlayer.animationState.setAnimation(0, animation, true, 0)
setSpineAnimation(animation)
},
activeRule: () => {
return spineAnimation === 'Interact'
@@ -278,8 +301,10 @@ export default function Operator() {
}, {
name: 'special',
onClick: () => {
spinePlayer.animationState.setAnimation(0, "Special", true, 0)
setSpineAnimation('Special')
const animation = "Special"
playAnimationVoice(animation)
spinePlayer.animationState.setAnimation(0, animation, true, 0)
setSpineAnimation(animation)
},
activeRule: () => {
return spineAnimation === 'Special'
@@ -297,6 +322,9 @@ export default function Operator() {
} else {
setVoiceLang(null)
}
if (!isVoicePlayingRef.current) {
playAnimationVoice(spineAnimation)
}
},
activeRule: () => {
return voiceLang === item
@@ -320,6 +348,9 @@ export default function Operator() {
}
}
}) || []
}, {
name: 'music',
el: <MusicElement />
}, {
name: 'backgrounds',
options: backgrounds.map((item) => {
@@ -348,6 +379,13 @@ export default function Operator() {
}}>
{
spineSettings.map((item) => {
if (item.el) {
return (
<section key={item.name}>
{item.el}
</section>
)
}
if (item.options.length === 0) return null
return (
<section key={item.name}>
@@ -417,7 +455,7 @@ export default function Operator() {
</section>
</section>
<section className={classes.container} style={currentBackground && {
backgroundImage: `url(/${key}/assets/${import.meta.env.VITE_BACKGROUND_FOLDER}/${currentBackground})`
backgroundImage: `url(/chen/assets/${import.meta.env.VITE_BACKGROUND_FOLDER}/${currentBackground})`
}} >
{
config && (
@@ -437,6 +475,80 @@ export default function Operator() {
</section>
</section>
<Border />
<VoiceElement
src={voiceSrc}
handleAduioStateChange={handleAduioStateChange}
/>
</section>
)
}
function MusicElement() {
const [enableMusic, setEnableMusic] = useState(false)
const { i18n } = useI18n()
const musicIntroRef = useRef(null)
const musicLoopRef = useRef(null)
const [background,] = useAtom(backgroundAtom)
useEffect(() => {
if (musicIntroRef.current && musicIntroRef.current) {
musicIntroRef.current.volume = 0.5
musicLoopRef.current.volume = 0.5
}
}, [musicIntroRef, musicLoopRef])
useEffect(() => {
if (!enableMusic || background) {
musicIntroRef.current.pause()
musicLoopRef.current.pause()
}
}, [enableMusic, background])
useEffect(() => {
if (background && enableMusic) {
const introOgg = musicMapping[background].intro
const intro = `./chen/assets/${import.meta.env.VITE_MUSIC_FOLDER}/${introOgg}`
const loop = `./chen/assets/${import.meta.env.VITE_MUSIC_FOLDER}/${musicMapping[background].loop}`
musicLoopRef.current.src = loop
if (introOgg) {
musicIntroRef.current.src = intro || loop
} else {
musicLoopRef.current.play()
}
}
}, [background, enableMusic])
const handleIntroTimeUpdate = useCallback(() => {
if (musicIntroRef.current.currentTime >= musicIntroRef.current.duration - 0.3) {
musicIntroRef.current.pause()
musicLoopRef.current.play()
}
}, [])
const handleLoopTimeUpdate = useCallback(() => {
if (musicLoopRef.current.currentTime >= musicLoopRef.current.duration - 0.3) {
musicLoopRef.current.currentTime = 0
musicLoopRef.current.play()
}
}, [])
return (
<section>
<section
className={classes.titleWithSwitch}
onClick={() => setEnableMusic(!enableMusic)}
>
<section className={classes.text}>{i18n('music')}</section>
<section className={classes.switch}>
<Switch on={enableMusic} />
</section>
</section>
<audio ref={musicIntroRef} preload="auto" autoPlay onTimeUpdate={() => handleIntroTimeUpdate()}>
<source type="audio/ogg" />
</audio>
<audio ref={musicLoopRef} preload="auto" onTimeUpdate={() => handleLoopTimeUpdate()}>
<source type="audio/ogg"/>
</audio>
</section>
)
}

View File

@@ -22,6 +22,7 @@
padding-top: 10rem;
font-size: 3rem;
gap: 2rem;
padding-bottom: 0;
}
.spine {
@@ -38,6 +39,7 @@
@media (max-width: 768px) {
.main {
padding-top: 6rem;
max-height: calc(100vh - 6rem);
}
.content {
font-size: 2rem;
@@ -46,6 +48,7 @@
@media (max-width: 480px) {
.main {
padding-top: 4rem;
max-height: calc(100vh - 4rem);
}
.content {
font-size: 1.5rem;

View File

@@ -5,7 +5,7 @@ import { read } from './yaml.js';
import AssetsProcessor from './assets_processor.js'
import EnvGenerator from './env_generator.js'
export default function ({ backgrounds }) {
export default function ({ backgrounds, musicMapping }) {
const extractedFolder = path.join(__projectRoot, __config.folder.operator, '_directory')
const targetFolder = path.join(__projectRoot, __config.folder.release, __config.folder.directory);
const sourceFolder = path.join(__projectRoot, __config.folder.operator);
@@ -78,6 +78,12 @@ export default function ({ backgrounds }) {
}, {
key: "error_files",
value: JSON.stringify(__config.directory.error).replace('#', '%23')
}, {
key: "music_folder",
value: __config.folder.music
}, {
key: "music_mapping",
value: JSON.stringify(musicMapping)
}
]), path.join(__projectRoot, 'directory', '.env'))

View File

@@ -76,7 +76,7 @@ export default class Music {
}
success() {
this.changeMusic(window.settings.currentBackground)
if (this.#currentMusic === null) this.changeMusic(window.settings.currentBackground)
}
changeMusic(name) {

View File

@@ -93,10 +93,10 @@ window.wallpaperPropertyListener = {
}
if (properties.music_title) {
window.music.useMusic = properties.music_title.value
window.music.changeMusic(properties.music_selection.value)
window.settings.functionInsights("useMusic", Object.keys(properties) !== 1)
}
if (properties.music_selection) {
// TODO: not working
window.music.changeMusic(properties.music_selection.value)
window.settings.functionInsights("music_selection", Object.keys(properties) !== 1)
}