UNPKG

kinetic-slider

Version:

A WebGL-powered kinetic slider component using PIXI.js

241 lines (238 loc) 8.47 kB
import { Assets } from 'pixi.js'; /** * Font utilities for KineticSlider */ // Common system fonts that don't need to be loaded const SYSTEM_FONTS = [ // Sans-serif fonts 'Arial', 'Helvetica', 'Helvetica Neue', 'Verdana', 'Tahoma', 'Trebuchet MS', 'Segoe UI', 'Roboto', 'Open Sans', 'Liberation Sans', 'Noto Sans', 'Ubuntu', // Serif fonts 'Times', 'Times New Roman', 'Georgia', 'Palatino', 'Garamond', 'Bookman', 'Cambria', 'Constantia', 'Liberation Serif', 'Noto Serif', // Monospace fonts 'Courier', 'Courier New', 'Monaco', 'Consolas', 'Liberation Mono', 'Menlo', // Generic families 'sans-serif', 'serif', 'monospace', 'cursive', 'fantasy', 'system-ui' ]; // Font file extensions to check const FONT_EXTENSIONS = ['.woff2', '.woff', '.ttf', '.otf']; /** * Extract font names from a CSS font-family string * * @param fontStack - CSS font-family value * @returns Array of individual font names */ function parseFontStack(fontStack) { if (!fontStack) return []; // Split by commas and clean up each font name return fontStack .split(',') .map(font => { // Remove quotes and trim whitespace return font .replace(/^["'\s]+|["'\s]+$/g, '') // Remove quotes and spaces at the start/end .trim(); }) .filter(Boolean); // Remove any empty entries } /** * Identify which fonts in a font stack are likely custom fonts * * @param parsedFonts - Array of font names * @returns Array of potential custom fonts */ function identifyCustomFonts(parsedFonts) { return parsedFonts.filter(font => // Not in our system fonts list and not a generic family !SYSTEM_FONTS.some(systemFont => systemFont.toLowerCase() === font.toLowerCase())); } /** * Check if a font file exists in the fonts directory * This is a simplified implementation for client-side * * @param fontName - Name of the font to check * @param extensions - File extensions to try * @returns Path to the font file if found, null otherwise */ async function findFontFile(fontName, extensions = FONT_EXTENSIONS) { // List of paths to try - check both case-sensitive and lowercase versions const fontPaths = []; for (const ext of extensions) { // Original case fontPaths.push(`/fonts/${fontName}${ext}`); // Lowercase version fontPaths.push(`/fonts/${fontName.toLowerCase()}${ext}`); } const additionalPaths = []; for (const ext of extensions) { // Original case additionalPaths.push(`/public/fonts/${fontName}${ext}`); // Lowercase version additionalPaths.push(`/public/fonts/${fontName.toLowerCase()}${ext}`); } // Combine paths const allPaths = [...fontPaths, ...additionalPaths]; // Try each path for (const path of allPaths) { try { // Try to fetch the font file to check if it exists const response = await fetch(path, { method: 'HEAD' }); if (response.ok) { console.log(`Found font file at: ${path}`); return path; } } catch (error) { // Ignore fetch errors and try the next path } } console.warn(`No font file found for: ${fontName}`); return null; } /** * Load custom fonts for use with Pixi.js * * @param fontStack - CSS font-family value * @returns Promise resolving to an array of successfully loaded font paths */ async function loadCustomFonts(fontStack) { if (!fontStack) return []; // Parse font stack and identify custom fonts const parsedFonts = parseFontStack(fontStack); const customFonts = identifyCustomFonts(parsedFonts); if (!customFonts.length) { console.log('No custom fonts identified in font stack:', fontStack); return []; } console.log('Identified custom fonts:', customFonts); // Try to load each custom font const loadedFonts = []; for (const fontName of customFonts) { try { // Remove any quotes from the font name const cleanFontName = fontName.replace(/['"]/g, ''); const fontPath = await findFontFile(cleanFontName); if (fontPath) { // Add font to Assets and load it console.log(`Loading font: ${cleanFontName} from ${fontPath}`); try { // Register and load the font with PIXI Assets await Assets.load({ src: fontPath, data: { // Add font-specific metadata if needed family: cleanFontName } }); loadedFonts.push(fontPath); console.log(`Successfully loaded font: ${cleanFontName}`); } catch (loadError) { console.warn(`Failed to load font ${cleanFontName}:`, loadError); } } } catch (error) { console.warn(`Error processing font ${fontName}:`, error); } } return loadedFonts; } /** * Create @font-face CSS rules for custom fonts * This is needed for proper text rendering in Pixi.js * * @param fontPaths - Array of paths to font files * @returns CSS string with @font-face rules */ function createFontFaceCSS(fontPaths) { return fontPaths.map(path => { // Extract font name from path const fontName = path.split('/').pop()?.split('.')[0] || ''; const fontFormat = path.split('.').pop(); let format; switch (fontFormat) { case 'woff2': format = 'woff2'; break; case 'woff': format = 'woff'; break; case 'ttf': format = 'truetype'; break; case 'otf': format = 'opentype'; break; default: format = 'truetype'; } return ` @font-face { font-family: '${fontName}'; src: url('${path}') format('${format}'); font-weight: normal; font-style: normal; font-display: swap; } `; }).join('\n'); } /** * Add @font-face rules to the document head * * @param css - CSS string with @font-face rules */ function injectFontFaceCSS(css) { if (typeof document === 'undefined' || !css) return; // Create a style element const style = document.createElement('style'); style.textContent = css; style.setAttribute('data-kinetic-slider-fonts', 'true'); // Add it to the head document.head.appendChild(style); } /** * Main function to handle custom font loading for KineticSlider * * @param titleFontStack - Title font-family value * @param subtitleFontStack - Subtitle font-family value * @returns Promise resolving when fonts are loaded */ async function setupCustomFonts(titleFontStack, subtitleFontStack) { // Skip during server-side rendering if (typeof window === 'undefined') return; console.log('Setting up custom fonts:', { titleFontStack, subtitleFontStack }); // Track loaded fonts to avoid duplicates const loadedFontPaths = []; // Process title fonts if (titleFontStack) { const titleFontPaths = await loadCustomFonts(titleFontStack); loadedFontPaths.push(...titleFontPaths); } // Process subtitle fonts if (subtitleFontStack) { const subtitleFontPaths = await loadCustomFonts(subtitleFontStack); // Filter out duplicates const newPaths = subtitleFontPaths.filter(path => !loadedFontPaths.includes(path)); loadedFontPaths.push(...newPaths); } // Create and inject CSS if fonts were found if (loadedFontPaths.length > 0) { const fontFaceCSS = createFontFaceCSS(loadedFontPaths); injectFontFaceCSS(fontFaceCSS); console.log('Injected font-face CSS for:', loadedFontPaths); // Allow some time for the fonts to load return new Promise((resolve) => { // Use a timeout to ensure fonts have time to load setTimeout(resolve, 100); }); } return Promise.resolve(); } export { createFontFaceCSS, findFontFile, identifyCustomFonts, injectFontFaceCSS, loadCustomFonts, parseFontStack, setupCustomFonts }; //# sourceMappingURL=fontUtils.js.map