better responsiveness
This commit is contained in:
42
src/icons.ts
Normal file
42
src/icons.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { createIcons, icons } from 'lucide';
|
||||
|
||||
export function createIcon(name: string, size: number = 16): string {
|
||||
const iconMap: Record<string, string> = {
|
||||
'menu': 'menu',
|
||||
'close': 'x',
|
||||
'help': 'help-circle',
|
||||
'fullscreen': 'maximize-2',
|
||||
'show': 'eye',
|
||||
'hide': 'eye-off',
|
||||
'random': 'dice-3',
|
||||
'share': 'share-2',
|
||||
'export': 'download',
|
||||
'play': 'play',
|
||||
'settings': 'settings',
|
||||
'resolution': 'monitor',
|
||||
'fps': 'zap',
|
||||
'palette': 'palette',
|
||||
'library': 'book-open'
|
||||
};
|
||||
|
||||
const iconName = iconMap[name];
|
||||
if (!iconName) return '';
|
||||
|
||||
return `<i data-lucide="${iconName}" width="${size}" height="${size}" stroke-width="2"></i>`;
|
||||
}
|
||||
|
||||
export function addIconToButton(button: HTMLElement, iconName: string, keepText: boolean = false): void {
|
||||
const originalText = button.textContent || '';
|
||||
const iconHtml = createIcon(iconName);
|
||||
|
||||
if (keepText) {
|
||||
button.innerHTML = iconHtml + ' ' + originalText;
|
||||
} else {
|
||||
button.innerHTML = iconHtml;
|
||||
button.setAttribute('aria-label', originalText);
|
||||
}
|
||||
}
|
||||
|
||||
export function initializeLucideIcons(): void {
|
||||
createIcons({ icons });
|
||||
}
|
||||
141
src/main.ts
141
src/main.ts
@ -1,5 +1,6 @@
|
||||
import { FakeShader } from './FakeShader';
|
||||
import { Storage } from './Storage';
|
||||
import { addIconToButton, createIcon, initializeLucideIcons } from './icons';
|
||||
|
||||
class BitfielderApp {
|
||||
private shader: FakeShader;
|
||||
@ -21,6 +22,7 @@ class BitfielderApp {
|
||||
this.shader = new FakeShader(this.canvas, this.editor.value);
|
||||
|
||||
this.setupEventListeners();
|
||||
this.initializeIcons();
|
||||
this.loadFromURL();
|
||||
this.renderShaderLibrary();
|
||||
this.render();
|
||||
@ -59,11 +61,29 @@ class BitfielderApp {
|
||||
const fpsSelect = document.getElementById('fps-select') as HTMLSelectElement;
|
||||
const renderModeSelect = document.getElementById('render-mode-select') as HTMLSelectElement;
|
||||
const opacitySlider = document.getElementById('opacity-slider') as HTMLInputElement;
|
||||
const opacityValue = document.getElementById('opacity-value')!;
|
||||
const evalBtn = document.getElementById('eval-btn')!;
|
||||
const helpPopup = document.getElementById('help-popup')!;
|
||||
const closeBtn = helpPopup.querySelector('.close-btn')!;
|
||||
|
||||
// Mobile elements
|
||||
const hamburgerMenu = document.getElementById('hamburger-menu')!;
|
||||
const mobileMenuOverlay = document.getElementById('mobile-menu-overlay')!;
|
||||
const randomBtnMobile = document.getElementById('random-btn-mobile')!;
|
||||
const hideUiBtnMobile = document.getElementById('hide-ui-btn-mobile')!;
|
||||
const libraryBtnMobile = document.getElementById('library-btn-mobile')!;
|
||||
|
||||
// Mobile menu controls
|
||||
const resolutionSelectMobile = document.getElementById('resolution-select-mobile') as HTMLSelectElement;
|
||||
const fpsSelectMobile = document.getElementById('fps-select-mobile') as HTMLSelectElement;
|
||||
const renderModeSelectMobile = document.getElementById('render-mode-select-mobile') as HTMLSelectElement;
|
||||
const opacitySliderMobile = document.getElementById('opacity-slider-mobile') as HTMLInputElement;
|
||||
|
||||
// Mobile menu buttons
|
||||
const helpBtnMobile = document.getElementById('help-btn-mobile')!;
|
||||
const fullscreenBtnMobile = document.getElementById('fullscreen-btn-mobile')!;
|
||||
const shareBtnMobile = document.getElementById('share-btn-mobile')!;
|
||||
const exportPngBtnMobile = document.getElementById('export-png-btn-mobile')!;
|
||||
|
||||
// Library elements
|
||||
const saveShaderBtn = document.getElementById('save-shader-btn')!;
|
||||
const shaderNameInput = document.getElementById('shader-name-input') as HTMLInputElement;
|
||||
@ -92,6 +112,48 @@ class BitfielderApp {
|
||||
});
|
||||
shaderSearchInput.addEventListener('input', () => this.renderShaderLibrary());
|
||||
|
||||
// Mobile event listeners
|
||||
hamburgerMenu.addEventListener('click', () => this.toggleMobileMenu());
|
||||
mobileMenuOverlay.addEventListener('click', () => this.closeMobileMenu());
|
||||
randomBtnMobile.addEventListener('click', () => this.generateRandom());
|
||||
hideUiBtnMobile.addEventListener('click', () => this.toggleUI());
|
||||
libraryBtnMobile.addEventListener('click', () => this.toggleShaderLibrary());
|
||||
|
||||
// Mobile menu controls sync with desktop
|
||||
resolutionSelectMobile.addEventListener('change', () => {
|
||||
resolutionSelect.value = resolutionSelectMobile.value;
|
||||
this.updateResolution();
|
||||
});
|
||||
fpsSelectMobile.addEventListener('change', () => {
|
||||
fpsSelect.value = fpsSelectMobile.value;
|
||||
this.updateFPS();
|
||||
});
|
||||
renderModeSelectMobile.addEventListener('change', () => {
|
||||
renderModeSelect.value = renderModeSelectMobile.value;
|
||||
this.updateRenderMode();
|
||||
});
|
||||
opacitySliderMobile.addEventListener('input', () => {
|
||||
opacitySlider.value = opacitySliderMobile.value;
|
||||
this.updateUIOpacity();
|
||||
});
|
||||
|
||||
// Mobile menu buttons
|
||||
helpBtnMobile.addEventListener('click', () => {
|
||||
this.closeMobileMenu();
|
||||
this.showHelp();
|
||||
});
|
||||
fullscreenBtnMobile.addEventListener('click', () => {
|
||||
this.closeMobileMenu();
|
||||
this.toggleFullscreen();
|
||||
});
|
||||
shareBtnMobile.addEventListener('click', () => {
|
||||
this.closeMobileMenu();
|
||||
this.shareURL();
|
||||
});
|
||||
exportPngBtnMobile.addEventListener('click', () => {
|
||||
this.closeMobileMenu();
|
||||
this.exportPNG();
|
||||
});
|
||||
|
||||
// Close help popup when clicking outside
|
||||
helpPopup.addEventListener('click', (e) => {
|
||||
@ -285,13 +347,83 @@ class BitfielderApp {
|
||||
private updateUIOpacity(): void {
|
||||
const opacitySlider = document.getElementById('opacity-slider') as HTMLInputElement;
|
||||
const opacityValue = document.getElementById('opacity-value')!;
|
||||
const opacityValueMobile = document.getElementById('opacity-value-mobile')!;
|
||||
const opacity = parseInt(opacitySlider.value) / 100;
|
||||
|
||||
document.documentElement.style.setProperty('--ui-opacity', opacity.toString());
|
||||
opacityValue.textContent = `${opacitySlider.value}%`;
|
||||
opacityValueMobile.textContent = `${opacitySlider.value}%`;
|
||||
|
||||
Storage.saveSettings({ uiOpacity: opacity });
|
||||
}
|
||||
|
||||
private initializeIcons(): void {
|
||||
// Desktop buttons
|
||||
addIconToButton(document.getElementById('help-btn')!, 'help');
|
||||
addIconToButton(document.getElementById('fullscreen-btn')!, 'fullscreen');
|
||||
addIconToButton(document.getElementById('hide-ui-btn')!, 'hide');
|
||||
addIconToButton(document.getElementById('random-btn')!, 'random');
|
||||
addIconToButton(document.getElementById('share-btn')!, 'share');
|
||||
addIconToButton(document.getElementById('export-png-btn')!, 'export');
|
||||
|
||||
// Mobile buttons
|
||||
addIconToButton(document.getElementById('hamburger-menu')!, 'menu');
|
||||
addIconToButton(document.getElementById('library-btn-mobile')!, 'library');
|
||||
addIconToButton(document.getElementById('random-btn-mobile')!, 'random');
|
||||
addIconToButton(document.getElementById('hide-ui-btn-mobile')!, 'hide');
|
||||
addIconToButton(document.getElementById('show-ui-btn')!, 'show');
|
||||
|
||||
// Mobile menu buttons with text
|
||||
const helpIcon = document.querySelector('#help-btn-mobile .icon') as HTMLElement;
|
||||
const fullscreenIcon = document.querySelector('#fullscreen-btn-mobile .icon') as HTMLElement;
|
||||
const shareIcon = document.querySelector('#share-btn-mobile .icon') as HTMLElement;
|
||||
const exportIcon = document.querySelector('#export-png-btn-mobile .icon') as HTMLElement;
|
||||
|
||||
if (helpIcon) helpIcon.innerHTML = createIcon('help');
|
||||
if (fullscreenIcon) fullscreenIcon.innerHTML = createIcon('fullscreen');
|
||||
if (shareIcon) shareIcon.innerHTML = createIcon('share');
|
||||
if (exportIcon) exportIcon.innerHTML = createIcon('export');
|
||||
|
||||
// Initialize all Lucide icons
|
||||
initializeLucideIcons();
|
||||
}
|
||||
|
||||
private toggleMobileMenu(): void {
|
||||
const mobileMenu = document.getElementById('mobile-menu')!;
|
||||
const mobileMenuOverlay = document.getElementById('mobile-menu-overlay')!;
|
||||
const hamburgerMenu = document.getElementById('hamburger-menu')!;
|
||||
|
||||
mobileMenu.classList.toggle('open');
|
||||
mobileMenuOverlay.classList.toggle('open');
|
||||
|
||||
// Update hamburger icon
|
||||
if (mobileMenu.classList.contains('open')) {
|
||||
addIconToButton(hamburgerMenu, 'close');
|
||||
} else {
|
||||
addIconToButton(hamburgerMenu, 'menu');
|
||||
}
|
||||
|
||||
// Reinitialize icons
|
||||
initializeLucideIcons();
|
||||
}
|
||||
|
||||
private closeMobileMenu(): void {
|
||||
const mobileMenu = document.getElementById('mobile-menu')!;
|
||||
const mobileMenuOverlay = document.getElementById('mobile-menu-overlay')!;
|
||||
const hamburgerMenu = document.getElementById('hamburger-menu')!;
|
||||
|
||||
mobileMenu.classList.remove('open');
|
||||
mobileMenuOverlay.classList.remove('open');
|
||||
addIconToButton(hamburgerMenu, 'menu');
|
||||
|
||||
// Reinitialize icons
|
||||
initializeLucideIcons();
|
||||
}
|
||||
|
||||
private toggleShaderLibrary(): void {
|
||||
const shaderLibrary = document.getElementById('shader-library')!;
|
||||
shaderLibrary.classList.toggle('open');
|
||||
}
|
||||
|
||||
private evalShader(): void {
|
||||
this.shader.setCode(this.editor.value);
|
||||
@ -315,11 +447,18 @@ class BitfielderApp {
|
||||
(document.getElementById('fps-select') as HTMLSelectElement).value = settings.fps.toString();
|
||||
(document.getElementById('render-mode-select') as HTMLSelectElement).value = settings.renderMode || 'classic';
|
||||
|
||||
// Sync mobile controls
|
||||
(document.getElementById('resolution-select-mobile') as HTMLSelectElement).value = settings.resolution.toString();
|
||||
(document.getElementById('fps-select-mobile') as HTMLSelectElement).value = settings.fps.toString();
|
||||
(document.getElementById('render-mode-select-mobile') as HTMLSelectElement).value = settings.renderMode || 'classic';
|
||||
|
||||
// Apply UI opacity
|
||||
const opacity = settings.uiOpacity ?? 0.3;
|
||||
document.documentElement.style.setProperty('--ui-opacity', opacity.toString());
|
||||
(document.getElementById('opacity-slider') as HTMLInputElement).value = (opacity * 100).toString();
|
||||
(document.getElementById('opacity-slider-mobile') as HTMLInputElement).value = (opacity * 100).toString();
|
||||
document.getElementById('opacity-value')!.textContent = `${Math.round(opacity * 100)}%`;
|
||||
document.getElementById('opacity-value-mobile')!.textContent = `${Math.round(opacity * 100)}%`;
|
||||
|
||||
// Load last shader code if no URL hash
|
||||
if (!window.location.hash) {
|
||||
|
||||
Reference in New Issue
Block a user