|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
如何固定列宽下数字过长时:先继承父级字号,再按设计稿字号逐步 -1 缩小,最小 12;仍超出才 `...`。 适配约定(375 设计稿): - `html.fontSize = (width / 375) * 100` - postcss `rootValue: 100` → 设计稿 px 转 rem:`px / 100` --- ## 封装:AutoFitText ```tsx import { useLayoutEffect, useRef, type ReactNode } from 'react' /** 与 index.html / postcss-pxtorem 一致 */ const DESIGN_ROOT_VALUE = 100 const MIN_DESIGN_FONT_SIZE = 12 const designPxToRem = (designPx: number) => `${designPx / DESIGN_ROOT_VALUE}rem` interface AutoFitTextProps { children: ReactNode className?: string } /** * 默认 inherit;超出后设计稿字号逐步 -1,最小 12;仍超出再省略号。 */ export const AutoFitText = ({ children, className }: AutoFitTextProps) => { const ref = useRef<HTMLSpanElement>(null) useLayoutEffect(() => { const el = ref.current if (!el) return const fit = () => { el.style.fontSize = '' el.classList.remove('is-ellipsis') if (el.clientWidth <= 0) return if (el.scrollWidth <= el.clientWidth + 1) return const rootPx = parseFloat(getComputedStyle(document.documentElement).fontSize) || DESIGN_ROOT_VALUE const inheritedCssPx = parseFloat(getComputedStyle(el).fontSize) || 14 let designSize = Math.round((inheritedCssPx / rootPx) * DESIGN_ROOT_VALUE) while ( designSize > MIN_DESIGN_FONT_SIZE && el.scrollWidth > el.clientWidth + 1 ) { designSize -= 1 el.style.fontSize = designPxToRem(designSize) } if (el.scrollWidth > el.clientWidth + 1) { el.classList.add('is-ellipsis') } } fit() window.addEventListener('resize', fit, false) return () => window.removeEventListener('resize', fit, false) }, [children]) return ( <span ref={ref} className={`auto-fit-text${className ? ` ${className}` : ''}`} > {children} </span> ) } ``` 配套样式: ```less .auto-fit-text { font-size: inherit; text-overflow: clip; &.is-ellipsis { text-overflow: ellipsis; } } /* 容器必须有固定宽度,文字节点占满宽度,才能正确测溢出 */ .demo-box { width: 120px; overflow: hidden; font-size: 16px; > span { display: block; width: 100%; max-width: 100%; overflow: hidden; white-space: nowrap; } } ``` --- ## 使用案例(独立 Demo) ```tsx import { useState } from 'react' import { AutoFitText } from './auto-fit-text' export default function AutoFitTextDemo() { const [text, setText] = useState('12345678901234567890') return ( <div> <input value={text} onChange={(e) => setText(e.target.value)} placeholder="输入一段文字试试" /> {/* 固定 120px 宽,观察字号缩小 / 省略号 */} <div className="demo-box"> <AutoFitText>{text}</AutoFitText> </div> </div> ) } ``` 效果说明: 1. 输入较短内容:保持父级 `16px` 2. 内容变长:字号逐步降到 `15 → 14 → … → 12` 3. 到 `12` 仍放不下:显示 `...` --- ## 注意点 1. 父容器要有明确宽度,且 `overflow: hidden` 2. 文字节点 `width: 100%` + `white-space: nowrap`,否则 `scrollWidth === clientWidth` 测不准 3. 缩小后写 rem(`设计稿px / 100`),不要写死 `px` 4. 未缩小时清空内联 `fontSize`,继续继承父级字号 |

