基于vue3.0和element-plus的组件库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

143 lines
3.8 KiB

/**
* Text measurement utilities using @chenglou/pretext
* Provides "shrink wrap" width measurement via walkLineRanges
*
* IMPORTANT: All functions cache PreparedText handles internally.
* prepareWithSegments() calls are expensive (Canvas measureText), so we cache
* by text+font key to maximize reuse.
*/
import {
prepareWithSegments,
layout,
walkLineRanges,
type PreparedTextWithSegments,
} from "@chenglou/pretext";
/**
* Cache for PreparedText handles.
* Key: `${text}|${font}`, Value: PreparedTextWithSegments (the superset type)
*/
const preparedCache = new Map<string, PreparedTextWithSegments>();
/**
* Get or create a cached PreparedText handle.
* Uses prepareWithSegments since it returns the superset type that works
* with both layout() and walkLineRanges().
*/
function getPrepared(text: string, font: string): PreparedTextWithSegments {
const cacheKey = `${text}|${font}`;
let prepared = preparedCache.get(cacheKey);
if (!prepared) {
prepared = prepareWithSegments(text, font);
preparedCache.set(cacheKey, prepared);
// Limit cache size to prevent memory leaks (simple eviction)
if (preparedCache.size > 10000) {
// Simple strategy: clear oldest half when limit reached
const keys = preparedCache.keys();
let removed = 0;
const targetRemoval = preparedCache.size / 2;
for (const key of keys) {
if (removed >= targetRemoval) break;
preparedCache.delete(key);
removed++;
}
}
}
return prepared;
}
/**
* Clear the entire prepared text cache.
* Call this if font configuration changes globally.
*/
export function clearPreparedCache() {
preparedCache.clear();
}
/**
* Get cache statistics (for debugging)
*/
export function getPreparedCacheStats() {
return {
size: preparedCache.size,
};
}
export interface TextMeasurement {
/** Minimum width to contain all lines (widest line wins) */
shrinkWrapWidth: number;
/** Height at a given maxWidth */
heightAtWidth: (maxWidth: number) => number;
/** Line count at a given maxWidth */
lineCountAtWidth: (maxWidth: number) => number;
}
/**
* Prepare text for measurement. Results are cached internally.
* @param text - The text to measure (may contain \n)
* @param font - CSS font string
*/
export function measureText(text: string, font: string): TextMeasurement {
if (!text) {
return {
shrinkWrapWidth: 0,
heightAtWidth: () => 0,
lineCountAtWidth: () => 0,
};
}
const prepared = getPrepared(text, font);
// Find shrink-wrap width: maximum line width across all lines
let shrinkWrapWidth = 0;
walkLineRanges(prepared, 10000, (line) => {
if (line.width > shrinkWrapWidth) {
shrinkWrapWidth = line.width;
}
});
return {
shrinkWrapWidth,
heightAtWidth: (maxWidth: number) => {
const result = layout(prepared, maxWidth, 20); // 20 = default lineHeight
return result.height;
},
lineCountAtWidth: (maxWidth: number) => {
const result = layout(prepared, maxWidth, 20);
return result.lineCount;
},
};
}
/**
* Measure text and get shrink-wrap width in one call.
* Uses cached PreparedText handle for performance.
*/
export function measureShrinkWrapWidth(text: string, font: string): number {
if (!text) return 0;
const prepared = getPrepared(text, font);
let maxW = 0;
walkLineRanges(prepared, 10000, (line) => {
if (line.width > maxW) maxW = line.width;
});
return maxW;
}
/**
* Measure text height at a specific column width.
* Uses cached PreparedText handle for performance.
*/
export function measureTextHeight(
text: string,
font: string,
maxWidth: number,
lineHeight: number = 20
): number {
if (!text) return 0;
const prepared = getPrepared(text, font);
const result = layout(prepared, maxWidth, lineHeight);
return result.height;
}