snugtext
v1.0.3
Published
Динамический расчёт размера шрифта для идеальной подгонки текста в контейнер
Maintainers
Readme
SnugText
🎯 Динамический расчёт размера шрифта для идеальной подгонки текста в контейнер
Автоматически подбирает оптимальный font-size, чтобы текст идеально помещался в заданные размеры контейнера. Production-ready с кэшированием, debounce, lazy loading и всеми современными оптимизациями.
📦 Установка
# npm
npm install snugtext
# yarn
yarn add snugtext
# pnpm
pnpm add snugtext🚀 Быстрый старт (5 минут)
Vanilla JavaScript
import { fitText } from 'snugtext'
const result = fitText('Hello World', {
maxWidth: 300,
maxHeight: 100
})
console.log(result.fontSize) // 42.5 (px)
// Применяем к элементу
const element = document.querySelector('.title')
element.style.fontSize = `${result.fontSize}px`
element.textContent = 'Hello World'React
import { AutoFitText } from 'snugtext/react'
function MyComponent() {
return (
<AutoFitText
style={{ width: 300, height: 100 }}
minFontSize={12}
maxFontSize={48}
>
Hello World
</AutoFitText>
)
}HTML Setup
<!DOCTYPE html>
<html>
<head>
<script type="module">
import { fitText } from 'https://cdn.jsdelivr.net/npm/snugtext'
const result = fitText('Hello', { maxWidth: 400, maxHeight: 100 })
document.querySelector('.text').style.fontSize = `${result.fontSize}px`
</script>
</head>
<body>
<div class="text">Hello</div>
</body>
</html>📚 Таблица содержания
- fitText() - Однострочный текст
- fitTextMultiline() - Многострочный текст
- autoFitText() - Автоматические стили
- fitTextAll() - Массовая обработка
- createFitText() - Переиспользование
- React компоненты
- Практические примеры
- API Reference
- Производительность
🎯 fitText()
Вычисляет оптимальный размер шрифта для однострочного текста.
Пример 1: Базовое использование
import { fitText } from 'snugtext'
const result = fitText('My Text', {
maxWidth: 400,
maxHeight: 100
})
console.log(result)
// {
// fontSize: 38.25, // В пикселях
// width: 398, // Реальная ширина
// height: 95 // Реальная высота
// }Пример 2: С кастомным шрифтом
const result = fitText('Bold Text', {
maxWidth: 500,
maxHeight: 150,
fontFamily: 'Roboto',
fontWeight: 'bold',
fontStyle: 'italic'
})
// Применяем результат
const element = document.querySelector('.text')
element.style.fontSize = `${result.fontSize}px`
element.style.fontFamily = 'Roboto'
element.style.fontWeight = 'bold'
element.style.fontStyle = 'italic'
element.textContent = 'Bold Text'Пример 3: С ограничениями размера
const result = fitText('Limited Size', {
maxWidth: 600,
maxHeight: 200,
minFontSize: 16, // Не меньше 16px
maxFontSize: 72 // Не больше 72px
})
console.log(result.fontSize) // Будет между 16 и 72Пример 4: С единицами rem/em
// Результат в rem (базовый размер 16px)
const result = fitText('Responsive Text', {
maxWidth: 400,
maxHeight: 100,
unit: 'rem' // 'px' | 'rem' | 'em'
})
console.log(result.fontSize) // 2.5 (вместо 40px)
// В HTML
element.style.fontSize = `${result.fontSize}rem`Пример 5: Дополнительные стили
const result = fitText('Special', {
maxWidth: 300,
maxHeight: 100,
fontFamily: 'Arial',
fontWeight: '900',
lineHeight: 1.2,
additionalStyles: {
'letter-spacing': '0.1em',
'text-transform': 'uppercase',
'word-spacing': '0.2em'
}
})
element.style.fontSize = `${result.fontSize}px`
element.style.letterSpacing = '0.1em'
element.style.textTransform = 'uppercase'Пример 6: Разные контейнеры
// Для узкого контейнера
const narrowResult = fitText('Text', {
maxWidth: 100,
maxHeight: 50
})
console.log(narrowResult.fontSize) // Очень маленький
// Для широкого контейнера
const wideResult = fitText('Text', {
maxWidth: 800,
maxHeight: 300
})
console.log(wideResult.fontSize) // Большой размер🎯 fitTextMultiline()
Для многострочного текста с переносами и line-height.
Пример 1: С явными переносами
import { fitTextMultiline } from 'snugtext'
const result = fitTextMultiline(
'Строка 1\nСтрока 2\nСтрока 3',
{
maxWidth: 400,
maxHeight: 300,
lineHeight: 1.5
}
)
console.log(result.fontSize) // Размер подогнан для трёх строк
// Применяем
const element = document.querySelector('.text')
element.style.fontSize = `${result.fontSize}px`
element.style.lineHeight = '1.5'
element.textContent = 'Строка 1\nСтрока 2\nСтрока 3'Пример 2: Автоматический перенос
const text = 'Это длинный текст который автоматически переносится на новые строки чтобы уместиться в контейнер'
const result = fitTextMultiline(text, {
maxWidth: 250, // Узкий контейнер
maxHeight: 400,
fontFamily: 'Arial',
lineHeight: 1.4
})
element.style.fontSize = `${result.fontSize}px`
element.style.lineHeight = '1.4'
element.style.width = '250px'
element.textContent = textПример 3: Разный line-height
// Плотный текст
const dense = fitTextMultiline(text, {
maxWidth: 300,
maxHeight: 200,
lineHeight: 1.2 // Плотно
})
// Просторный текст
const spacious = fitTextMultiline(text, {
maxWidth: 300,
maxHeight: 200,
lineHeight: 1.8 // Редко
})
console.log(dense.fontSize) // Больше
console.log(spacious.fontSize) // МеньшеПример 4: С подзаголовками
// Заголовок
const titleResult = fitTextMultiline('Main Title', {
maxWidth: 600,
maxHeight: 150,
fontWeight: 'bold',
lineHeight: 1.2
})
// Описание под заголовком
const descResult = fitTextMultiline(
'Длинное описание которое может быть\nна нескольких строках',
{
maxWidth: 600,
maxHeight: 300,
lineHeight: 1.5,
fontWeight: 'normal'
}
)🎯 autoFitText()
Автоматически извлекает стили из CSS элемента.
Пример 1: Базовое использование
<style>
.title {
font-family: 'Montserrat', sans-serif;
font-weight: bold;
font-size: 16px;
width: 400px;
height: 100px;
color: #333;
}
</style>
<div class="title">My Beautiful Title</div>import { autoFitText } from 'snugtext'
const element = document.querySelector('.title')
// ✅ Автоматически использует:
// - font-family: 'Montserrat', sans-serif
// - font-weight: bold
// - width: 400px
// - height: 100px
autoFitText(element)
// element.style.fontSize будет установлен автоматическиПример 2: С переопределением параметров
autoFitText(element, {
minFontSize: 20, // Переопределяем минимум
maxFontSize: 64, // Переопределяем максимум
unit: 'rem' // Результат в rem
})Пример 3: Для многострочного текста
import { autoFitTextMultiline } from 'snugtext'
const paragraph = document.querySelector('.description')
autoFitTextMultiline(paragraph, {
lineHeight: 1.6,
minFontSize: 14,
maxFontSize: 24
})Пример 4: На разных элементах
// Заголовок
const h1 = document.querySelector('h1')
autoFitText(h1, {
minFontSize: 32,
maxFontSize: 96
})
// Подзаголовок
const h2 = document.querySelector('h2')
autoFitText(h2, {
minFontSize: 24,
maxFontSize: 64
})
// Параграф
const p = document.querySelector('p')
autoFitTextMultiline(p, {
minFontSize: 12,
maxFontSize: 18,
lineHeight: 1.5
})Пример 5: С обработкой ошибок
try {
const element = document.querySelector('.maybe-missing')
if (element) {
autoFitText(element, {
minFontSize: 10,
maxFontSize: 50
})
console.log('✅ Размер подогнан')
} else {
console.warn('⚠️ Элемент не найден')
}
} catch (error) {
console.error('❌ Ошибка:', error.message)
}🎯 fitTextAll()
Применяет fitText ко всем элементам по CSS селектору.
Пример 1: Базовое использование
<div class="card">
<h3 class="card-title">Product 1</h3>
</div>
<div class="card">
<h3 class="card-title">Product 2</h3>
</div>
<div class="card">
<h3 class="card-title">Product 3</h3>
</div>import { fitTextAll } from 'snugtext'
// ✅ Один вызов для всех элементов!
fitTextAll('.card-title', {
minFontSize: 14,
maxFontSize: 32
})
// Все три h3 получили оптимальный размер шрифтаПример 2: Разные селекторы
// Заголовки
fitTextAll('h1, h2, h3', {
fontWeight: 'bold',
minFontSize: 24,
maxFontSize: 72
})
// Описания
fitTextAll('.description', {
minFontSize: 12,
maxFontSize: 18,
lineHeight: 1.5
})
// Цены
fitTextAll('.price', {
fontWeight: '900',
minFontSize: 18,
maxFontSize: 42
})Пример 3: Селекторы с контекстом
// Только заголовки в карточках
fitTextAll('.product-card .title', {
minFontSize: 16,
maxFontSize: 28
})
// Только первый заголовок каждой карточки
fitTextAll('.card:first-child h2', {
fontWeight: 'bold'
})
// Все заголовки, кроме скрытых
fitTextAll('h1:not(.hidden)', {
minFontSize: 24,
maxFontSize: 64
})Пример 4: С проверкой
// Проверяем сколько элементов найдено
const elements = document.querySelectorAll('.title')
console.log(`Найдено элементов: ${elements.length}`)
fitTextAll('.title', {
minFontSize: 20,
maxFontSize: 56
})
// Проверяем результаты
elements.forEach((el, i) => {
const fontSize = window.getComputedStyle(el).fontSize
console.log(`${i + 1}. Font-size: ${fontSize}`)
})Пример 5: При динамическом добавлении элементов
// Исходные элементы
fitTextAll('.dynamic-title')
// Добавили новые элементы
function addNewCard(title) {
const card = document.createElement('div')
card.className = 'card'
card.innerHTML = `<h3 class="dynamic-title">${title}</h3>`
document.body.appendChild(card)
// ✅ Применяем к новым элементам
fitTextAll('.dynamic-title')
}
addNewCard('New Product')
addNewCard('Another Product')🎯 createFitText()
Создаёт переиспользуемую функцию с предустановками.
Пример 1: Базовое использование
import { createFitText } from 'snugtext'
// Создаём функцию с предустановками
const fitCardTitle = createFitText({
fontFamily: 'Roboto',
fontWeight: 'bold',
minFontSize: 14,
maxFontSize: 28,
unit: 'rem'
})
// Теперь используем просто так
const r1 = fitCardTitle('Title 1', { maxWidth: 250, maxHeight: 60 })
const r2 = fitCardTitle('Title 2', { maxWidth: 250, maxHeight: 60 })
const r3 = fitCardTitle('Title 3', { maxWidth: 300, maxHeight: 80 })
console.log(r1.fontSize) // в rem
console.log(r2.fontSize) // в remПример 2: Множественные конфигурации
// Конфигурация для заголовков
const fitHeading = createFitText({
fontFamily: 'Montserrat',
fontWeight: 'bold',
minFontSize: 24,
maxFontSize: 72,
lineHeight: 1.2
})
// Конфигурация для основного текста
const fitBody = createFitText({
fontFamily: 'Inter',
fontWeight: 'normal',
minFontSize: 12,
maxFontSize: 20,
lineHeight: 1.6
})
// Конфигурация для мелкого текста
const fitSmall = createFitText({
fontFamily: 'Arial',
minFontSize: 8,
maxFontSize: 14
})
// Используем везде
const h1 = fitHeading('Main Title', { maxWidth: 800, maxHeight: 150 })
const p = fitBody('Body text...', { maxWidth: 600, maxHeight: 300 })
const small = fitSmall('Note', { maxWidth: 200, maxHeight: 50 })Пример 3: Переопределение параметров
const standardFit = createFitText({
minFontSize: 12,
maxFontSize: 48,
fontFamily: 'Arial'
})
// Используем с дефолтными параметрами
const result1 = standardFit('Text 1', { maxWidth: 300, maxHeight: 100 })
// Переопределяем maxFontSize для этого случая
const result2 = standardFit('Special Text', {
maxWidth: 500,
maxHeight: 200,
maxFontSize: 100 // ✅ Переопределили!
})
console.log(result1.fontSize) // Макс 48
console.log(result2.fontSize) // Макс 100Пример 4: С единицами
// В rem
const remFit = createFitText({
unit: 'rem',
minFontSize: 0.75,
maxFontSize: 3
})
const remResult = remFit('Text', { maxWidth: 400, maxHeight: 100 })
console.log(remResult.fontSize) // Например: 2.5 (rem)
// В em
const emFit = createFitText({
unit: 'em',
minFontSize: 0.5,
maxFontSize: 2
})
const emResult = emFit('Text', { maxWidth: 400, maxHeight: 100 })
console.log(emResult.fontSize) // Например: 1.5 (em)Пример 5: Для React компонентов
import { createFitText } from 'snugtext'
import { useState, useEffect } from 'react'
const fitProductTitle = createFitText({
fontFamily: 'Roboto',
minFontSize: 14,
maxFontSize: 24
})
function ProductCard({ title, width = 250, height = 60 }) {
const [fontSize, setFontSize] = useState(16)
useEffect(() => {
const result = fitProductTitle(title, { maxWidth: width, maxHeight: height })
setFontSize(result.fontSize)
}, [title, width, height])
return (
<h3 style={{ fontSize: `${fontSize}px` }}>
{title}
</h3>
)
}⚛️ React компоненты
AutoFitText Component
Готовый компонент для React.
Пример 1: Базовое использование
import { AutoFitText } from 'snugtext/react'
function MyPage() {
return (
<div style={{ width: 300, height: 100, border: '1px solid' }}>
<AutoFitText minFontSize={12} maxFontSize={48}>
Adaptive Title
</AutoFitText>
</div>
)
}Пример 2: С явными размерами
<AutoFitText
style={{
width: 400,
height: 120,
border: '1px solid #ccc'
}}
minFontSize={16}
maxFontSize={56}
fontWeight="bold"
>
Product Title
</AutoFitText>Пример 3: WatchResize для адаптивности
function ResponsiveTitle() {
return (
<AutoFitText
style={{
width: '100%',
height: 120
}}
watchResize={true} // ✅ Следит за resize
resizeDebounce={150} // Debounce 150ms
minFontSize={20}
maxFontSize={80}
>
Responsive Text
</AutoFitText>
)
}Пример 4: Multiline текст
<AutoFitText
multiline={true}
lineHeight={1.6}
style={{
width: 500,
height: 300
}}
minFontSize={14}
maxFontSize={28}
>
Длинный текст который автоматически переносится на несколько строк
и подстраивает размер шрифта так чтобы всё уместилось в контейнер
</AutoFitText>Пример 5: С разными тегами
// h1
<AutoFitText
as="h1"
style={{ width: '100%', height: 100 }}
minFontSize={32}
maxFontSize={96}
fontWeight="900"
>
Main Title
</AutoFitText>
// p
<AutoFitText
as="p"
multiline
style={{ width: '100%', height: 300 }}
minFontSize={14}
maxFontSize={20}
>
Body paragraph text...
</AutoFitText>
// span
<AutoFitText
as="span"
style={{ width: 200, height: 40 }}
>
Inline text
</AutoFitText>Пример 6: С callback
<AutoFitText
style={{ width: 300, height: 100 }}
onResize={(fontSize) => {
console.log(`Font size changed to: ${fontSize}px`)
}}
watchResize
>
Text
</AutoFitText>Пример 7: Lazy loading
function LongList({ items }) {
return (
<div style={{ maxHeight: 600, overflow: 'auto' }}>
{items.map(item => (
<div key={item.id} style={{ height: 100 }}>
<AutoFitText
lazy={true} // ✅ Вычисляется только когда виден
style={{ width: 300, height: 80 }}
>
{item.title}
</AutoFitText>
</div>
))}
</div>
)
}FitTextProvider
Глобальный провайдер для настроек по умолчанию.
Пример 1: Базовое использование
import { FitTextProvider, AutoFitText } from 'snugtext/react'
function App() {
return (
<FitTextProvider
defaultOptions={{
minFontSize: 12,
maxFontSize: 48,
fontFamily: 'Inter',
unit: 'rem'
}}
>
{/* Все AutoFitText наследуют эти настройки */}
<AutoFitText style={{ width: 300, height: 80 }}>
Text 1 (использует default options)
</AutoFitText>
<AutoFitText style={{ width: 400, height: 100 }}>
Text 2 (использует default options)
</AutoFitText>
{/* Можно переопределить */}
<AutoFitText
style={{ width: 500, height: 120 }}
maxFontSize={72} // ✅ Переопределение
>
Text 3 (custom max size)
</AutoFitText>
</FitTextProvider>
)
}Пример 2: Вложенные провайдеры
<FitTextProvider defaultOptions={{ fontFamily: 'Roboto' }}>
{/* Секция заголовков */}
<FitTextProvider defaultOptions={{
minFontSize: 24,
maxFontSize: 72,
fontWeight: 'bold'
}}>
<AutoFitText style={{ width: 600, height: 100 }}>
Title 1
</AutoFitText>
<AutoFitText style={{ width: 600, height: 100 }}>
Title 2
</AutoFitText>
</FitTextProvider>
{/* Секция описаний */}
<FitTextProvider defaultOptions={{
minFontSize: 12,
maxFontSize: 18,
lineHeight: 1.5
}}>
<AutoFitText multiline style={{ width: 600, height: 300 }}>
Description text...
</AutoFitText>
</FitTextProvider>
</FitTextProvider>useFitText Hook
Хук для полного контроля.
Пример 1: Базовый хук
import { useFitText } from 'snugtext/react'
function MyText({ text }) {
const { ref, fontSize, dimensions } = useFitText(text, {
maxWidth: 300,
maxHeight: 100,
minFontSize: 12,
maxFontSize: 48
})
return (
<div
ref={ref}
style={{
fontSize: `${fontSize}px`,
width: 300,
height: 100
}}
>
{text}
</div>
)
}Пример 2: С информацией о размерах
function InfoComponent({ text }) {
const { ref, fontSize, unit, dimensions } = useFitText(text, {
maxWidth: 400,
maxHeight: 150,
unit: 'rem'
})
return (
<div>
<div
ref={ref}
style={{ fontSize: `${fontSize}rem` }}
>
{text}
</div>
<div className="debug">
<p>Font size: {fontSize}{unit}</p>
<p>Width: {dimensions.width}px</p>
<p>Height: {dimensions.height}px</p>
</div>
</div>
)
}Пример 3: С watchResize
function ResponsiveWithHook({ text }) {
const { ref, fontSize } = useFitText(text, {
maxWidth: 400,
maxHeight: 150,
watchResize: true,
debounce: 100
})
return (
<div
ref={ref}
style={{ fontSize: `${fontSize}px`, width: '100%' }}
>
{text}
</div>
)
}Пример 4: С мемоизацией
import { useMemo } from 'react'
import { useFitText } from 'snugtext/react'
function OptimizedComponent({ text, width, height }) {
// Мемоизируем options чтобы избежать бесконечных пересчётов
const options = useMemo(() => ({
maxWidth: width,
maxHeight: height,
minFontSize: 14,
maxFontSize: 56
}), [width, height])
const { ref, fontSize } = useFitText(text, options)
return (
<div ref={ref} style={{ fontSize: `${fontSize}px` }}>
{text}
</div>
)
}💡 Примеры использования
Пример 1: Система карточек товаров
import { fitTextAll } from 'snugtext'
function ProductCatalog() {
useEffect(() => {
// Применяем ко всем карточкам
fitTextAll('.product-title', {
fontWeight: 'bold',
minFontSize: 14,
maxFontSize: 28
})
fitTextAll('.product-price', {
fontWeight: '900',
minFontSize: 16,
maxFontSize: 32
})
fitTextAll('.product-description', {
minFontSize: 11,
maxFontSize: 16,
lineHeight: 1.4
})
}, [])
return (
<div className="catalog">
<div className="product-card">
<h3 className="product-title">Amazing Product</h3>
<p className="product-price">$99.99</p>
<p className="product-description">High quality product</p>
</div>
{/* More cards */}
</div>
)
}Пример 2: Адаптивный баннер
import { AutoFitText } from 'snugtext/react'
function Banner({ title, subtitle }) {
return (
<div className="banner" style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
padding: 40,
minHeight: 300
}}>
<AutoFitText
style={{
width: '100%',
height: 120,
color: 'white'
}}
watchResize
resizeDebounce={200}
fontWeight="900"
minFontSize={32}
maxFontSize={96}
>
{title}
</AutoFitText>
<AutoFitText
style={{
width: '100%',
height: 60,
color: 'rgba(255,255,255,0.8)',
marginTop: 20
}}
watchResize
fontWeight="500"
minFontSize={16}
maxFontSize={32}
>
{subtitle}
</AutoFitText>
</div>
)
}Пример 3: Форма с валидацией
import { AutoFitText } from 'snugtext/react'
function ContactForm() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const isValid = name.length >= 3 && email.includes('@')
return (
<form>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
{isValid && (
<AutoFitText
style={{
width: 300,
height: 60,
color: 'green',
padding: 20,
textAlign: 'center'
}}
minFontSize={14}
maxFontSize={32}
fontWeight="bold"
>
✅ Form is valid!
</AutoFitText>
)}
</form>
)
}Пример 4: Таблица с адаптивным текстом
import { AutoFitText } from 'snugtext/react'
function DataTable({ data }) {
return (
<table>
<thead>
<tr>
<th style={{ width: 150, height: 40 }}>
<AutoFitText minFontSize={10} maxFontSize={14}>
Product Name
</AutoFitText>
</th>
<th style={{ width: 100, height: 40 }}>
<AutoFitText minFontSize={10} maxFontSize={14}>
Price
</AutoFitText>
</th>
<th style={{ width: 120, height: 40 }}>
<AutoFitText minFontSize={10} maxFontSize={14}>
Quantity
</AutoFitText>
</th>
</tr>
</thead>
<tbody>
{data.map(row => (
<tr key={row.id}>
<td style={{ width: 150, height: 40 }}>
<AutoFitText minFontSize={9} maxFontSize={12}>
{row.name}
</AutoFitText>
</td>
<td style={{ width: 100, height: 40 }}>
<AutoFitText minFontSize={9} maxFontSize={12}>
${row.price}
</AutoFitText>
</td>
<td style={{ width: 120, height: 40 }}>
<AutoFitText minFontSize={9} maxFontSize={12}>
{row.quantity}
</AutoFitText>
</td>
</tr>
))}
</tbody>
</table>
)
}Пример 5: Длинный список с lazy loading
import { AutoFitText } from 'snugtext/react'
function ProductList({ products }) {
return (
<div className="products-container" style={{ maxHeight: 800, overflow: 'auto' }}>
{products.map(product => (
<div key={product.id} className="product-item" style={{ height: 100 }}>
<AutoFitText
lazy={true} // ✅ Вычисляется только когда виден!
style={{ width: 300, height: 80 }}
minFontSize={14}
maxFontSize={24}
>
{product.title}
</AutoFitText>
</div>
))}
</div>
)
}Пример 6: Тёмный режим
import { AutoFitText, FitTextProvider } from 'snugtext/react'
function App() {
const [isDark, setIsDark] = useState(false)
return (
<div style={{
background: isDark ? '#1a1a1a' : '#fff',
color: isDark ? '#fff' : '#000'
}}>
<button onClick={() => setIsDark(!isDark)}>
Toggle Dark Mode
</button>
<FitTextProvider defaultOptions={{
fontFamily: isDark ? 'Courier' : 'Roboto',
minFontSize: 14,
maxFontSize: 48
}}>
<AutoFitText style={{ width: 300, height: 100 }}>
{isDark ? 'Dark Mode' : 'Light Mode'}
</AutoFitText>
</FitTextProvider>
</div>
)
}📖 API Reference
fitText(text, options)
import { fitText } from 'snugtext'
const result = fitText(text: string, options: FitTextOptions): FitTextResult
// options
interface FitTextOptions {
maxWidth: number // Обязательно - ширина контейнера
maxHeight: number // Обязательно - высота контейнера
minFontSize?: number // Мин размер (default: 1)
maxFontSize?: number // Макс размер (default: 1000)
unit?: 'px' | 'rem' | 'em' // Единицы (default: 'px')
fontFamily?: string // Шрифт (default: 'Arial')
fontStyle?: string // Стиль (default: 'normal')
fontWeight?: string | number // Толщина (default: 'normal')
lineHeight?: string | number // Интервал (default: 'normal')
additionalStyles?: Record<string, string>
}
// result
interface FitTextResult {
fontSize: number // Оптимальный размер
width: number // Реальная ширина (px)
height: number // Реальная высота (px)
}fitTextMultiline(text, options)
import { fitTextMultiline } from 'snugtext'
const result = fitTextMultiline(
text: string,
options: FitTextOptions
): FitTextResult
// Используются те же options что и fitTextautoFitText(element, options?)
import { autoFitText } from 'snugtext'
autoFitText(element: HTMLElement, options?: Partial<FitTextOptions>): void
// Автоматически использует стили из CSS элементаfitTextAll(selector, options)
import { fitTextAll } from 'snugtext'
fitTextAll(selector: string, options: FitTextOptions): void
// Применяет ко всем найденным элементамcreateFitText(options)
import { createFitText } from 'snugtext'
const myFit = createFitText(options: Partial<FitTextOptions>)
const result = myFit(text: string, options: FitTextOptions): FitTextResult
// Возвращает функцию с предустановкамиУтилиты кэша
import {
clearMeasurementCache,
getCacheStats
} from 'snugtext'
// Очистить кэш
clearMeasurementCache(): void
// Получить статистику
getCacheStats(): { size: number; maxSize: number }⚡ Производительность
Оптимизации
SnugText включает все современные оптимизации:
1. Кэширование с TTL
// Первый вызов - 3ms
fitText('Hello', { maxWidth: 200, maxHeight: 50 })
// Повторный - 0.1ms (из кэша)
fitText('Hello', { maxWidth: 200, maxHeight: 50 })2. Бинарный поиск
Алгоритм O(log n) находит оптимальный размер за 6-8 итераций вместо 20-30.
// Быстро находит размер между minFontSize и maxFontSize
fitText('Text', {
maxWidth: 300,
maxHeight: 100,
minFontSize: 10,
maxFontSize: 100 // Поиск в диапазоне 10-100
})3. Ранний выход (early exit)
// Если найден "достаточно хороший" размер - алгоритм останавливается
// Экономит 1-2 вычисления
const result = fitText('Text', { maxWidth: 300, maxHeight: 100 })4. Batch DOM операции
import { batchDOMOperation } from 'snugtext/utils'
// ❌ 10 операций = 10 reflow
elements.forEach(el => el.style.fontSize = '20px')
// ✅ 1 операция = 1 reflow
batchDOMOperation(() => {
elements.forEach(el => el.style.fontSize = '20px')
})5. Lazy loading
// Вычисляется только когда элемент виден в viewport
<AutoFitText lazy>
Heavy calculation
</AutoFitText>Бенчмарки
Сценарий | Время | vs baseline
--------------------------------------------------
Одно измерение (fresh) | 2-3ms | 1x
Одно измерение (cached) | 0.1ms | 30x faster!
100 элементов (fitTextAll) | 150ms |
100 элементов (lazy) | 50ms | 3x faster!
React render (memoized) | 1ms |
React render (no memo) | 5ms | 5x slowerСоветы оптимизации
// ✅ 1. Используйте createFitText для переиспользования
const fit = createFitText({ fontFamily: 'Arial', minFontSize: 12 })
const r1 = fit('Text1', { maxWidth: 200, maxHeight: 50 }) // Быстро
const r2 = fit('Text2', { maxWidth: 200, maxHeight: 50 }) // Очень быстро
// ✅ 2. Используйте lazy для длинных списков
<AutoFitText lazy>Heavy Text</AutoFitText>
// ✅ 3. Используйте debounce для resize
const debouncedResize = debounce(fitTextAll, 150)
window.addEventListener('resize', debouncedResize)
// ✅ 4. Используйте watchResize с debounce
<AutoFitText watchResize resizeDebounce={200}>
Text
</AutoFitText>
// ✅ 5. Очищайте кэш при смене шрифтов
function changeFont(newFont) {
applyFont(newFont)
clearMeasurementCache() // Очистить кэш
fitTextAll('.text') // Пересчитать
}🧪 Обработка ошибок
Некорректные параметры
try {
fitText('Text', {
maxWidth: -100, // ❌ Отрицательное
maxHeight: 50
})
} catch (error) {
if (error instanceof RangeError) {
console.error('Invalid parameters:', error.message)
}
}Проверка перед использованием
function safeFitText(text, options) {
// Проверяем параметры
if (!text || typeof text !== 'string') {
throw new TypeError('Text must be a non-empty string')
}
if (options.maxWidth <= 0 || options.maxHeight <= 0) {
throw new RangeError('maxWidth and maxHeight must be positive')
}
return fitText(text, options)
}Обработка в React
function SafeAutoFitText({ text, ...props }) {
const [error, setError] = useState(null)
if (!text) {
return <div>No text provided</div>
}
return (
<div>
{error && <div className="error">Error: {error}</div>}
<AutoFitText {...props}>
{text}
</AutoFitText>
</div>
)
}🔗 Ссылки
📄 Лицензия
Apache-2.0 © Bilal
💬 FAQ
Q: Почему текст не помещается? A: Убедитесь что maxWidth и maxHeight достаточно большие, или уменьшите minFontSize.
Q: Как использовать с Next.js? A: Просто добавьте 'use client' в начало файла компонента при использовании App Router.
Q: Работает ли с SSR? A: Да, но пересчёт происходит после гидрации на клиенте.
Q: Как очистить кэш?
A: Используйте clearMeasurementCache() при смене шрифтов или тем.
