Spaces:
Running
Running
File size: 4,044 Bytes
181bd77 a522ff9 c82338a 181bd77 c82338a 181bd77 c82338a 181bd77 c82338a 181bd77 c82338a a522ff9 c82338a 075181d c82338a 075181d c82338a 075181d c82338a 075181d c82338a 075181d c82338a 181bd77 c82338a 181bd77 c82338a a88f8ce 181bd77 |
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 |
/**
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
*
* @param {String} text The text to be rendered.
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
*
* @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
*/
import { useTimeline } from ".."
function getTextWidthInCanvas(text: string, font: string) {
if (typeof window === "undefined") {
return 0
}
const canvas = document.createElement("canvas")
const context = canvas.getContext("2d")
if (!context) { return 0 }
context.font = font
const metrics = context.measureText(text)
return metrics.width
}
// one option could be to pre-compute some of the width
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789()_-"+=,;:?/\\&@#'.split('')
const charLength = characters.reduce((acc, char) => ({
...acc,
[char]: getTextWidthInCanvas(char, "bold Arial")
}), {} as Record<string, number>)
let defaultCharLength = 5.561523437
// change this whenever you modify the font size
const webglFontWidthFactor = 1.7
/**
* Compute the text of a simple Arial text in a WebGL environmment
* This actually just do a lookup + sum
*
* @param text
* @returns
*/
export function getWebGLCharWidth(char: string = ""): number {
const cellWidthInPixels = useTimeline.getState().cellWidth
let responsiveHack = 1.5
if (cellWidthInPixels < 16) {
responsiveHack = 1.20
} else if (cellWidthInPixels < 20) {
responsiveHack = 1.10
} else if (cellWidthInPixels < 24) {
responsiveHack = 1.05
} else if (cellWidthInPixels < 28) {
responsiveHack = 1
} else if (cellWidthInPixels < 32) {
responsiveHack = 1.0
} else if (cellWidthInPixels < 48) {
responsiveHack = 0.9
} else if (cellWidthInPixels < 64) {
responsiveHack = 0.9
} else if (cellWidthInPixels < 128) {
responsiveHack = 0.8
} else {
responsiveHack = 0.7
}
return responsiveHack * webglFontWidthFactor * (charLength[char] || defaultCharLength)
}
/**
* Compute the text of a simple Arial text in a WebGL environmment
* This actually just do a lookup + sum
*
* @param text
* @returns
*/
export function getWebGLTextWidth(text: string = ""): number {
return text.split('').reduce((s, c) => (s + getWebGLCharWidth(c)), 0)
}
/**
* Clamp a text to a given
* @param text
* @param maxWidthInPixels
* @returns
*/
export function clampWebGLText(
input: string,
maxWidthInPixels: number,
maxNbLines: number
): string[] {
let buffer = ""
let width = 0
let lines: string[] = []
const text = `${input || ""}`.replace('\n', ' ').trim()
const characters = text.split('')
for (const c of characters) {
width += getWebGLCharWidth(c)
buffer += c
if (width >= maxWidthInPixels) {
if (lines.length >= (maxNbLines - 1)) {
buffer = buffer.trim() // to avoid writing "and .."
buffer += ".."
break
} else {
// TODO: we should do something smarter, which is to split the last sentence
const words = buffer.split(" ")
const lastWord = (words.at(-1) || "")
if (lastWord.length) {
lines.push(words.slice(0, -1).join(" "))
buffer = lastWord
width = getWebGLTextWidth(lastWord)
} else {
lines.push(buffer)
buffer = ""
width = 0
}
}
}
}
if (buffer.length) {
lines.push(buffer)
}
return lines
}
export function clampWebGLTextNaive(input: string = "", maxWidthInPixels: number = 0): string {
// this cutoff is very approximate as we should make it dependent on each character's width
// a simple heuristic can be to count the uppercase / lower case
const maxhInCharacter = Math.ceil(maxWidthInPixels / 3.4)
const text = `${input || ""}`
return (text.length >= maxhInCharacter)
? `${text.slice(0, maxhInCharacter)}..`
: text
} |