RichText.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. "use strict";
  2. exports.__esModule = true;
  3. var tslib_1 = require("tslib");
  4. var parseText_1 = require("./helper/parseText");
  5. var Text_1 = require("./Text");
  6. var util_1 = require("../core/util");
  7. var text_1 = require("../contain/text");
  8. var Image_1 = require("./Image");
  9. var Rect_1 = require("./shape/Rect");
  10. var BoundingRect_1 = require("../core/BoundingRect");
  11. var Displayable_1 = require("./Displayable");
  12. var DEFAULT_RICH_TEXT_COLOR = {
  13. fill: '#000'
  14. };
  15. var RichText = (function (_super) {
  16. tslib_1.__extends(RichText, _super);
  17. function RichText(opts) {
  18. var _this = _super.call(this) || this;
  19. _this.type = 'richtext';
  20. _this._children = [];
  21. _this._defaultColor = DEFAULT_RICH_TEXT_COLOR;
  22. _this.attr(opts);
  23. return _this;
  24. }
  25. RichText.prototype.childrenRef = function () {
  26. return this._children;
  27. };
  28. RichText.prototype.update = function () {
  29. if (this.__dirtyStyle) {
  30. this._updateSubTexts();
  31. }
  32. _super.prototype.update.call(this);
  33. };
  34. RichText.prototype._updateSubTexts = function () {
  35. this._childCursor = 0;
  36. normalizeTextStyle(this.style);
  37. this.style.rich
  38. ? this._updateRichTexts()
  39. : this._updatePlainTexts();
  40. this._children.length = this._childCursor;
  41. for (var i = 0; i < this._children.length; i++) {
  42. var child = this._children[i];
  43. child.zlevel = this.zlevel;
  44. child.z = this.z;
  45. child.z2 = this.z2;
  46. child.culling = this.culling;
  47. child.cursor = this.cursor;
  48. child.invisible = this.invisible;
  49. }
  50. };
  51. RichText.prototype.getBoundingRect = function () {
  52. if (this.__dirtyStyle) {
  53. this._updateSubTexts();
  54. }
  55. if (!this._rect) {
  56. var tmpRect = new BoundingRect_1["default"](0, 0, 0, 0);
  57. var children = this._children;
  58. var tmpMat = [];
  59. var rect = null;
  60. for (var i = 0; i < children.length; i++) {
  61. var child = children[i];
  62. var childRect = child.getBoundingRect();
  63. var transform = child.getLocalTransform(tmpMat);
  64. if (transform) {
  65. tmpRect.copy(childRect);
  66. tmpRect.applyTransform(transform);
  67. rect = rect || tmpRect.clone();
  68. rect.union(tmpRect);
  69. }
  70. else {
  71. rect = rect || childRect.clone();
  72. rect.union(childRect);
  73. }
  74. }
  75. this._rect = rect || tmpRect;
  76. }
  77. return this._rect;
  78. };
  79. RichText.prototype.setDefaultTextColor = function (defaultTextStyle) {
  80. this._defaultColor = defaultTextStyle || DEFAULT_RICH_TEXT_COLOR;
  81. };
  82. RichText.prototype.setTextContent = function (textContent) {
  83. throw new Error('Can\'t attach richText on another richText');
  84. };
  85. RichText.prototype._mergeStyle = function (targetStyle, sourceStyle) {
  86. var sourceRich = sourceStyle.rich;
  87. var targetRich = targetStyle.rich || (sourceRich && {});
  88. util_1.extend(targetStyle, sourceStyle);
  89. if (sourceRich && targetRich) {
  90. this._mergeRich(targetRich, sourceRich);
  91. targetStyle.rich = targetRich;
  92. }
  93. else if (targetRich) {
  94. targetStyle.rich = targetRich;
  95. }
  96. return targetStyle;
  97. };
  98. RichText.prototype._mergeRich = function (targetRich, sourceRich) {
  99. var richNames = util_1.keys(sourceRich);
  100. for (var i = 0; i < richNames.length; i++) {
  101. var richName = richNames[i];
  102. targetRich[richName] = targetRich[richName] || {};
  103. util_1.extend(targetRich[richName], sourceRich[richName]);
  104. }
  105. };
  106. RichText.prototype._getOrCreateChild = function (Ctor) {
  107. var child = this._children[this._childCursor];
  108. if (!child || !(child instanceof Ctor)) {
  109. child = new Ctor();
  110. }
  111. this._children[this._childCursor++] = child;
  112. child.parent = this;
  113. return child;
  114. };
  115. RichText.prototype._updatePlainTexts = function () {
  116. var style = this.style;
  117. var text = style.text || '';
  118. var textFont = style.font || text_1.DEFAULT_FONT;
  119. var textPadding = style.padding;
  120. var contentBlock = parseText_1.parsePlainText(text, style);
  121. var needDrawBg = needDrawBackground(style);
  122. var outerHeight = contentBlock.outerHeight;
  123. var textLines = contentBlock.lines;
  124. var lineHeight = contentBlock.lineHeight;
  125. var defaultColor = this._defaultColor;
  126. var baseX = style.x || 0;
  127. var baseY = style.y || 0;
  128. var textAlign = style.align || 'left';
  129. var textVerticalAlign = style.verticalAlign;
  130. var boxY = text_1.adjustTextY(baseY, outerHeight, textVerticalAlign);
  131. var textX = baseX;
  132. var textY = boxY;
  133. if (needDrawBg || textPadding) {
  134. var outerWidth_1 = contentBlock.width;
  135. textPadding && (outerWidth_1 += textPadding[1] + textPadding[3]);
  136. var boxX = text_1.adjustTextX(baseX, outerWidth_1, textAlign);
  137. needDrawBg && this._renderBackground(style, boxX, boxY, outerWidth_1, outerHeight);
  138. if (textPadding) {
  139. textX = getTextXForPadding(baseX, textAlign, textPadding);
  140. textY += textPadding[0];
  141. }
  142. }
  143. textY += lineHeight / 2;
  144. var textStrokeLineWidth = style.lineWidth;
  145. var textStroke = getStroke('stroke' in style ? style.stroke : defaultColor.stroke);
  146. var textFill = getFill('fill' in style ? style.fill : defaultColor.fill);
  147. var hasShadow = style.textShadowBlur > 0;
  148. for (var i = 0; i < textLines.length; i++) {
  149. var el = this._getOrCreateChild(Text_1["default"]);
  150. var subElStyle = el.style;
  151. subElStyle.text = textLines[i];
  152. subElStyle.x = textX;
  153. subElStyle.y = textY;
  154. if (textAlign) {
  155. subElStyle.textAlign = textAlign;
  156. }
  157. subElStyle.textBaseline = 'middle';
  158. subElStyle.opacity = style.opacity;
  159. subElStyle.strokeFirst = true;
  160. if (hasShadow) {
  161. subElStyle.shadowBlur = style.textShadowBlur || 0;
  162. subElStyle.shadowColor = style.textShadowColor || 'transparent';
  163. subElStyle.shadowOffsetX = style.textShadowOffsetX || 0;
  164. subElStyle.shadowOffsetY = style.textShadowOffsetY || 0;
  165. }
  166. if (textStroke) {
  167. subElStyle.stroke = textStroke;
  168. subElStyle.lineWidth = textStrokeLineWidth || defaultColor.lineWidth;
  169. }
  170. if (textFill) {
  171. subElStyle.fill = textFill;
  172. }
  173. subElStyle.font = textFont;
  174. textY += lineHeight;
  175. }
  176. };
  177. RichText.prototype._updateRichTexts = function () {
  178. var style = this.style;
  179. var contentBlock = parseText_1.parseRichText(style.text || '', style);
  180. var contentWidth = contentBlock.width;
  181. var outerWidth = contentBlock.outerWidth;
  182. var outerHeight = contentBlock.outerHeight;
  183. var textPadding = style.padding;
  184. var baseX = style.x || 0;
  185. var baseY = style.y || 0;
  186. var textAlign = style.align;
  187. var textVerticalAlign = style.verticalAlign;
  188. var boxX = text_1.adjustTextX(baseX, outerWidth, textAlign);
  189. var boxY = text_1.adjustTextY(baseY, outerHeight, textVerticalAlign);
  190. var xLeft = boxX;
  191. var lineTop = boxY;
  192. if (textPadding) {
  193. xLeft += textPadding[3];
  194. lineTop += textPadding[0];
  195. }
  196. var xRight = xLeft + contentWidth;
  197. if (needDrawBackground(style)) {
  198. this._renderBackground(style, boxX, boxY, outerWidth, outerHeight);
  199. }
  200. for (var i = 0; i < contentBlock.lines.length; i++) {
  201. var line = contentBlock.lines[i];
  202. var tokens = line.tokens;
  203. var tokenCount = tokens.length;
  204. var lineHeight = line.lineHeight;
  205. var usedWidth = line.width;
  206. var leftIndex = 0;
  207. var lineXLeft = xLeft;
  208. var lineXRight = xRight;
  209. var rightIndex = tokenCount - 1;
  210. var token = void 0;
  211. while (leftIndex < tokenCount
  212. && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left')) {
  213. this._placeToken(token, style, lineHeight, lineTop, lineXLeft, 'left');
  214. usedWidth -= token.width;
  215. lineXLeft += token.width;
  216. leftIndex++;
  217. }
  218. while (rightIndex >= 0
  219. && (token = tokens[rightIndex], token.textAlign === 'right')) {
  220. this._placeToken(token, style, lineHeight, lineTop, lineXRight, 'right');
  221. usedWidth -= token.width;
  222. lineXRight -= token.width;
  223. rightIndex--;
  224. }
  225. lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2;
  226. while (leftIndex <= rightIndex) {
  227. token = tokens[leftIndex];
  228. this._placeToken(token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center');
  229. lineXLeft += token.width;
  230. leftIndex++;
  231. }
  232. lineTop += lineHeight;
  233. }
  234. };
  235. RichText.prototype._placeToken = function (token, style, lineHeight, lineTop, x, textAlign) {
  236. var tokenStyle = style.rich[token.styleName] || {};
  237. tokenStyle.text = token.text;
  238. var textVerticalAlign = token.textVerticalAlign;
  239. var y = lineTop + lineHeight / 2;
  240. if (textVerticalAlign === 'top') {
  241. y = lineTop + token.height / 2;
  242. }
  243. else if (textVerticalAlign === 'bottom') {
  244. y = lineTop + lineHeight - token.height / 2;
  245. }
  246. !token.isLineHolder && needDrawBackground(tokenStyle) && this._renderBackground(tokenStyle, textAlign === 'right'
  247. ? x - token.width
  248. : textAlign === 'center'
  249. ? x - token.width / 2
  250. : x, y - token.height / 2, token.width, token.height);
  251. var textPadding = token.textPadding;
  252. if (textPadding) {
  253. x = getTextXForPadding(x, textAlign, textPadding);
  254. y -= token.height / 2 - textPadding[2] - token.height / 2;
  255. }
  256. var el = this._getOrCreateChild(Text_1["default"]);
  257. var subElStyle = el.style;
  258. var defaultColor = this._defaultColor;
  259. var textStroke = getStroke('stroke' in tokenStyle ? tokenStyle.stroke
  260. : 'stroke' in style ? style.stroke : defaultColor.stroke);
  261. var textFill = getStroke('fill' in tokenStyle ? tokenStyle.fill
  262. : 'fill' in style ? style.fill : defaultColor.fill);
  263. var hasShadow = tokenStyle.textShadowBlur > 0
  264. || style.textShadowBlur > 0;
  265. subElStyle.text = token.text;
  266. subElStyle.x = x;
  267. subElStyle.y = y;
  268. if (hasShadow) {
  269. subElStyle.shadowBlur = tokenStyle.textShadowBlur || style.textShadowBlur || 0;
  270. subElStyle.shadowColor = tokenStyle.textShadowColor || style.textShadowColor || 'transparent';
  271. subElStyle.shadowOffsetX = tokenStyle.textShadowOffsetX || style.textShadowOffsetX || 0;
  272. subElStyle.shadowOffsetY = tokenStyle.textShadowOffsetY || style.textShadowOffsetY || 0;
  273. }
  274. subElStyle.textAlign = textAlign;
  275. subElStyle.textBaseline = 'middle';
  276. subElStyle.font = token.font || text_1.DEFAULT_FONT;
  277. if (textStroke) {
  278. subElStyle.lineWidth = util_1.retrieve3(tokenStyle.lineWidth, style.lineWidth, defaultColor.lineWidth);
  279. subElStyle.stroke = textStroke;
  280. }
  281. if (textFill) {
  282. subElStyle.fill = textFill;
  283. }
  284. };
  285. RichText.prototype._renderBackground = function (style, x, y, width, height) {
  286. var textBackgroundColor = style.backgroundColor;
  287. var textBorderWidth = style.borderWidth;
  288. var textBorderColor = style.borderColor;
  289. var isPlainBg = util_1.isString(textBackgroundColor);
  290. var textBorderRadius = style.borderRadius;
  291. var self = this;
  292. var rectEl;
  293. var imgEl;
  294. if (isPlainBg || (textBorderWidth && textBorderColor)) {
  295. rectEl = this._getOrCreateChild(Rect_1["default"]);
  296. rectEl.style.fill = null;
  297. var rectShape = rectEl.shape;
  298. rectShape.x = x;
  299. rectShape.y = y;
  300. rectShape.width = width;
  301. rectShape.height = height;
  302. rectShape.r = textBorderRadius;
  303. rectEl.dirtyShape();
  304. }
  305. if (isPlainBg) {
  306. var rectStyle = rectEl.style;
  307. rectStyle.fill = textBackgroundColor || null;
  308. rectStyle.opacity = util_1.retrieve2(style.opacity, 1);
  309. rectStyle.fillOpacity = util_1.retrieve2(style.fillOpacity, 1);
  310. }
  311. else if (textBackgroundColor && textBackgroundColor.image) {
  312. imgEl = this._getOrCreateChild(Image_1["default"]);
  313. imgEl.onload = function () {
  314. self.dirtyStyle();
  315. };
  316. var imgStyle = imgEl.style;
  317. imgStyle.image = textBackgroundColor.image;
  318. imgStyle.x = x;
  319. imgStyle.y = y;
  320. imgStyle.width = width;
  321. imgStyle.height = height;
  322. }
  323. if (textBorderWidth && textBorderColor) {
  324. var rectStyle = rectEl.style;
  325. rectStyle.lineWidth = textBorderWidth;
  326. rectStyle.stroke = textBorderColor;
  327. rectStyle.strokeOpacity = util_1.retrieve2(style.strokeOpacity, 1);
  328. }
  329. var shadowStyle = (rectEl || imgEl).style;
  330. shadowStyle.shadowBlur = style.shadowBlur || 0;
  331. shadowStyle.shadowColor = style.shadowColor || 'transparent';
  332. shadowStyle.shadowOffsetX = style.shadowOffsetX || 0;
  333. shadowStyle.shadowOffsetY = style.shadowOffsetY || 0;
  334. };
  335. RichText.makeFont = function (style) {
  336. var font = (style.fontSize || style.fontFamily) && [
  337. style.fontStyle,
  338. style.fontWeight,
  339. (style.fontSize || 12) + 'px',
  340. style.fontFamily || 'sans-serif'
  341. ].join(' ');
  342. return font && util_1.trim(font) || style.textFont || style.font;
  343. };
  344. return RichText;
  345. }(Displayable_1["default"]));
  346. var VALID_TEXT_ALIGN = { left: true, right: 1, center: 1 };
  347. var VALID_TEXT_VERTICAL_ALIGN = { top: 1, bottom: 1, middle: 1 };
  348. function normalizeTextStyle(style) {
  349. normalizeStyle(style);
  350. util_1.each(style.rich, normalizeStyle);
  351. return style;
  352. }
  353. exports.normalizeTextStyle = normalizeTextStyle;
  354. function normalizeStyle(style) {
  355. if (style) {
  356. style.font = RichText.makeFont(style);
  357. var textAlign = style.align;
  358. textAlign === 'middle' && (textAlign = 'center');
  359. style.align = (textAlign == null || VALID_TEXT_ALIGN[textAlign]) ? textAlign : 'left';
  360. var textVerticalAlign = style.verticalAlign;
  361. textVerticalAlign === 'center' && (textVerticalAlign = 'middle');
  362. style.verticalAlign = (textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign]) ? textVerticalAlign : 'top';
  363. var textPadding = style.padding;
  364. if (textPadding) {
  365. style.padding = util_1.normalizeCssArray(style.padding);
  366. }
  367. }
  368. }
  369. function getStroke(stroke, lineWidth) {
  370. return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')
  371. ? null
  372. : (stroke.image || stroke.colorStops)
  373. ? '#000'
  374. : stroke;
  375. }
  376. function getFill(fill) {
  377. return (fill == null || fill === 'none')
  378. ? null
  379. : (fill.image || fill.colorStops)
  380. ? '#000'
  381. : fill;
  382. }
  383. function getTextXForPadding(x, textAlign, textPadding) {
  384. return textAlign === 'right'
  385. ? (x - textPadding[1])
  386. : textAlign === 'center'
  387. ? (x + textPadding[3] / 2 - textPadding[1] / 2)
  388. : (x + textPadding[3]);
  389. }
  390. function needDrawBackground(style) {
  391. return !!(style.backgroundColor
  392. || (style.borderWidth && style.borderColor));
  393. }
  394. exports["default"] = RichText;
  395. //# sourceMappingURL=RichText.js.map