diff --git a/packages/webgal/src/Core/Modules/events.ts b/packages/webgal/src/Core/Modules/events.ts index af8b08eae..1d27b1af7 100644 --- a/packages/webgal/src/Core/Modules/events.ts +++ b/packages/webgal/src/Core/Modules/events.ts @@ -11,6 +11,7 @@ export class Events { public userInteractNext = formEvent('__NEXT'); public fullscreenDbClick = formEvent('fullscreen-dbclick'); public styleUpdate = formEvent('style-update'); + public afterStyleUpdate = formEvent('after-style-update'); } const eventBus = mitt(); diff --git a/packages/webgal/src/Core/gameScripts/choose/index.tsx b/packages/webgal/src/Core/gameScripts/choose/index.tsx index d730da788..b7813c4cc 100644 --- a/packages/webgal/src/Core/gameScripts/choose/index.tsx +++ b/packages/webgal/src/Core/gameScripts/choose/index.tsx @@ -83,7 +83,7 @@ export const choose = (sentence: ISentence): IPerform => { function Choose(props: { chooseOptions: ChooseOption[] }) { const font = useFontFamily(); const { playSeEnter, playSeClick } = useSEByWebgalStore(); - const applyStyle = useApplyStyle('Stage/Choose/choose.scss'); + const applyStyle = useApplyStyle('choose'); // 运行时计算JSX.Element[] const runtimeBuildList = (chooseListFull: ChooseOption[]) => { return chooseListFull diff --git a/packages/webgal/src/Core/util/coreInitialFunction/templateLoader.ts b/packages/webgal/src/Core/util/coreInitialFunction/templateLoader.ts index 074840fbd..3e43745bf 100644 --- a/packages/webgal/src/Core/util/coreInitialFunction/templateLoader.ts +++ b/packages/webgal/src/Core/util/coreInitialFunction/templateLoader.ts @@ -6,6 +6,8 @@ import { buildFontOptionsFromTemplate } from '@/Core/util/fonts/fontOptions'; import { webgalStore } from '@/store/store'; import { setFontOptions } from '@/store/GUIReducer'; import { setOptionData } from '@/store/userDataReducer'; +import { scss2cssinjsParser } from '@/Core/controller/customUI/scss2cssinjsParser'; +import { injectGlobal } from '@emotion/css'; const TEMPLATE_PATH = './game/template/template.json'; const TEMPLATE_FONT_STYLE_SELECTOR = 'style[data-webgal-template-fonts]'; @@ -17,6 +19,8 @@ export async function loadTemplate(): Promise { const fonts = data.fonts ?? []; injectTemplateFonts(fonts); updateFontOptions(fonts); + await loadStyleFiles(); + WebGAL.events.styleUpdate.on(loadStyleFiles); return data; } catch (error) { logger.warn('加载模板文件失败', error); @@ -76,3 +80,29 @@ function resolveTemplateAssetPath(path: string): string { const normalized = path.replace(/^[./]+/, ''); return `./game/template/${normalized}`; } + +async function loadStyleFiles() { + // TODO: 以后应该改成从 templates.json 读取,而不是现在这样写死 + const TEMPLATES: { ui: string; path: string }[] = [ + { ui: 'title', path: 'UI/Title/title.scss' }, + { ui: 'textbox', path: 'Stage/TextBox/textbox.scss' }, + { ui: 'choose', path: 'Stage/Choose/choose.scss' }, + ]; + + await Promise.all( + TEMPLATES.map(async (templatePath) => { + try { + logger.info(`加载模板样式文件: ${templatePath.path}`); + const resp = await axios.get(`game/template/${templatePath.path}`); + const scssStr = resp.data; + const styleObject = scss2cssinjsParser(scssStr); + WebGAL.styleObjects.set(templatePath.ui, styleObject); + injectGlobal(styleObject.others); + } catch (error) { + logger.warn(`加载模板样式文件失败: ${templatePath.path}`, error); + } + }), + ); + + WebGAL.events.afterStyleUpdate.emit(); +} diff --git a/packages/webgal/src/Core/webgalCore.ts b/packages/webgal/src/Core/webgalCore.ts index ae0646b6e..98d1c53ec 100644 --- a/packages/webgal/src/Core/webgalCore.ts +++ b/packages/webgal/src/Core/webgalCore.ts @@ -6,6 +6,7 @@ import { Gameplay } from './Modules/gamePlay'; import { Events } from '@/Core/Modules/events'; import { SteamIntegration } from '@/Core/integration/steamIntegration'; import { WebgalTemplate } from '@/types/template'; +import { IWebGALStyleObj } from 'webgal-parser/build/types/styleParser'; export class WebgalCore { public sceneManager = new SceneManager(); @@ -17,4 +18,5 @@ export class WebgalCore { public events = new Events(); public steam = new SteamIntegration(); public template: WebgalTemplate | null = null; + public styleObjects: Map = new Map(); } diff --git a/packages/webgal/src/Stage/TextBox/IMSSTextbox.tsx b/packages/webgal/src/Stage/TextBox/IMSSTextbox.tsx index 58194f5aa..65a12dcc4 100644 --- a/packages/webgal/src/Stage/TextBox/IMSSTextbox.tsx +++ b/packages/webgal/src/Stage/TextBox/IMSSTextbox.tsx @@ -28,7 +28,7 @@ export default function IMSSTextbox(props: ITextboxProps) { textSizeState, } = props; - const applyStyle = useApplyStyle('Stage/TextBox/textbox.scss'); + const applyStyle = useApplyStyle('textbox'); useEffect(() => { function settleText() { diff --git a/packages/webgal/src/UI/Title/Title.tsx b/packages/webgal/src/UI/Title/Title.tsx index baae72dd4..061df48a6 100644 --- a/packages/webgal/src/UI/Title/Title.tsx +++ b/packages/webgal/src/UI/Title/Title.tsx @@ -25,7 +25,7 @@ export default function Title() { const tCommon = useTrans('common.'); const { playSeEnter, playSeClick } = useSoundEffect(); - const applyStyle = useApplyStyle('UI/Title/title.scss'); + const applyStyle = useApplyStyle('title'); useConfigData(); // 监听基础ConfigData变化 const appreciationItems = useSelector((state: RootState) => state.userData.appreciationData); diff --git a/packages/webgal/src/hooks/useApplyStyle.ts b/packages/webgal/src/hooks/useApplyStyle.ts index ad25477b1..46622c66d 100644 --- a/packages/webgal/src/hooks/useApplyStyle.ts +++ b/packages/webgal/src/hooks/useApplyStyle.ts @@ -9,8 +9,8 @@ import { RootState } from '@/store/store'; import { IWebGALStyleObj } from 'webgal-parser/build/types/styleParser'; import { logger } from '@/Core/util/logger'; -export default function useApplyStyle(url: string) { - const styleObject = useValue({ classNameStyles: {}, others: '' }); +export default function useApplyStyle(ui: string) { + const styleObject = useValue(WebGAL.styleObjects.get(ui) ?? { classNameStyles: {}, others: '' }); const replaced = useSelector((state: RootState) => state.stage.replacedUIlable); const applyStyle = (classNameLable: string, fallbackClassName: string) => { @@ -23,32 +23,22 @@ export default function useApplyStyle(url: string) { return fallbackClassName; }; - const updateStyleFile = async () => { - logger.debug('更新 Scss 文件', url); - const resp = await axios.get(`game/template/${url}`); - const scssStr = resp.data; - styleObject.set(scss2cssinjsParser(scssStr)); + const updateStyleObject = () => { + styleObject.value = WebGAL.styleObjects.get(ui) ?? { classNameStyles: {}, others: '' }; }; - useEffect(() => { - updateStyleFile(); - }, []); - - useEffect(() => { - injectGlobal(styleObject.value.others); - }, [styleObject.value.others]); - - useRigisterStyleUpdate(updateStyleFile); + useRegisterAfterStyleUpdate(updateStyleObject); return applyStyle; } -function useRigisterStyleUpdate(callback: Function) { +function useRegisterAfterStyleUpdate(callback: Function) { + // TODO : 这里可能需要加个依赖项数组?但是当前由于使用了 useValue,状态过期问题可能被规避了,并且之前一直表现正常 const handler = () => { callback(); }; useEffect(() => { - WebGAL.events.styleUpdate.on(handler); - return () => WebGAL.events.styleUpdate.off(handler); + WebGAL.events.afterStyleUpdate.on(handler); + return () => WebGAL.events.afterStyleUpdate.off(handler); }, []); }