|
1 | | -import React, { useEffect, useState } from 'react'; |
| 1 | +import { RefObject, useMemo } from 'react'; |
| 2 | + |
| 3 | +export type useTextWidthTextOptions = { |
| 4 | + text: string | string[] | undefined; |
| 5 | + font?: string; |
| 6 | +}; |
| 7 | + |
| 8 | +export type useTextWidthRefOptions = { |
| 9 | + ref: RefObject<Element>; |
| 10 | +}; |
2 | 11 |
|
3 | 12 | export interface useTextWidthType { |
4 | | - ({ text, font, ref }: { text?: string | string[]; font?: string; ref?: any }): number; |
| 13 | + (options: useTextWidthTextOptions | useTextWidthRefOptions): number; |
5 | 14 | } |
6 | 15 |
|
7 | | -const useTextWidth: useTextWidthType = ({ text, ref, font = '16px Times' }) => { |
8 | | - const [width, setWidth] = useState(-1); |
| 16 | +const getContext = () => { |
| 17 | + const fragment: DocumentFragment = document.createDocumentFragment(); |
| 18 | + const canvas: HTMLCanvasElement = document.createElement('canvas'); |
| 19 | + fragment.appendChild(canvas); |
| 20 | + return canvas.getContext('2d') as CanvasRenderingContext2D; |
| 21 | +}; |
9 | 22 |
|
10 | | - useEffect(() => { |
11 | | - const getContext = () => { |
12 | | - const fragment: DocumentFragment = document.createDocumentFragment(); |
13 | | - const canvas: HTMLCanvasElement = document.createElement('canvas'); |
14 | | - fragment.appendChild(canvas); |
15 | | - return canvas.getContext('2d'); |
16 | | - }; |
| 23 | +const getTextWidth = (currentText: string | string[], font: string) => { |
| 24 | + const context = getContext(); |
| 25 | + context.font = font; |
17 | 26 |
|
18 | | - const getTextWidth = (currentText: string | string[]) => { |
19 | | - const context = getContext(); |
20 | | - context.font = font; |
| 27 | + if (Array.isArray(currentText)) { |
| 28 | + return Math.max(...currentText.map((t) => context.measureText(t).width)); |
| 29 | + } else { |
| 30 | + const metrics = context.measureText(currentText); |
| 31 | + return metrics.width; |
| 32 | + } |
| 33 | +}; |
21 | 34 |
|
22 | | - if (Array.isArray(currentText)) { |
23 | | - return Math.max(...currentText.map((t) => context.measureText(t).width)); |
24 | | - } else { |
25 | | - const metrics = context.measureText(currentText); |
26 | | - return metrics.width; |
27 | | - } |
28 | | - }; |
| 35 | +const useTextWidth: useTextWidthType = (options) => { |
| 36 | + const textOptions = useMemo(() => ('text' in options ? options : undefined), [options]); |
| 37 | + const refOptions = useMemo(() => ('ref' in options ? options : undefined), [options]); |
29 | 38 |
|
30 | | - if (ref && ref.current) { |
| 39 | + return useMemo(() => { |
| 40 | + if (refOptions?.ref.current?.textContent) { |
31 | 41 | const context = getContext(); |
32 | | - const computedStyles = window.getComputedStyle(ref.current); |
| 42 | + const computedStyles = window.getComputedStyle(refOptions.ref.current); |
33 | 43 | context.font = computedStyles.font; |
34 | | - const metrics = context.measureText(ref.current.textContent); |
35 | | - setWidth(metrics.width); |
36 | | - } else if (text) { |
37 | | - setWidth(getTextWidth(text)); |
| 44 | + const metrics = context.measureText(refOptions.ref.current.textContent); |
| 45 | + |
| 46 | + return metrics.width; |
| 47 | + } else if (textOptions?.text) { |
| 48 | + return getTextWidth(textOptions.text, textOptions.font ?? '16px times'); |
38 | 49 | } |
39 | | - }, [font, ref, text]); |
40 | 50 |
|
41 | | - return width; |
| 51 | + return NaN; |
| 52 | + }, [textOptions?.text, textOptions?.font, refOptions?.ref]); |
42 | 53 | }; |
43 | 54 |
|
44 | 55 | export default useTextWidth; |
0 commit comments