feat(directory): add voice

This commit is contained in:
Haoyu Xu
2023-02-27 16:34:36 -05:00
parent 00e6916ad1
commit 0a320e1eb0
19 changed files with 399 additions and 123 deletions

View File

@@ -14,6 +14,7 @@
font-size: 1.5rem;
letter-spacing: 0.1rem;
flex: auto;
user-select: none;
}
.home .item-group .item {
@@ -41,7 +42,7 @@
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
}
.home .item-group .item .item-outline{
.home .item-group .item .item-outline {
width: 100%;
height: 100%;
left: -6px;
@@ -86,11 +87,17 @@
background-color: var(--home-item-hover-background-color);
}
.home .item-group .item .item-img .live2d {
width: 100%;
height: 360px;
}
.home .item-group .item .item-info {
white-space: nowrap;
position: relative;
padding: 0.8rem 0.4rem;
line-height: 1;
line-height: 1.2em;
height: 36px;
}
.home .item-group .item .item-info .item-title-container {
@@ -109,11 +116,15 @@
}
.home .item-group .item .item-info .item-title-container .item-title {
line-height: 1.1em;
height: 1.25rem;
line-height: 1.3em;
height: auto;
}
.home .item-group .item .item-info .item-title-container .item-type {
display: flex;
flex-direction: row;
align-items: baseline;
text-align: center;
width: 1.5rem;
fill: var(--text-color)
}

View File

@@ -2,10 +2,11 @@ import {
useState,
useEffect,
useContext,
useMemo
useRef
} from 'react'
import {
Link,
useOutletContext,
} from "react-router-dom";
import './home.css'
import { ConfigContext } from '@/context/useConfigContext';
@@ -13,14 +14,19 @@ import { LanguageContext } from '@/context/useLanguageContext';
import { HeaderContext } from '@/context/useHeaderContext';
import CharIcon from '@/component/char_icon';
import MainBorder from '@/component/main_border';
import useUmami from '@parcellab/react-use-umami'
import useUmami from '@parcellab/react-use-umami';
import Switch from '@/component/switch';
import spine from '!/libs/spine-player';
import '!/libs/spine-player.css';
export default function Home() {
const _trackEvt = useUmami('/')
const { setAppbarExtraArea } = useOutletContext()
const {
setTitle,
tabs, setTabs,
currentTab, setCurrentTab } = useContext(HeaderContext)
setTabs,
currentTab, setCurrentTab
} = useContext(HeaderContext)
const { config } = useContext(ConfigContext)
const {
language,
@@ -29,6 +35,12 @@ export default function Home() {
i18n
} = useContext(LanguageContext)
const [content, setContent] = useState([])
const [voiceOn, setVoiceOn] = useState(false)
const [live2dOn, setLive2dOn] = useState(false)
const [audioUrl, setAudioUrl] = useState('')
const audioEl = new Audio(audioUrl)
const live2dRefObject = useRef({})
const live2dSpineObject = useRef({})
useEffect(() => {
setTitle('dynamic_compile')
@@ -48,20 +60,100 @@ export default function Home() {
}, [])
useEffect(() => {
const value = config.reduce((acc, cur) => {
const date = cur.date
if (acc[date]) {
acc[date].push(cur)
} else {
acc[date] = [cur]
}
return acc
}, {})
setContent(Object.values(value).sort((a, b) => new Date(b[0].date) - new Date(a[0].date)))
setContent(config?.operators || [])
}, [config])
const toggleVoice = () => {
setVoiceOn(!voiceOn)
setAudioUrl('')
}
useEffect(() => {
setAppbarExtraArea([
(
<Switch
key="voice"
text={i18n.key.voice[language]}
on={voiceOn}
handleOnClick={() => toggleVoice()}
/>
// ), (
// <Switch
// key="live2d"
// text={i18n.key.live2d[language]}
// on={live2dOn}
// handleOnClick={() => setLive2dOn(!live2dOn)}
// />
)
])
}, [voiceOn, live2dOn])
const isShown = (type) => currentTab === 'all' || currentTab === type
const playVoice = (link) => {
if (!voiceOn) return
audioEl.src = `/${link}/assets/voice/${import.meta.env.VITE_APP_VOICE_URL}`
let startPlayPromise = audioEl.play()
if (startPlayPromise !== undefined) {
startPlayPromise
.then(() => {
return
})
.catch(() => {
return
})
}
}
const getLive2d = (link) => {
const reactEl = <section className="live2d" ref={ref => {
live2dRefObject.current[link] = ref
}}></section>
return reactEl
}
useEffect(() => {
if (live2dOn) {
Object.keys(live2dRefObject.current).forEach((link) => {
const ref = live2dRefObject.current[link]
if (ref) {
if (live2dSpineObject.current[link]) {
return
}
fetch(`/_assets/dyn_portrait_char_2014_nian_nian%234.json`)
.then((res) => res.json())
.then((data) => {
live2dSpineObject.current[link] = new spine.SpinePlayer(ref, {
skelUrl: `./assets/dyn_portrait_char_2014_nian_nian%234.skel`,
atlasUrl: `./assets/dyn_portrait_char_2014_nian_nian%234.atlas`,
rawDataURIs: data,
animation: "Idle",
premultipliedAlpha: true,
alpha: true,
backgroundColor: "#00000000",
viewport: {
debugRender: false,
padLeft: `0%`,
padRight: `0%`,
padTop: `0%`,
padBottom: `0%`,
x: 0,
y: 0,
},
showControls: false,
touch: false,
fps: 60,
defaultMix: 0,
success: function (widget) {
},
})
})
}
})
}
}, [live2dOn])
return (
<section className="home">
{
@@ -71,31 +163,44 @@ export default function Home() {
<section className="item-group-wrapper" key={v[0].date} hidden={length === 0}>
<section className="item-group">
{v.map(item => {
return (<Link reloadDocument to={`/${item.link}`} className="item" key={item.link} hidden={!isShown(item.type)}>
<section className="item-background-filler" />
<section className="item-outline" />
<section className="item-img">
<img src={`/${item.link}/assets/${item.fallback_name.replace("#", "%23")}_portrait.png`} alt={item.codename[language]} />
</section>
<section className="item-info">
<section className="item-title-container">
<section className="item-title">{item.codename[language]}</section>
<section className="item-type">
<CharIcon
type={item.type}
viewBox={
item.type === 'operator' ? '0 0 88.969 71.469' : '0 0 94.563 67.437'
} />
return (
<Link
reloadDocument
to={`/${item.link}`}
className="item"
key={item.link}
hidden={!isShown(item.type)}
onMouseEnter={(e) => playVoice(item.link)}
>
<section className="item-background-filler" />
<section className="item-outline" />
<section className="item-img">
{live2dOn && item.portrait !== null ? (
getLive2d(item.link)
) : (
<img src={`/${item.link}/assets/${item.fallback_name.replace("#", "%23")}_portrait.png`} alt={item.codename[language]} />
)}
</section>
<section className="item-info">
<section className="item-title-container">
<section className="item-title">{item.codename[language]}</section>
<section className="item-type">
<CharIcon
type={item.type}
viewBox={
item.type === 'operator' ? '0 0 88.969 71.469' : '0 0 94.563 67.437'
} />
</section>
</section>
<section className="item-text-wrapper">
<span className='item-text'>{item.codename[language.startsWith("en") ? alternateLang : textDefaultLang]}</span>
</section>
<section className="item-info-background" style={{
color: item.color
}} />
</section>
<section className="item-text-wrapper">
<span className='item-text'>{item.codename[language.startsWith("en") ? alternateLang : textDefaultLang]}</span>
</section>
<section className="item-info-background" style={{
color: item.color
}} />
</section>
</Link>)
</Link>
)
})}
<section className='item-group-date'>{v[0].date}</section>
</section>