diff --git a/src/content/TextAnimations/TextPressure/TextPressure.jsx b/src/content/TextAnimations/TextPressure/TextPressure.jsx index e0858130..e337eb8c 100644 --- a/src/content/TextAnimations/TextPressure/TextPressure.jsx +++ b/src/content/TextAnimations/TextPressure/TextPressure.jsx @@ -1,11 +1,31 @@ // Component ported from https://codepen.io/JuanFuentes/full/rgXKGQ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState, useMemo, useCallback } from 'react'; + +const dist = (a, b) => { + const dx = b.x - a.x; + const dy = b.y - a.y; + return Math.sqrt(dx * dx + dy * dy); +}; + +const getAttr = (distance, maxDist, minVal, maxVal) => { + const val = maxVal - Math.abs((maxVal * distance) / maxDist); + return Math.max(minVal, val + minVal); +}; + +const debounce = (func, delay) => { + let timeoutId; + return (...args) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + func.apply(this, args); + }, delay); + }; +}; const TextPressure = ({ text = 'Compressa', fontFamily = 'Compressa VF', - // This font is just an example, you should not use it in commercial projects. fontUrl = 'https://res.cloudinary.com/dr6lvwubh/raw/upload/v1529908256/CompressaPRO-GX.woff2', width = true, @@ -36,12 +56,6 @@ const TextPressure = ({ const chars = text.split(''); - const dist = (a, b) => { - const dx = b.x - a.x; - const dy = b.y - a.y; - return Math.sqrt(dx * dx + dy * dy); - }; - useEffect(() => { const handleMouseMove = e => { cursorRef.current.x = e.clientX; @@ -54,7 +68,7 @@ const TextPressure = ({ }; window.addEventListener('mousemove', handleMouseMove); - window.addEventListener('touchmove', handleTouchMove, { passive: false }); + window.addEventListener('touchmove', handleTouchMove, { passive: true }); if (containerRef.current) { const { left, top, width, height } = containerRef.current.getBoundingClientRect(); @@ -70,7 +84,7 @@ const TextPressure = ({ }; }, []); - const setSize = () => { + const setSize = useCallback(() => { if (!containerRef.current || !titleRef.current) return; const { width: containerW, height: containerH } = containerRef.current.getBoundingClientRect(); @@ -92,14 +106,14 @@ const TextPressure = ({ setLineHeight(yRatio); } }); - }; + }, [chars.length, minFontSize, scale]); useEffect(() => { - setSize(); - window.addEventListener('resize', setSize); - return () => window.removeEventListener('resize', setSize); - // eslint-disable-next-line - }, [scale, text]); + const debouncedSetSize = debounce(setSize, 100); + debouncedSetSize(); + window.addEventListener('resize', debouncedSetSize); + return () => window.removeEventListener('resize', debouncedSetSize); + }, [setSize]); useEffect(() => { let rafId; @@ -122,18 +136,19 @@ const TextPressure = ({ const d = dist(mouseRef.current, charCenter); - const getAttr = (distance, minVal, maxVal) => { - const val = maxVal - Math.abs((maxVal * distance) / maxDist); - return Math.max(minVal, val + minVal); - }; + const wdth = width ? Math.floor(getAttr(d, maxDist, 5, 200)) : 100; + const wght = weight ? Math.floor(getAttr(d, maxDist, 100, 900)) : 400; + const italVal = italic ? getAttr(d, maxDist, 0, 1).toFixed(2) : 0; + const alphaVal = alpha ? getAttr(d, maxDist, 0, 1).toFixed(2) : 1; - const wdth = width ? Math.floor(getAttr(d, 5, 200)) : 100; - const wght = weight ? Math.floor(getAttr(d, 100, 900)) : 400; - const italVal = italic ? getAttr(d, 0, 1).toFixed(2) : 0; - const alphaVal = alpha ? getAttr(d, 0, 1).toFixed(2) : 1; + const newFontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${italVal}`; - span.style.opacity = alphaVal; - span.style.fontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${italVal}`; + if (span.style.fontVariationSettings !== newFontVariationSettings) { + span.style.fontVariationSettings = newFontVariationSettings; + } + if (alpha && span.style.opacity !== alphaVal) { + span.style.opacity = alphaVal; + } }); } @@ -142,20 +157,10 @@ const TextPressure = ({ animate(); return () => cancelAnimationFrame(rafId); - }, [width, weight, italic, alpha, chars.length]); + }, [width, weight, italic, alpha]); - const dynamicClassName = [className, flex ? 'flex' : '', stroke ? 'stroke' : ''].filter(Boolean).join(' '); - - return ( -
+ const styleElement = useMemo(() => { + return ( + ); + }, [fontFamily, fontUrl, flex, stroke, textColor, strokeColor]); + const dynamicClassName = [className, flex ? 'flex' : '', stroke ? 'stroke' : ''].filter(Boolean).join(' '); + + return ( +
+ {styleElement}

{ + const dx = b.x - a.x; + const dy = b.y - a.y; + return Math.sqrt(dx * dx + dy * dy); +}; + +const getAttr = (distance, maxDist, minVal, maxVal) => { + const val = maxVal - Math.abs((maxVal * distance) / maxDist); + return Math.max(minVal, val + minVal); +}; + +const debounce = (func, delay) => { + let timeoutId; + return (...args) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + func.apply(this, args); + }, delay); + }; +}; const TextPressure = ({ text = 'Compressa', @@ -37,12 +58,6 @@ const TextPressure = ({ const chars = text.split(''); - const dist = (a, b) => { - const dx = b.x - a.x; - const dy = b.y - a.y; - return Math.sqrt(dx * dx + dy * dy); - }; - useEffect(() => { const handleMouseMove = e => { cursorRef.current.x = e.clientX; @@ -55,7 +70,7 @@ const TextPressure = ({ }; window.addEventListener('mousemove', handleMouseMove); - window.addEventListener('touchmove', handleTouchMove, { passive: false }); + window.addEventListener('touchmove', handleTouchMove, { passive: true }); if (containerRef.current) { const { left, top, width, height } = containerRef.current.getBoundingClientRect(); @@ -71,7 +86,7 @@ const TextPressure = ({ }; }, []); - const setSize = () => { + const setSize = useCallback(() => { if (!containerRef.current || !titleRef.current) return; const { width: containerW, height: containerH } = containerRef.current.getBoundingClientRect(); @@ -93,14 +108,14 @@ const TextPressure = ({ setLineHeight(yRatio); } }); - }; + }, [chars.length, minFontSize, scale]); useEffect(() => { - setSize(); - window.addEventListener('resize', setSize); - return () => window.removeEventListener('resize', setSize); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [scale, text]); + const debouncedSetSize = debounce(setSize, 100); + debouncedSetSize(); + window.addEventListener('resize', debouncedSetSize); + return () => window.removeEventListener('resize', debouncedSetSize); + }, [setSize]); useEffect(() => { let rafId; @@ -123,18 +138,19 @@ const TextPressure = ({ const d = dist(mouseRef.current, charCenter); - const getAttr = (distance, minVal, maxVal) => { - const val = maxVal - Math.abs((maxVal * distance) / maxDist); - return Math.max(minVal, val + minVal); - }; + const wdth = width ? Math.floor(getAttr(d, maxDist, 5, 200)) : 100; + const wght = weight ? Math.floor(getAttr(d, maxDist, 100, 900)) : 400; + const italVal = italic ? getAttr(d, maxDist, 0, 1).toFixed(2) : 0; + const alphaVal = alpha ? getAttr(d, maxDist, 0, 1).toFixed(2) : 1; - const wdth = width ? Math.floor(getAttr(d, 5, 200)) : 100; - const wght = weight ? Math.floor(getAttr(d, 100, 900)) : 400; - const italVal = italic ? getAttr(d, 0, 1).toFixed(2) : 0; - const alphaVal = alpha ? getAttr(d, 0, 1).toFixed(2) : 1; + const newFontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${italVal}`; - span.style.opacity = alphaVal; - span.style.fontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${italVal}`; + if (span.style.fontVariationSettings !== newFontVariationSettings) { + span.style.fontVariationSettings = newFontVariationSettings; + } + if (alpha && span.style.opacity !== alphaVal) { + span.style.opacity = alphaVal; + } }); } @@ -143,10 +159,10 @@ const TextPressure = ({ animate(); return () => cancelAnimationFrame(rafId); - }, [width, weight, italic, alpha, chars.length]); + }, [width, weight, italic, alpha]); - return ( -
+ const styleElement = useMemo(() => { + return ( + ); + }, [fontFamily, fontUrl, stroke, textColor, strokeColor, strokeWidth]); + return ( +
+ {styleElement}

{ + const dx = b.x - a.x; + const dy = b.y - a.y; + return Math.sqrt(dx * dx + dy * dy); +}; + +const getAttr = (distance: number, maxDist: number, minVal: number, maxVal: number) => { + const val = maxVal - Math.abs((maxVal * distance) / maxDist); + return Math.max(minVal, val + minVal); +}; + +const debounce = (func: (...args: any[]) => void, delay: number) => { + let timeoutId: NodeJS.Timeout; + return (...args: any[]) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + func.apply(this, args); + }, delay); + }; +}; + const TextPressure: React.FC = ({ text = 'Compressa', fontFamily = 'Compressa VF', @@ -48,12 +69,6 @@ const TextPressure: React.FC = ({ const chars = text.split(''); - const dist = (a: { x: number; y: number }, b: { x: number; y: number }) => { - const dx = b.x - a.x; - const dy = b.y - a.y; - return Math.sqrt(dx * dx + dy * dy); - }; - useEffect(() => { const handleMouseMove = (e: MouseEvent) => { cursorRef.current.x = e.clientX; @@ -66,7 +81,7 @@ const TextPressure: React.FC = ({ }; window.addEventListener('mousemove', handleMouseMove); - window.addEventListener('touchmove', handleTouchMove, { passive: false }); + window.addEventListener('touchmove', handleTouchMove, { passive: true }); if (containerRef.current) { const { left, top, width, height } = containerRef.current.getBoundingClientRect(); @@ -82,7 +97,7 @@ const TextPressure: React.FC = ({ }; }, []); - const setSize = () => { + const setSize = useCallback(() => { if (!containerRef.current || !titleRef.current) return; const { width: containerW, height: containerH } = containerRef.current.getBoundingClientRect(); @@ -104,13 +119,14 @@ const TextPressure: React.FC = ({ setLineHeight(yRatio); } }); - }; + }, [chars.length, minFontSize, scale]); useEffect(() => { - setSize(); - window.addEventListener('resize', setSize); - return () => window.removeEventListener('resize', setSize); - }, [scale, text]); + const debouncedSetSize = debounce(setSize, 100); + debouncedSetSize(); + window.addEventListener('resize', debouncedSetSize); + return () => window.removeEventListener('resize', debouncedSetSize); + }, [setSize]); useEffect(() => { let rafId: number; @@ -133,18 +149,19 @@ const TextPressure: React.FC = ({ const d = dist(mouseRef.current, charCenter); - const getAttr = (distance: number, minVal: number, maxVal: number) => { - const val = maxVal - Math.abs((maxVal * distance) / maxDist); - return Math.max(minVal, val + minVal); - }; + const wdth = width ? Math.floor(getAttr(d, maxDist, 5, 200)) : 100; + const wght = weight ? Math.floor(getAttr(d, maxDist, 100, 900)) : 400; + const italVal = italic ? getAttr(d, maxDist, 0, 1).toFixed(2) : '0'; + const alphaVal = alpha ? getAttr(d, maxDist, 0, 1).toFixed(2) : '1'; - const wdth = width ? Math.floor(getAttr(d, 5, 200)) : 100; - const wght = weight ? Math.floor(getAttr(d, 100, 900)) : 400; - const italVal = italic ? getAttr(d, 0, 1).toFixed(2) : 0; - const alphaVal = alpha ? getAttr(d, 0, 1).toFixed(2) : 1; + const newFontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${italVal}`; - span.style.opacity = alphaVal.toString(); - span.style.fontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${italVal}`; + if (span.style.fontVariationSettings !== newFontVariationSettings) { + span.style.fontVariationSettings = newFontVariationSettings; + } + if (alpha && span.style.opacity !== alphaVal) { + span.style.opacity = alphaVal; + } }); } @@ -153,20 +170,10 @@ const TextPressure: React.FC = ({ animate(); return () => cancelAnimationFrame(rafId); - }, [width, weight, italic, alpha, chars.length]); + }, [width, weight, italic, alpha]); - const dynamicClassName = [className, flex ? 'flex' : '', stroke ? 'stroke' : ''].filter(Boolean).join(' '); - - return ( -
+ const styleElement = useMemo(() => { + return ( + ); + }, [fontFamily, fontUrl, flex, stroke, textColor, strokeColor]); + const dynamicClassName = [className, flex ? 'flex' : '', stroke ? 'stroke' : ''].filter(Boolean).join(' '); + + return ( +
+ {styleElement}

{ + const dx = b.x - a.x; + const dy = b.y - a.y; + return Math.sqrt(dx * dx + dy * dy); +}; + +const getAttr = (distance: number, maxDist: number, minVal: number, maxVal: number) => { + const val = maxVal - Math.abs((maxVal * distance) / maxDist); + return Math.max(minVal, val + minVal); +}; + +const debounce = (func: (...args: any[]) => void, delay: number) => { + let timeoutId: NodeJS.Timeout; + return (...args: any[]) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + func.apply(this, args); + }, delay); + }; +}; + const TextPressure: React.FC = ({ text = 'Compressa', fontFamily = 'Compressa VF', @@ -50,12 +71,6 @@ const TextPressure: React.FC = ({ const chars = text.split(''); - const dist = (a: { x: number; y: number }, b: { x: number; y: number }) => { - const dx = b.x - a.x; - const dy = b.y - a.y; - return Math.sqrt(dx * dx + dy * dy); - }; - useEffect(() => { const handleMouseMove = (e: MouseEvent) => { cursorRef.current.x = e.clientX; @@ -68,7 +83,7 @@ const TextPressure: React.FC = ({ }; window.addEventListener('mousemove', handleMouseMove); - window.addEventListener('touchmove', handleTouchMove, { passive: false }); + window.addEventListener('touchmove', handleTouchMove, { passive: true }); if (containerRef.current) { const { left, top, width, height } = containerRef.current.getBoundingClientRect(); @@ -84,7 +99,7 @@ const TextPressure: React.FC = ({ }; }, []); - const setSize = () => { + const setSize = useCallback(() => { if (!containerRef.current || !titleRef.current) return; const { width: containerW, height: containerH } = containerRef.current.getBoundingClientRect(); @@ -106,13 +121,14 @@ const TextPressure: React.FC = ({ setLineHeight(yRatio); } }); - }; + }, [chars.length, minFontSize, scale]); useEffect(() => { - setSize(); - window.addEventListener('resize', setSize); - return () => window.removeEventListener('resize', setSize); - }, [scale, text]); + const debouncedSetSize = debounce(setSize, 100); + debouncedSetSize(); + window.addEventListener('resize', debouncedSetSize); + return () => window.removeEventListener('resize', debouncedSetSize); + }, [setSize]); useEffect(() => { let rafId: number; @@ -135,18 +151,19 @@ const TextPressure: React.FC = ({ const d = dist(mouseRef.current, charCenter); - const getAttr = (distance: number, minVal: number, maxVal: number) => { - const val = maxVal - Math.abs((maxVal * distance) / maxDist); - return Math.max(minVal, val + minVal); - }; + const wdth = width ? Math.floor(getAttr(d, maxDist, 5, 200)) : 100; + const wght = weight ? Math.floor(getAttr(d, maxDist, 100, 900)) : 400; + const italVal = italic ? getAttr(d, maxDist, 0, 1).toFixed(2) : '0'; + const alphaVal = alpha ? getAttr(d, maxDist, 0, 1).toFixed(2) : '1'; - const wdth = width ? Math.floor(getAttr(d, 5, 200)) : 100; - const wght = weight ? Math.floor(getAttr(d, 100, 900)) : 400; - const italVal = italic ? getAttr(d, 0, 1).toFixed(2) : '0'; - const alphaVal = alpha ? getAttr(d, 0, 1).toFixed(2) : '1'; + const newFontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${italVal}`; - span.style.opacity = alphaVal; - span.style.fontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${italVal}`; + if (span.style.fontVariationSettings !== newFontVariationSettings) { + span.style.fontVariationSettings = newFontVariationSettings; + } + if (alpha && span.style.opacity !== alphaVal) { + span.style.opacity = alphaVal; + } }); } @@ -155,10 +172,10 @@ const TextPressure: React.FC = ({ animate(); return () => cancelAnimationFrame(rafId); - }, [width, weight, italic, alpha, chars.length]); + }, [width, weight, italic, alpha]); - return ( -
+ const styleElement = useMemo(() => { + return ( + ); + }, [fontFamily, fontUrl, stroke, textColor, strokeColor, strokeWidth]); + return ( +
+ {styleElement}