yukinovel
Version:
Yukinovel is a simple web visual novel engine.
181 lines (176 loc) • 6.17 kB
JavaScript
export class ThemeManager {
constructor(game) {
this.currentTheme = null;
this.themes = new Map();
this.styleElement = null;
this.game = game;
this.initializeDefaultThemes();
this.createStyleElement();
}
// Khởi tạo themes mặc định
initializeDefaultThemes() {
// Default Light Theme
const lightTheme = {
name: 'light',
version: '1.0.0',
author: 'YukiNovel',
description: 'Default light theme',
colors: {
primary: '#3B82F6',
secondary: '#64748B',
accent: '#F59E0B',
background: '#FFFFFF',
surface: '#F8FAFC',
text: '#1E293B',
textSecondary: '#64748B',
border: '#E2E8F0',
shadow: 'rgba(0, 0, 0, 0.1)',
success: '#10B981',
warning: '#F59E0B',
error: '#EF4444',
info: '#3B82F6'
},
typography: {
fontFamily: '"Inter", "Segoe UI", Roboto, sans-serif',
fontSize: {
xs: '0.75rem',
sm: '0.875rem',
base: '1rem',
lg: '1.125rem',
xl: '1.25rem',
'2xl': '1.5rem',
'3xl': '1.875rem',
'4xl': '2.25rem'
},
fontWeight: {
light: 300,
normal: 400,
medium: 500,
semibold: 600,
bold: 700
},
lineHeight: {
tight: 1.25,
normal: 1.5,
relaxed: 1.75
}
},
components: {
textBox: {
background: 'rgba(248, 250, 252, 0.95)',
border: '1px solid #E2E8F0',
borderRadius: '0.5rem',
padding: '1.5rem',
fontSize: '1.125rem',
fontFamily: '"Inter", "Segoe UI", Roboto, sans-serif',
color: '#1E293B',
opacity: 0.95,
backdropFilter: 'blur(10px)'
},
button: {
background: '#3B82F6',
color: '#FFFFFF',
border: 'none',
borderRadius: '0.5rem',
padding: '0.75rem 1.5rem',
fontSize: '1rem',
fontWeight: 500,
transition: 'all 300ms cubic-bezier(0.4, 0, 0.2, 1)',
hover: {
background: '#2563EB',
color: '#FFFFFF',
transform: 'translateY(-1px)'
},
active: {
background: '#1D4ED8',
transform: 'translateY(0)'
}
}
}
};
this.themes.set('light', lightTheme);
}
// Tạo style element
createStyleElement() {
this.styleElement = document.createElement('style');
this.styleElement.id = 'yukinovel-theme-styles';
document.head.appendChild(this.styleElement);
}
// Apply theme
applyTheme(themeName) {
const theme = this.themes.get(themeName);
if (!theme) {
console.error(`Theme "${themeName}" not found`);
return;
}
this.currentTheme = theme;
this.generateCSS(theme);
// Trigger event
this.game.emit('themeChanged', { theme: themeName, config: theme });
}
// Generate CSS from theme
generateCSS(theme) {
if (!this.styleElement)
return;
let css = `
/* YukiNovel Theme: ${theme.name} */
.yn-textbox {
background: ${theme.components.textBox.background};
border: ${theme.components.textBox.border};
border-radius: ${theme.components.textBox.borderRadius};
padding: ${theme.components.textBox.padding};
font-size: ${theme.components.textBox.fontSize};
font-family: ${theme.components.textBox.fontFamily};
color: ${theme.components.textBox.color};
opacity: ${theme.components.textBox.opacity};
${theme.components.textBox.backdropFilter ? `backdrop-filter: ${theme.components.textBox.backdropFilter};` : ''}
}
.yn-button {
background: ${theme.components.button.background};
color: ${theme.components.button.color};
border: ${theme.components.button.border};
border-radius: ${theme.components.button.borderRadius};
padding: ${theme.components.button.padding};
font-size: ${theme.components.button.fontSize};
font-weight: ${theme.components.button.fontWeight};
transition: ${theme.components.button.transition};
cursor: pointer;
}
.yn-button:hover {
background: ${theme.components.button.hover.background};
color: ${theme.components.button.hover.color};
transform: ${theme.components.button.hover.transform};
}
.yn-button:active {
background: ${theme.components.button.active.background};
transform: ${theme.components.button.active.transform};
}
/* Custom CSS */
${theme.customCSS || ''}
`;
this.styleElement.textContent = css;
}
// Register new theme
registerTheme(theme) {
this.themes.set(theme.name, theme);
}
// Get theme
getTheme(name) {
return this.themes.get(name);
}
// Get all themes
getAllThemes() {
return Array.from(this.themes.values());
}
// Get current theme
getCurrentTheme() {
return this.currentTheme;
}
// Cleanup
destroy() {
if (this.styleElement) {
this.styleElement.remove();
this.styleElement = null;
}
}
}