getTextSize.ts 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { LETTER_SPACING } from './constants'
  2. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  3. let melm: any
  4. function getMeasurementDiv() {
  5. // A div used for measurement
  6. document.getElementById('__textLabelMeasure')?.remove()
  7. const pre = document.createElement('pre')
  8. pre.id = '__textLabelMeasure'
  9. Object.assign(pre.style, {
  10. whiteSpace: 'pre',
  11. width: 'auto',
  12. border: '1px solid transparent',
  13. padding: '4px',
  14. margin: '0px',
  15. letterSpacing: LETTER_SPACING,
  16. opacity: '0',
  17. position: 'absolute',
  18. top: '-500px',
  19. left: '0px',
  20. zIndex: '9999',
  21. pointerEvents: 'none',
  22. userSelect: 'none',
  23. alignmentBaseline: 'mathematical',
  24. dominantBaseline: 'mathematical',
  25. })
  26. pre.tabIndex = -1
  27. document.body.appendChild(pre)
  28. return pre
  29. }
  30. if (typeof window !== 'undefined') {
  31. melm = getMeasurementDiv()
  32. }
  33. const cache = new Map<string, [number, number]>()
  34. const getKey = (text: string, font: string) => {
  35. return `${text}-${font}`
  36. }
  37. const hasCached = (text: string, font: string) => {
  38. const key = getKey(text, font)
  39. return cache.has(key)
  40. }
  41. const getCached = (text: string, font: string) => {
  42. const key = getKey(text, font)
  43. return cache.get(key)
  44. }
  45. const saveCached = (text: string, font: string, size: [number, number]) => {
  46. const key = getKey(text, font)
  47. cache.set(key, size)
  48. }
  49. export function getTextLabelSize(text: string, font: string) {
  50. if (!text) {
  51. return [16, 32]
  52. }
  53. if (!hasCached(text, font)) {
  54. if (!melm) {
  55. // We're in SSR
  56. return [10, 10]
  57. }
  58. if (!melm.parent) document.body.appendChild(melm)
  59. melm.textContent = text
  60. melm.style.font = font
  61. // In tests, offsetWidth and offsetHeight will be 0
  62. const width = melm.offsetWidth || 1
  63. const height = melm.offsetHeight || 1
  64. saveCached(text, font, [width, height])
  65. }
  66. return getCached(text, font)!
  67. }