UNPKG

@visactor/vtable

Version:

canvas table width high performance

164 lines (149 loc) 7.24 kB
import { DefaultTextStyle, getTextBounds, DefaultTextMeasureContribution, TextMeasureContribution, ContainerModule, container, Text } from "./../../vrender"; import { isValid, TextMeasure } from "@visactor/vutils"; let customAlphabetCharSet = "", textMeasureMode = "quick"; const textMeasureModule = new ContainerModule(((bind, unbind, isBound, rebind) => { isBound(TextMeasureContribution) ? rebind(TextMeasureContribution).to(FastTextMeasureContribution).inSingletonScope() : bind(TextMeasureContribution).to(FastTextMeasureContribution).inSingletonScope(); })), restoreTextMeasureModule = new ContainerModule(((bind, unbind, isBound, rebind) => { isBound(TextMeasureContribution) ? rebind(TextMeasureContribution).to(DefaultTextMeasureContribution).inSingletonScope() : bind(TextMeasureContribution).to(DefaultTextMeasureContribution).inSingletonScope(); })); export default textMeasureModule; export const initTextMeasure = (textSpec, option, useNaiveCanvas) => new TextMeasure(Object.assign({ defaultFontParams: { fontFamily: DefaultTextStyle.fontFamily, fontSize: DefaultTextStyle.fontSize }, getTextBounds: useNaiveCanvas ? void 0 : getTextBounds, specialCharSet: `{}()//&-/: .,@%'"~…=——${TextMeasure.ALPHABET_CHAR_SET}${TextMeasure.ALPHABET_CHAR_SET.toUpperCase()}0123456789${customAlphabetCharSet}` }, null != option ? option : {}), textSpec); const fastTextMeasureCache = new Map; function getFastTextMeasure(fontSize, fontWeight, fontFamily, fontStyle = "normal") { const key = `${fontSize}-${fontWeight}-${fontFamily}-${fontStyle}`, cache = fastTextMeasureCache.get(key); if (cache) return cache; const fastTextMeasure = (textSpec = { fontSize: fontSize, fontFamily: fontFamily, fontWeight: fontWeight, fontStyle: fontStyle }, new TextMeasure(Object.assign({ defaultFontParams: { fontFamily: DefaultTextStyle.fontFamily, fontSize: DefaultTextStyle.fontSize }, getTextBounds: useNaiveCanvas ? void 0 : getTextBounds, specialCharSet: `{}()//&-/: .,@%'"~…=——${TextMeasure.ALPHABET_CHAR_SET}${TextMeasure.ALPHABET_CHAR_SET.toUpperCase()}0123456789${customAlphabetCharSet}` }, null != option ? option : {}), textSpec)); var textSpec, option, useNaiveCanvas; return fastTextMeasureCache.set(key, fastTextMeasure), fastTextMeasure; } export class FastTextMeasureContribution extends DefaultTextMeasureContribution { _fastMeasure(text, options) { const {fontSize: fontSize, fontFamily: fontFamily = "Arial,sans-serif", fontWeight: fontWeight = "normal", fontStyle: fontStyle = "normal"} = options, textMeasure = getFastTextMeasure(fontSize, fontWeight, fontFamily, fontStyle).measure(text, textMeasureMode); if (!isValid(textMeasure.fontBoundingBoxAscent) && !isValid(textMeasure.fontBoundingBoxDescent)) { const {ascent: ascent, descent: descent} = this.measureTextBoundADscentEstimate(options); textMeasure.fontBoundingBoxAscent = ascent, textMeasure.fontBoundingBoxDescent = descent; } return textMeasure; } measureTextWidth(text, options) { return this._fastMeasure(text, options).width; } measureText(text, options) { return this._fastMeasure(text, options); } _measureTextWithoutAlignBaseline(text, options, compatible) { return this._fastMeasure(text, options); } _measureTextWithAlignBaseline(text, options, compatible) { return this._fastMeasure(text, options); } } export class TextMeasureTool { measureText(text, options) { const {fontSize: fontSize, fontFamily: fontFamily = "Arial,sans-serif", fontWeight: fontWeight = "normal", fontStyle: fontStyle = "normal"} = options; return getFastTextMeasure(fontSize, fontWeight, fontFamily, fontStyle).measure(text, textMeasureMode); } measureTextWidth(text, options) { const {fontSize: fontSize, fontFamily: fontFamily = "Arial,sans-serif", fontWeight: fontWeight = "normal", fontStyle: fontStyle = "normal"} = options; return getFastTextMeasure(fontSize, fontWeight, fontFamily, fontStyle).measure(text, textMeasureMode).width; } clipText(text, options, width) { if (0 === text.length) return { str: "", width: 0 }; let length = this.measureTextWidth(text, options); return length <= width ? { str: text, width: length } : (length = this.measureTextWidth(text[0], options), length > width ? { str: "", width: 0 } : this._clipText(text, options, width, 0, text.length - 1)); } _clipText(text, options, width, leftIdx, rightIdx) { const middleIdx = Math.floor((leftIdx + rightIdx) / 2), subText = text.substring(0, middleIdx + 1), strWidth = this.measureTextWidth(subText, options); let length; if (strWidth > width) { if (subText.length <= 1) return { str: "", width: 0 }; const str = text.substring(0, middleIdx); return length = this.measureTextWidth(str, options), length <= width ? { str: str, width: length } : this._clipText(text, options, width, leftIdx, middleIdx); } if (strWidth < width) { if (middleIdx >= text.length - 1) return { str: text, width: this.measureTextWidth(text, options) }; const str = text.substring(0, middleIdx + 2); return length = this.measureTextWidth(str, options), length >= width ? { str: subText, width: strWidth } : this._clipText(text, options, width, middleIdx, rightIdx); } return { str: subText, width: strWidth }; } clipTextWithSuffix(text, options, width, suffix) { if ("" === suffix) return this.clipText(text, options, width); if (0 === text.length) return { str: "", width: 0 }; const length = this.measureTextWidth(text, options); if (length <= width) return { str: text, width: length }; const suffixWidth = this.measureTextWidth(suffix, options); if (suffixWidth > width) return { str: "", width: 0 }; width -= suffixWidth; const data = this._clipText(text, options, width, 0, text.length - 1); return data.str += suffix, data.width += suffixWidth, data; } } export const textMeasure = new TextMeasureTool; export function setCustomAlphabetCharSet(str) { customAlphabetCharSet = str, fastTextMeasureCache.clear(); } export function restoreMeasureText() { textMeasureMode = "canvas", container.load(restoreTextMeasureModule); } const utilTextMark = new Text({ ignoreBuf: !0 }); export function measureTextBounds(attribute) { return utilTextMark.initAttributes(Object.assign(Object.assign({}, attribute), { ignoreBuf: !0 })), utilTextMark.AABBBounds; } //# sourceMappingURL=text-measure.js.map