UNPKG

@visactor/vrender-core

Version:
339 lines (333 loc) 20.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: !0 }), exports.createText = exports.Text = void 0; const vutils_1 = require("@visactor/vutils"), text_1 = require("../common/text"), layout_1 = require("../core/contributions/textMeasure/layout"), application_1 = require("../application"), graphic_1 = require("./graphic"), theme_1 = require("./theme"), utils_1 = require("../common/utils"), constants_1 = require("./constants"), tools_1 = require("./tools"), TEXT_UPDATE_TAG_KEY = [ "text", "maxLineWidth", "maxWidth", "textAlign", "textBaseline", "heightLimit", "lineClamp", "fontSize", "fontFamily", "fontWeight", "ellipsis", "lineHeight", "direction", "wordBreak", "heightLimit", "lineClamp", ...graphic_1.GRAPHIC_UPDATE_TAG_KEY ]; class Text extends graphic_1.Graphic { get font() { const textTheme = this.getGraphicTheme(); return this._font || (this._font = (0, vutils_1.getContextFont)(this.attribute, textTheme)), this._font; } get clipedText() { var _a; const attribute = this.attribute, textTheme = this.getGraphicTheme(), maxWidth = this.getMaxWidth(textTheme); return Number.isFinite(maxWidth) ? (this.tryUpdateAABBBounds(), this.cache.clipedText) : (null !== (_a = attribute.text) && void 0 !== _a ? _a : textTheme.text).toString(); } get clipedWidth() { return this.tryUpdateAABBBounds(), this.cache.clipedWidth; } get cliped() { var _a, _b; const textTheme = this.getGraphicTheme(), attribute = this.attribute, maxWidth = this.getMaxWidth(textTheme); if (!Number.isFinite(maxWidth)) return !1; const {text: text} = this.attribute; if (this.tryUpdateAABBBounds(), null === (_b = null === (_a = this.cache) || void 0 === _a ? void 0 : _a.layoutData) || void 0 === _b ? void 0 : _b.lines) { let mergedText = ""; this.cache.layoutData.lines.forEach((item => { mergedText += item.str; })); return (Array.isArray(text) ? text.join("") : text) !== mergedText; } return "vertical" === attribute.direction && this.cache.verticalList && this.cache.verticalList[0] ? this.cache.verticalList[0].map((item => item.text)).join("") !== attribute.text.toString() : null != this.clipedText && this.clipedText !== attribute.text.toString(); } get multilineLayout() { return this.tryUpdateAABBBounds(), this.cache.layoutData; } get isMultiLine() { return Array.isArray(this.attribute.text) || "normal" === this.attribute.whiteSpace; } constructor(params = { text: "", fontSize: 16 }) { super(params), this.type = "text", this.numberType = constants_1.TEXT_NUMBER_TYPE, this.cache = {}; } isValid() { return super.isValid() && this._isValid(); } _isValid() { const {text: text} = this.attribute; return (0, vutils_1.isArray)(text) ? !text.every((t => null == t || "" === t)) : null != text && "" !== text; } getGraphicTheme() { return (0, theme_1.getTheme)(this).text; } doUpdateOBBBounds() { const graphicTheme = this.getGraphicTheme(); this._OBBBounds.clear(); const attribute = this.attribute, {angle: angle = graphicTheme.angle} = attribute; if (!angle) { const b = this.AABBBounds; return this._OBBBounds.setValue(b.x1, b.y1, b.x2, b.y2), this._OBBBounds; } this.obbText || (this.obbText = new Text({})), this.obbText.setAttributes(Object.assign(Object.assign({}, attribute), { angle: 0 })); const bounds1 = this.obbText.AABBBounds, {x: x, y: y} = attribute, boundsCenter = { x: (bounds1.x1 + bounds1.x2) / 2, y: (bounds1.y1 + bounds1.y2) / 2 }, center = (0, vutils_1.rotatePoint)(boundsCenter, angle, { x: x, y: y }); return this._OBBBounds.copy(bounds1), this._OBBBounds.translate(center.x - boundsCenter.x, center.y - boundsCenter.y), this._OBBBounds.angle = angle, this._OBBBounds; } updateAABBBounds(attribute, textTheme, aabbBounds) { const {text: text = textTheme.text} = this.attribute; Array.isArray(text) ? this.updateMultilineAABBBounds(text) : this.updateSingallineAABBBounds(text); const {tb1: tb1} = application_1.application.graphicService.updateTempAABBBounds(aabbBounds), {scaleX: scaleX = textTheme.scaleX, scaleY: scaleY = textTheme.scaleY, shadowBlur: shadowBlur = textTheme.shadowBlur, strokeBoundsBuffer: strokeBoundsBuffer = textTheme.strokeBoundsBuffer} = attribute; if (shadowBlur) { const shadowBlurHalfWidth = shadowBlur / Math.abs(scaleX + scaleY); (0, tools_1.boundStroke)(tb1, shadowBlurHalfWidth, !0, strokeBoundsBuffer), aabbBounds.union(tb1); } return application_1.application.graphicService.combindShadowAABBBounds(aabbBounds, this), null == attribute.forceBoundsHeight && null == attribute.forceBoundsWidth || application_1.application.graphicService.updateHTMLTextAABBBounds(attribute, textTheme, aabbBounds), (0, vutils_1.transformBoundsWithMatrix)(aabbBounds, aabbBounds, this.transMatrix), aabbBounds; } updateSingallineAABBBounds(text) { this.updateMultilineAABBBounds([ text ]); const layoutData = this.cache.layoutData; if (layoutData && layoutData.lines && layoutData.lines.length) { const line = layoutData.lines[0]; this.cache.clipedText = line.str, this.cache.clipedWidth = line.width; } return this._AABBBounds; } updateMultilineAABBBounds(text) { const textTheme = this.getGraphicTheme(), {direction: direction = textTheme.direction, underlineOffset: underlineOffset = textTheme.underlineOffset} = this.attribute, b = "horizontal" === direction ? this.updateHorizontalMultilineAABBBounds(text) : this.updateVerticalMultilineAABBBounds(text); return "horizontal" === direction && underlineOffset && this._AABBBounds.add(this._AABBBounds.x1, this._AABBBounds.y2 + underlineOffset), b; } guessLineHeightBuf(fontSize) { return fontSize ? .1 * fontSize : 0; } updateHorizontalMultilineAABBBounds(text) { var _a; const textTheme = this.getGraphicTheme(), attribute = this.attribute, {fontFamily: fontFamily = textTheme.fontFamily, textAlign: textAlign = textTheme.textAlign, textBaseline: textBaseline = textTheme.textBaseline, fontSize: fontSize = textTheme.fontSize, fontWeight: fontWeight = textTheme.fontWeight, ellipsis: ellipsis = textTheme.ellipsis, maxLineWidth: maxLineWidth, stroke: stroke = textTheme.stroke, wrap: wrap = textTheme.wrap, measureMode: measureMode = textTheme.measureMode, lineWidth: lineWidth = textTheme.lineWidth, whiteSpace: whiteSpace = textTheme.whiteSpace, suffixPosition: suffixPosition = textTheme.suffixPosition, ignoreBuf: ignoreBuf = textTheme.ignoreBuf, keepCenterInLine: keepCenterInLine = textTheme.keepCenterInLine} = attribute, buf = ignoreBuf ? 0 : this.guessLineHeightBuf(fontSize), lineHeight = this.getLineHeight(attribute, textTheme, buf); if ("normal" === whiteSpace || wrap) return this.updateWrapAABBBounds(text); if (!this.shouldUpdateShape() && (null === (_a = this.cache) || void 0 === _a ? void 0 : _a.layoutData)) { const bbox = this.cache.layoutData.bbox; return this._AABBBounds.set(bbox.xOffset, bbox.yOffset, bbox.xOffset + bbox.width, bbox.yOffset + bbox.height), stroke && this._AABBBounds.expand(lineWidth / 2), this._AABBBounds; } const textMeasure = application_1.application.graphicUtil.textMeasure, layoutData = new layout_1.CanvasTextLayout(fontFamily, { fontSize: fontSize, fontWeight: fontWeight, fontFamily: fontFamily, lineHeight: lineHeight }, textMeasure).GetLayoutByLines(text, textAlign, textBaseline, lineHeight, !0 === ellipsis ? textTheme.ellipsis : ellipsis || void 0, !1, { lineWidth: maxLineWidth, suffixPosition: suffixPosition, measureMode: measureMode, keepCenterInLine: keepCenterInLine }), {bbox: bbox} = layoutData; return this.cache.layoutData = layoutData, this.clearUpdateShapeTag(), this._AABBBounds.set(bbox.xOffset, bbox.yOffset, bbox.xOffset + bbox.width, bbox.yOffset + bbox.height), stroke && this._AABBBounds.expand(lineWidth / 2), this._AABBBounds; } updateWrapAABBBounds(text) { var _a, _b, _c; const textTheme = this.getGraphicTheme(), {fontFamily: fontFamily = textTheme.fontFamily, textAlign: textAlign = textTheme.textAlign, textBaseline: textBaseline = textTheme.textBaseline, fontSize: fontSize = textTheme.fontSize, ellipsis: ellipsis = textTheme.ellipsis, maxLineWidth: maxLineWidth, stroke: stroke = textTheme.stroke, lineWidth: lineWidth = textTheme.lineWidth, wordBreak: wordBreak = textTheme.wordBreak, fontWeight: fontWeight = textTheme.fontWeight, ignoreBuf: ignoreBuf = textTheme.ignoreBuf, measureMode: measureMode = textTheme.measureMode, suffixPosition: suffixPosition = textTheme.suffixPosition, heightLimit: heightLimit = 0, lineClamp: lineClamp, keepCenterInLine: keepCenterInLine = textTheme.keepCenterInLine} = this.attribute, buf = ignoreBuf ? 0 : this.guessLineHeightBuf(fontSize), lineHeight = this.getLineHeight(this.attribute, textTheme, buf); if (!this.shouldUpdateShape() && (null === (_a = this.cache) || void 0 === _a ? void 0 : _a.layoutData)) { const bbox = this.cache.layoutData.bbox; return this._AABBBounds.set(bbox.xOffset, bbox.yOffset, bbox.xOffset + bbox.width, bbox.yOffset + bbox.height), stroke && this._AABBBounds.expand(lineWidth / 2), this._AABBBounds; } const textMeasure = application_1.application.graphicUtil.textMeasure, textOptions = { fontSize: fontSize, fontWeight: fontWeight, fontFamily: fontFamily, lineHeight: lineHeight }, layoutObj = new layout_1.CanvasTextLayout(fontFamily, textOptions, textMeasure), lines = (0, vutils_1.isArray)(text) ? text.map((l => l.toString())) : [ text.toString() ], linesLayout = [], bboxWH = [ 0, 0 ]; let lineCountLimit = 1 / 0; if (heightLimit > 0 && (lineCountLimit = Math.max(Math.floor(heightLimit / lineHeight), 1)), lineClamp && (lineCountLimit = Math.min(lineCountLimit, lineClamp)), "number" == typeof maxLineWidth && maxLineWidth !== 1 / 0) { if (maxLineWidth > 0) for (let i = 0; i < lines.length; i++) { const str = lines[i]; let needCut = !0; if (i === lineCountLimit - 1) { const clip = textMeasure.clipTextWithSuffix(str, textOptions, maxLineWidth, ellipsis, !1, suffixPosition, i !== lines.length - 1), matrics = textMeasure.measureTextPixelADscentAndWidth(clip.str, textOptions, measureMode); linesLayout.push({ str: clip.str, width: clip.width, ascent: matrics.ascent, descent: matrics.descent, keepCenterInLine: keepCenterInLine }); break; } const clip = textMeasure.clipText(str, textOptions, maxLineWidth, "break-all" !== wordBreak, "keep-all" === wordBreak); if ("" !== str && "" === clip.str || clip.wordBreaked) { if (ellipsis) { const clipEllipsis = textMeasure.clipTextWithSuffix(str, textOptions, maxLineWidth, ellipsis, !1, suffixPosition); clip.str = null !== (_b = clipEllipsis.str) && void 0 !== _b ? _b : "", clip.width = null !== (_c = clipEllipsis.width) && void 0 !== _c ? _c : 0; } else clip.str = "", clip.width = 0; needCut = !1; } const matrics = textMeasure.measureTextPixelADscentAndWidth(clip.str, textOptions, measureMode); linesLayout.push({ str: clip.str, width: clip.width, ascent: matrics.ascent, descent: matrics.descent, keepCenterInLine: keepCenterInLine }); let cutLength = clip.str.length; if (!clip.wordBreaked || "" !== str && "" === clip.str || (needCut = !0, cutLength = clip.wordBreaked), clip.str.length === str.length) ; else if (needCut) { let newStr = str.substring(cutLength); "keep-all" === wordBreak && (newStr = newStr.replace(/^\s+/g, "")), lines.splice(i + 1, 0, newStr); } } let maxWidth = 0; linesLayout.forEach((layout => { maxWidth = Math.max(maxWidth, layout.width); })), bboxWH[0] = maxWidth; } else { let width, text, lineWidth = 0; for (let i = 0, len = lines.length; i < len; i++) { if (i === lineCountLimit - 1) { const clip = textMeasure.clipTextWithSuffix(lines[i], textOptions, maxLineWidth, ellipsis, !1, suffixPosition), matrics = textMeasure.measureTextPixelADscentAndWidth(clip.str, textOptions, measureMode); linesLayout.push({ str: clip.str, width: clip.width, ascent: matrics.ascent, descent: matrics.descent, keepCenterInLine: keepCenterInLine }), lineWidth = Math.max(lineWidth, clip.width); break; } text = lines[i], width = textMeasure.measureTextWidth(text, textOptions), lineWidth = Math.max(lineWidth, width); const matrics = textMeasure.measureTextPixelADscentAndWidth(text, textOptions, measureMode); linesLayout.push({ str: text, width: width, ascent: matrics.ascent, descent: matrics.descent, keepCenterInLine: keepCenterInLine }); } bboxWH[0] = lineWidth; } bboxWH[1] = linesLayout.length * lineHeight; const bbox = { xOffset: 0, yOffset: 0, width: bboxWH[0], height: bboxWH[1] }; layoutObj.LayoutBBox(bbox, textAlign, textBaseline, linesLayout); const layoutData = layoutObj.layoutWithBBox(bbox, linesLayout, textAlign, textBaseline, lineHeight); return this.cache.layoutData = layoutData, this.clearUpdateShapeTag(), this._AABBBounds.set(bbox.xOffset, bbox.yOffset, bbox.xOffset + bbox.width, bbox.yOffset + bbox.height), stroke && this._AABBBounds.expand(lineWidth / 2), this._AABBBounds; } updateVerticalMultilineAABBBounds(text) { var _a, _b; const textTheme = this.getGraphicTheme(), textMeasure = application_1.application.graphicUtil.textMeasure; let width; const attribute = this.attribute, {maxLineWidth: maxLineWidth = textTheme.maxLineWidth, ellipsis: ellipsis = textTheme.ellipsis, fontFamily: fontFamily = textTheme.fontFamily, fontSize: fontSize = textTheme.fontSize, fontWeight: fontWeight = textTheme.fontWeight, stroke: stroke = textTheme.stroke, lineWidth: lineWidth = textTheme.lineWidth, verticalMode: verticalMode = textTheme.verticalMode, suffixPosition: suffixPosition = textTheme.suffixPosition} = attribute, lineHeight = this.getLineHeight(attribute, textTheme, 0); let {textAlign: textAlign = textTheme.textAlign, textBaseline: textBaseline = textTheme.textBaseline} = attribute; if (!verticalMode) { const t = textAlign; textAlign = null !== (_a = Text.baselineMapAlign[textBaseline]) && void 0 !== _a ? _a : "left", textBaseline = null !== (_b = Text.alignMapBaseline[t]) && void 0 !== _b ? _b : "top"; } if (width = 0, !this.shouldUpdateShape() && this.cache) { this.cache.verticalList.forEach((item => { const w = item.reduce(((a, b) => a + b.width), 0); width = (0, vutils_1.max)(w, width); })); const dx = (0, text_1.textDrawOffsetX)(textAlign, width), height = this.cache.verticalList.length * lineHeight, dy = (0, text_1.textLayoutOffsetY)(textBaseline, height, fontSize); return this._AABBBounds.set(dy, dx, dy + height, dx + width), stroke && this._AABBBounds.expand(lineWidth / 2), this._AABBBounds; } const verticalLists = text.map((str => (0, tools_1.verticalLayout)(str.toString()))); verticalLists.forEach(((verticalData, i) => { if (Number.isFinite(maxLineWidth)) if (ellipsis) { const strEllipsis = !0 === ellipsis ? textTheme.ellipsis : ellipsis, data = textMeasure.clipTextWithSuffixVertical(verticalData, { fontSize: fontSize, fontWeight: fontWeight, fontFamily: fontFamily }, maxLineWidth, strEllipsis, !1, suffixPosition); verticalLists[i] = data.verticalList, width = data.width; } else { const data = textMeasure.clipTextVertical(verticalData, { fontSize: fontSize, fontWeight: fontWeight, fontFamily: fontFamily }, maxLineWidth, !1); verticalLists[i] = data.verticalList, width = data.width; } else width = 0, verticalData.forEach((t => { const w = t.direction === tools_1.TextDirection.HORIZONTAL ? fontSize : textMeasure.measureTextWidth(t.text, { fontSize: fontSize, fontWeight: fontWeight, fontFamily: fontFamily }); width += w, t.width = w; })); })), this.cache.verticalList = verticalLists, this.clearUpdateShapeTag(), this.cache.verticalList.forEach((item => { const w = item.reduce(((a, b) => a + b.width), 0); width = (0, vutils_1.max)(w, width); })); const dx = (0, text_1.textDrawOffsetX)(textAlign, width), height = this.cache.verticalList.length * lineHeight, dy = (0, text_1.textLayoutOffsetY)(textBaseline, height, fontSize); return this._AABBBounds.set(dy, dx, dy + height, dx + width), stroke && this._AABBBounds.expand(lineWidth / 2), this._AABBBounds; } getMaxWidth(theme) { var _a, _b; const attribute = this.attribute; return null !== (_b = null !== (_a = attribute.maxLineWidth) && void 0 !== _a ? _a : attribute.maxWidth) && void 0 !== _b ? _b : theme.maxWidth; } getLineHeight(attribute, textTheme, buf) { var _a; return null !== (_a = (0, utils_1.calculateLineHeight)(attribute.lineHeight, attribute.fontSize || textTheme.fontSize)) && void 0 !== _a ? _a : (attribute.fontSize || textTheme.fontSize) + buf; } needUpdateTags(keys, k = TEXT_UPDATE_TAG_KEY) { return super.needUpdateTags(keys, k); } needUpdateTag(key, k = TEXT_UPDATE_TAG_KEY) { return super.needUpdateTag(key, k); } _interpolate(key, ratio, lastStepVal, nextStepVal, nextAttributes) { "text" === key && (nextAttributes.text = nextStepVal); } clone() { return new Text(Object.assign({}, this.attribute)); } getNoWorkAnimateAttr() { return Text.NOWORK_ANIMATE_ATTR; } getBaselineMapAlign() { return Text.baselineMapAlign; } getAlignMapBaseline() { return Text.alignMapBaseline; } } function createText(attributes) { return new Text(attributes); } exports.Text = Text, Text.NOWORK_ANIMATE_ATTR = Object.assign({ ellipsis: 1, wordBreak: 1, direction: 1, textAlign: 1, textBaseline: 1, fontFamily: 1, fontWeight: 1 }, graphic_1.NOWORK_ANIMATE_ATTR), Text.baselineMapAlign = { top: "left", bottom: "right", middle: "center" }, Text.alignMapBaseline = { left: "top", right: "bottom", center: "middle" }, exports.createText = createText; //# sourceMappingURL=text.js.map