UNPKG

kinetic-slider

Version:

A WebGL-powered kinetic slider component using PIXI.js

241 lines (238 loc) 8.95 kB
import { useEffect } from 'react'; import { Container, TextStyle, Text } from 'pixi.js'; import { gsap } from 'gsap'; import { setupCustomFonts } from '../utils/fontUtils.js'; const log = { info: (message, ...args) => { }, warn: (message, ...args) => { }, error: (message, error) => { } }; const DEFAULT_FONTS = { title: 'Georgia, Times, "Times New Roman", serif', subtitle: "Helvetica, Arial, sans-serif" }; function prepareFontFamily(fontStack, defaultStack = DEFAULT_FONTS.title) { try { if (!fontStack) { log.info(`No font stack provided, using default: ${defaultStack}`); return defaultStack; } const cleanedStack = fontStack.split(",").map((font) => font.trim().replace(/['"]/g, "")).filter(Boolean).join(", "); log.info(`Processed font stack: ${cleanedStack}`); return cleanedStack || defaultStack; } catch (error) { return defaultStack; } } const useTextContainers = ({ sliderRef, appRef, slidesRef, textContainersRef, currentIndex, buttonMode, texts, // Title props textTitleColor, textTitleSize, mobileTextTitleSize, textTitleLetterspacing, textTitleFontFamily, // Subtitle props textSubTitleColor, textSubTitleSize, mobileTextSubTitleSize, textSubTitleLetterspacing, textSubTitleOffsetTop, mobileTextSubTitleOffsetTop, textSubTitleFontFamily, resourceManager }) => { const cancellationRef = { current: { isCancelled: false } }; useEffect(() => { if (typeof window === "undefined") return; cancellationRef.current.isCancelled = false; if (!appRef.current || !slidesRef.current.length || !texts.length) { log.warn("Initialization aborted due to missing references"); return; } const setupFontsAndCreateContainers = async () => { try { if (textTitleFontFamily || textSubTitleFontFamily) { log.info("Setting up custom fonts", { titleFont: textTitleFontFamily, subtitleFont: textSubTitleFontFamily }); await setupCustomFonts(textTitleFontFamily, textSubTitleFontFamily); } if (cancellationRef.current.isCancelled) return; const app = appRef.current; const stage = app.stage.children[0]; textContainersRef.current.forEach((container) => { try { if (container.parent) { container.parent.removeChild(container); } } catch (removeError) { log.error("Error removing existing text container", removeError); } }); textContainersRef.current = []; const isMobile = window.innerWidth < 768; const computedTitleSize = isMobile ? mobileTextTitleSize : textTitleSize; const computedSubTitleSize = isMobile ? mobileTextSubTitleSize : textSubTitleSize; const computedSubTitleOffset = isMobile ? mobileTextSubTitleOffsetTop : textSubTitleOffsetTop; const titleFontFamily = prepareFontFamily(textTitleFontFamily); const subtitleFontFamily = prepareFontFamily(textSubTitleFontFamily, DEFAULT_FONTS.subtitle); log.info("Creating text containers", { titleFont: titleFontFamily, subtitleFont: subtitleFontFamily }); texts.forEach((textPair, index) => { const [title, subtitle] = textPair; const textContainer = new Container(); textContainer.x = app.screen.width / 2; textContainer.y = app.screen.height / 2; if (resourceManager) { resourceManager.trackDisplayObject(textContainer); } const titleStyle = new TextStyle({ fill: textTitleColor, fontSize: computedTitleSize, letterSpacing: textTitleLetterspacing, fontWeight: "bold", align: "center", fontFamily: titleFontFamily }); const titleText = new Text({ text: title, style: titleStyle }); titleText.anchor.set(0.5, 0); titleText.y = 0; if (resourceManager) { resourceManager.trackDisplayObject(titleText); } const subtitleStyle = new TextStyle({ fill: textSubTitleColor, fontSize: computedSubTitleSize, letterSpacing: textSubTitleLetterspacing, align: "center", fontFamily: subtitleFontFamily }); const subText = new Text({ text: subtitle, style: subtitleStyle }); subText.anchor.set(0.5, 0); subText.y = titleText.height + computedSubTitleOffset; if (resourceManager) { resourceManager.trackDisplayObject(subText); } textContainer.addChild(titleText, subText); textContainer.pivot.y = textContainer.height / 2; textContainer.alpha = index === 0 ? 1 : 0; textContainer.visible = index === 0; if (buttonMode) { textContainer.eventMode = "static"; textContainer.cursor = "pointer"; textContainer.on("pointerover", () => { gsap.to(titleText.scale, { x: 1.1, y: 1.1, duration: 0.2, onComplete: () => { if (resourceManager) { resourceManager.trackDisplayObject(titleText); } } }); }); textContainer.on("pointerout", () => { gsap.to(titleText.scale, { x: 1, y: 1, duration: 0.2, onComplete: () => { if (resourceManager) { resourceManager.trackDisplayObject(titleText); } } }); }); textContainer.on("pointerdown", () => { const nextIndex = (currentIndex.current + 1) % slidesRef.current.length; window.dispatchEvent(new CustomEvent("slideChange", { detail: { nextIndex } })); }); } stage.addChild(textContainer); textContainersRef.current.push(textContainer); }); log.info(`Created ${texts.length} text containers`); } catch (error) { } }; setupFontsAndCreateContainers(); const handleResize = () => { try { if (cancellationRef.current.isCancelled || !appRef.current || !sliderRef.current || !textContainersRef.current.length) return; const containerWidth = sliderRef.current.clientWidth || 0; const containerHeight = sliderRef.current.clientHeight || 0; const isMobile = window.innerWidth < 768; const computedTitleSize = isMobile ? mobileTextTitleSize : textTitleSize; const computedSubTitleSize = isMobile ? mobileTextSubTitleSize : textSubTitleSize; const computedSubTitleOffset = isMobile ? mobileTextSubTitleOffsetTop : textSubTitleOffsetTop; const titleFontFamily = prepareFontFamily(textTitleFontFamily); const subtitleFontFamily = prepareFontFamily(textSubTitleFontFamily, DEFAULT_FONTS.subtitle); textContainersRef.current.forEach((container) => { container.x = containerWidth / 2; container.y = containerHeight / 2; const titleText = container.children[0]; titleText.style.fontSize = computedTitleSize; titleText.style.fontFamily = titleFontFamily; const titleContent = titleText.text; titleText.text = titleContent; const subText = container.children[1]; subText.style.fontSize = computedSubTitleSize; subText.style.fontFamily = subtitleFontFamily; subText.y = titleText.height + computedSubTitleOffset; const subContent = subText.text; subText.text = subContent; container.pivot.y = container.height / 2; if (resourceManager) { resourceManager.trackDisplayObject(container); resourceManager.trackDisplayObject(titleText); resourceManager.trackDisplayObject(subText); } }); log.info("Text containers resized"); } catch (error) { } }; window.addEventListener("resize", handleResize); handleResize(); return () => { cancellationRef.current.isCancelled = true; window.removeEventListener("resize", handleResize); }; }, [ // Dependency array with all props for comprehensive updates appRef.current, texts, textTitleColor, textTitleSize, mobileTextTitleSize, textTitleLetterspacing, textTitleFontFamily, textSubTitleColor, textSubTitleSize, mobileTextSubTitleSize, textSubTitleLetterspacing, textSubTitleOffsetTop, mobileTextSubTitleOffsetTop, textSubTitleFontFamily, buttonMode, resourceManager ]); }; export { useTextContainers as default }; //# sourceMappingURL=useTextContainers.js.map