feat(directory): removed portrait and add cache for image and audio

This commit is contained in:
Haoyu Xu
2023-02-27 23:19:19 -05:00
parent 0a320e1eb0
commit 7edc231e89
41 changed files with 127 additions and 184 deletions

View File

@@ -1,3 +1,3 @@
VITE_APP_TITLE=AKLive2D
VITE_VERSION=0.5.5
VITE_VERSION=0.5.7
VITE_APP_VOICE_URL=jp/CN_037.ogg

View File

@@ -1 +1 @@
0.5.6
0.5.8

View File

@@ -16,7 +16,8 @@ export function HeaderProvider(props) {
i18n
} = useContext(LanguageContext)
const [tabs, setTabs] = useState(null)
const [currentTab, setCurrentTab] = useState(null)
const [currentTab, setCurrentTab] = useState([])
const [appbarExtraArea, setAppbarExtraArea] = useState([])
useEffect(() => {
let newTitle = key
@@ -31,7 +32,8 @@ export function HeaderProvider(props) {
<HeaderContext.Provider value={{
title, setTitle,
tabs, setTabs,
currentTab, setCurrentTab
currentTab, setCurrentTab,
appbarExtraArea, setAppbarExtraArea
}}>
{props.children}
</HeaderContext.Provider>

View File

@@ -0,0 +1,9 @@
import Dexie from 'dexie';
const db = new Dexie('aklive2dDatabase');
db.version(2).stores({
image: '++key, blob',
voice: '++key, blob',
});
export default db;

View File

@@ -51,10 +51,6 @@
"zh-CN": "语音",
"en-US": "Voice"
},
"live2d": {
"zh-CN": "Live2D",
"en-US": "Live2D"
},
"zh-CN": {
"zh-CN": "简体中文",
"en-US": "Chinese (Simplified)"

View File

@@ -87,11 +87,6 @@
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;

View File

@@ -2,11 +2,10 @@ import {
useState,
useEffect,
useContext,
useRef
useCallback
} from 'react'
import {
Link,
useOutletContext,
} from "react-router-dom";
import './home.css'
import { ConfigContext } from '@/context/useConfigContext';
@@ -16,16 +15,17 @@ import CharIcon from '@/component/char_icon';
import MainBorder from '@/component/main_border';
import useUmami from '@parcellab/react-use-umami';
import Switch from '@/component/switch';
import spine from '!/libs/spine-player';
import '!/libs/spine-player.css';
import db from '@/db';
const audioEl = new Audio()
export default function Home() {
const _trackEvt = useUmami('/')
const { setAppbarExtraArea } = useOutletContext()
const {
setTitle,
setTabs,
currentTab, setCurrentTab
currentTab, setCurrentTab,
setAppbarExtraArea
} = useContext(HeaderContext)
const { config } = useContext(ConfigContext)
const {
@@ -36,11 +36,6 @@ export default function Home() {
} = 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')
@@ -65,7 +60,6 @@ export default function Home() {
const toggleVoice = () => {
setVoiceOn(!voiceOn)
setAudioUrl('')
}
useEffect(() => {
@@ -77,82 +71,46 @@ export default function Home() {
on={voiceOn}
handleOnClick={() => toggleVoice()}
/>
// ), (
// <Switch
// key="live2d"
// text={i18n.key.live2d[language]}
// on={live2dOn}
// handleOnClick={() => setLive2dOn(!live2dOn)}
// />
)
])
}, [voiceOn, live2dOn])
}, [voiceOn, language])
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}`
const playVoice = useCallback((blob) => {
const audioUrl = URL.createObjectURL(blob)
audioEl.src = audioUrl
let startPlayPromise = audioEl.play()
if (startPlayPromise !== undefined) {
startPlayPromise
.then(() => {
return
const audioEndedFunc = () => {
audioEl.removeEventListener('ended', audioEndedFunc)
URL.revokeObjectURL(audioUrl)
}
audioEl.addEventListener('ended', audioEndedFunc)
})
.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) {
},
const loadVoice = (link) => {
if (!voiceOn) return
db.voice.get({ key: link }).then((v) => {
if (v === undefined) {
fetch(`/${link}/assets/voice/${import.meta.env.VITE_APP_VOICE_URL}`)
.then(res => res.blob())
.then(blob => {
db.voice.put({ key: link, blob: blob })
playVoice(blob)
})
})
}
})
}
}, [live2dOn])
} else {
playVoice(v.blob)
}
})
}
return (
<section className="home">
@@ -170,16 +128,15 @@ export default function Home() {
className="item"
key={item.link}
hidden={!isShown(item.type)}
onMouseEnter={(e) => playVoice(item.link)}
onMouseEnter={() => loadVoice(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]} />
)}
<ImageElement
item={item}
language={language}
/>
</section>
<section className="item-info">
<section className="item-title-container">
@@ -211,4 +168,25 @@ export default function Home() {
}
</section>
)
}
function ImageElement({ item, language }) {
const [blobUrl, setBlobUrl] = useState(null)
useEffect(() => {
db.image.get({ key: item.link }).then((v) => {
if (v === undefined) {
fetch(`/${item.link}/assets/${item.fallback_name.replace("#", "%23")}_portrait.png`)
.then(res => res.blob())
.then(blob => {
db.image.put({ key: item.link, blob: blob })
setBlobUrl(URL.createObjectURL(blob))
})
} else {
setBlobUrl(URL.createObjectURL(v.blob))
}
})
}, [item.link])
return blobUrl ? <img src={blobUrl} alt={item.codename[language]} /> : null
}

View File

@@ -30,12 +30,12 @@ export default function Root(props) {
const {
title,
tabs,
currentTab, setCurrentTab
currentTab, setCurrentTab,
appbarExtraArea
} = useContext(HeaderContext)
const [drawerDestinations, setDrawerDestinations] = useState(null)
const currentYear = new Date().getFullYear()
const [headerTabs, setHeaderTabs] = useState(null)
const [appbarExtraArea, setAppbarExtraArea] = useState(null)
const renderHeaderTabs = (tabs) => {
setHeaderTabs(tabs?.map((item) => {
@@ -157,7 +157,7 @@ export default function Root(props) {
}}
/>
</MainBorder>
<Outlet context={{setAppbarExtraArea}}/>
<Outlet />
</main>
<footer className='footer'>
<section className='links section'>
@@ -183,7 +183,7 @@ export default function Root(props) {
<span>Spine Runtimes © 2013 - 2019 Esoteric Software LLC</span>
<span>Assets © 2017 - {currentYear} Arknights/Hypergryph Co., Ltd</span>
<span>Source Code © 2021 - {currentYear} Halyul</span>
<span>Version: {import.meta.env.VITE_APP_VERSION}</span>
<span>Version: {import.meta.env.VITE_VERSION}</span>
</section>
</footer>
</>