text.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. "use strict";
  2. exports.__esModule = true;
  3. var util_1 = require("../../core/util");
  4. var textContain = require("../../contain/text");
  5. var roundRectHelper = require("./roundRect");
  6. var imageHelper = require("./image");
  7. var fixShadow_1 = require("./fixShadow");
  8. var constant_1 = require("../constant");
  9. var DEFAULT_FONT = textContain.DEFAULT_FONT;
  10. var VALID_TEXT_ALIGN = { left: true, right: 1, center: 1 };
  11. var VALID_TEXT_VERTICAL_ALIGN = { top: 1, bottom: 1, middle: 1 };
  12. var SHADOW_STYLE_COMMON_PROPS = [
  13. ['textShadowBlur', 'shadowBlur', 0],
  14. ['textShadowOffsetX', 'shadowOffsetX', 0],
  15. ['textShadowOffsetY', 'shadowOffsetY', 0],
  16. ['textShadowColor', 'shadowColor', 'transparent']
  17. ];
  18. var _tmpTextPositionResult = {};
  19. var _tmpBoxPositionResult = {};
  20. function normalizeTextStyle(style) {
  21. normalizeStyle(style);
  22. util_1.each(style.rich, normalizeStyle);
  23. return style;
  24. }
  25. exports.normalizeTextStyle = normalizeTextStyle;
  26. function normalizeStyle(style) {
  27. if (style) {
  28. style.font = textContain.makeFont(style);
  29. var textAlign = style.textAlign;
  30. textAlign === 'middle' && (textAlign = 'center');
  31. style.textAlign = (textAlign == null || VALID_TEXT_ALIGN[textAlign]) ? textAlign : 'left';
  32. var textVerticalAlign = style.textVerticalAlign || style.textBaseline;
  33. textVerticalAlign === 'center' && (textVerticalAlign = 'middle');
  34. style.textVerticalAlign = (textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign]) ? textVerticalAlign : 'top';
  35. var textPadding = style.textPadding;
  36. if (textPadding) {
  37. style.textPadding = util_1.normalizeCssArray(style.textPadding);
  38. }
  39. }
  40. }
  41. function renderText(hostEl, ctx, text, style, rect, prevEl) {
  42. style.rich
  43. ? renderRichText(hostEl, ctx, text, style, rect, prevEl)
  44. : renderPlainText(hostEl, ctx, text, style, rect, prevEl);
  45. }
  46. exports.renderText = renderText;
  47. function renderPlainText(hostEl, ctx, text, style, rect, prevEl) {
  48. 'use strict';
  49. var needDrawBg = needDrawBackground(style);
  50. var cachedByMe = ctx.__attrCachedBy === constant_1.ContextCachedBy.PLAIN_TEXT;
  51. var prevStyle;
  52. var checkCache = false;
  53. if (prevEl !== constant_1.WILL_BE_RESTORED) {
  54. if (prevEl) {
  55. prevStyle = prevEl.style;
  56. checkCache = !needDrawBg && cachedByMe && !!prevStyle;
  57. }
  58. ctx.__attrCachedBy = needDrawBg ? constant_1.ContextCachedBy.NONE : constant_1.ContextCachedBy.PLAIN_TEXT;
  59. }
  60. else if (cachedByMe) {
  61. ctx.__attrCachedBy = constant_1.ContextCachedBy.NONE;
  62. }
  63. var styleFont = style.font || DEFAULT_FONT;
  64. if (!checkCache || styleFont !== (prevStyle.font || DEFAULT_FONT)) {
  65. ctx.font = styleFont;
  66. }
  67. var computedFont = hostEl.__computedFont;
  68. if (hostEl.__styleFont !== styleFont) {
  69. hostEl.__styleFont = styleFont;
  70. computedFont = hostEl.__computedFont = ctx.font;
  71. }
  72. var textPadding = style.textPadding;
  73. var textLineHeight = style.textLineHeight;
  74. var contentBlock = hostEl.__textCotentBlock;
  75. if (!contentBlock || hostEl.__dirtyText) {
  76. contentBlock = hostEl.__textCotentBlock = textContain.parsePlainText(text, computedFont, textPadding, textLineHeight, style.truncate);
  77. }
  78. var outerHeight = contentBlock.outerHeight;
  79. var textLines = contentBlock.lines;
  80. var lineHeight = contentBlock.lineHeight;
  81. var boxPos = getBoxPosition(_tmpBoxPositionResult, hostEl, style, rect);
  82. var baseX = boxPos.baseX;
  83. var baseY = boxPos.baseY;
  84. var textAlign = boxPos.textAlign || 'left';
  85. var textVerticalAlign = boxPos.textVerticalAlign;
  86. applyTextRotation(ctx, style, rect, baseX, baseY);
  87. var boxY = textContain.adjustTextY(baseY, outerHeight, textVerticalAlign);
  88. var textX = baseX;
  89. var textY = boxY;
  90. if (needDrawBg || textPadding) {
  91. var textWidth = textContain.getWidth(text, computedFont);
  92. var outerWidth_1 = textWidth;
  93. textPadding && (outerWidth_1 += textPadding[1] + textPadding[3]);
  94. var boxX = textContain.adjustTextX(baseX, outerWidth_1, textAlign);
  95. needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth_1, outerHeight);
  96. if (textPadding) {
  97. textX = getTextXForPadding(baseX, textAlign, textPadding);
  98. textY += textPadding[0];
  99. }
  100. }
  101. ctx.textAlign = textAlign;
  102. ctx.textBaseline = 'middle';
  103. ctx.globalAlpha = style.opacity || 1;
  104. for (var i = 0; i < SHADOW_STYLE_COMMON_PROPS.length; i++) {
  105. var propItem = SHADOW_STYLE_COMMON_PROPS[i];
  106. var styleProp = propItem[0];
  107. var ctxProp = propItem[1];
  108. var val = style[styleProp];
  109. if (!checkCache || val !== prevStyle[styleProp]) {
  110. ctx[ctxProp] = fixShadow_1["default"](ctx, ctxProp, (val || propItem[2]));
  111. }
  112. }
  113. textY += lineHeight / 2;
  114. var textStrokeWidth = style.textStrokeWidth;
  115. var textStrokeWidthPrev = checkCache ? prevStyle.textStrokeWidth : null;
  116. var strokeWidthChanged = !checkCache || textStrokeWidth !== textStrokeWidthPrev;
  117. var strokeChanged = !checkCache || strokeWidthChanged || style.textStroke !== prevStyle.textStroke;
  118. var textStroke = getStroke(style.textStroke, textStrokeWidth);
  119. var textFill = getFill(style.textFill);
  120. if (textStroke) {
  121. if (strokeWidthChanged) {
  122. ctx.lineWidth = textStrokeWidth;
  123. }
  124. if (strokeChanged) {
  125. ctx.strokeStyle = textStroke;
  126. }
  127. }
  128. if (textFill) {
  129. if (!checkCache || style.textFill !== prevStyle.textFill) {
  130. ctx.fillStyle = textFill;
  131. }
  132. }
  133. if (textLines.length === 1) {
  134. textStroke && ctx.strokeText(textLines[0], textX, textY);
  135. textFill && ctx.fillText(textLines[0], textX, textY);
  136. }
  137. else {
  138. for (var i = 0; i < textLines.length; i++) {
  139. textStroke && ctx.strokeText(textLines[i], textX, textY);
  140. textFill && ctx.fillText(textLines[i], textX, textY);
  141. textY += lineHeight;
  142. }
  143. }
  144. }
  145. function renderRichText(hostEl, ctx, text, style, rect, prevEl) {
  146. if (prevEl !== constant_1.WILL_BE_RESTORED) {
  147. ctx.__attrCachedBy = constant_1.ContextCachedBy.NONE;
  148. }
  149. var contentBlock = hostEl.__textCotentBlock;
  150. if (!contentBlock || hostEl.__dirtyText) {
  151. contentBlock = hostEl.__textCotentBlock = textContain.parseRichText(text, style);
  152. }
  153. drawRichText(hostEl, ctx, contentBlock, style, rect);
  154. }
  155. function drawRichText(hostEl, ctx, contentBlock, style, rect) {
  156. var contentWidth = contentBlock.width;
  157. var outerWidth = contentBlock.outerWidth;
  158. var outerHeight = contentBlock.outerHeight;
  159. var textPadding = style.textPadding;
  160. var boxPos = getBoxPosition(_tmpBoxPositionResult, hostEl, style, rect);
  161. var baseX = boxPos.baseX;
  162. var baseY = boxPos.baseY;
  163. var textAlign = boxPos.textAlign;
  164. var textVerticalAlign = boxPos.textVerticalAlign;
  165. applyTextRotation(ctx, style, rect, baseX, baseY);
  166. var boxX = textContain.adjustTextX(baseX, outerWidth, textAlign);
  167. var boxY = textContain.adjustTextY(baseY, outerHeight, textVerticalAlign);
  168. var xLeft = boxX;
  169. var lineTop = boxY;
  170. if (textPadding) {
  171. xLeft += textPadding[3];
  172. lineTop += textPadding[0];
  173. }
  174. var xRight = xLeft + contentWidth;
  175. needDrawBackground(style) && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);
  176. for (var i = 0; i < contentBlock.lines.length; i++) {
  177. var line = contentBlock.lines[i];
  178. var tokens = line.tokens;
  179. var tokenCount = tokens.length;
  180. var lineHeight = line.lineHeight;
  181. var usedWidth = line.width;
  182. var leftIndex = 0;
  183. var lineXLeft = xLeft;
  184. var lineXRight = xRight;
  185. var rightIndex = tokenCount - 1;
  186. var token = void 0;
  187. while (leftIndex < tokenCount
  188. && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left')) {
  189. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left');
  190. usedWidth -= token.width;
  191. lineXLeft += token.width;
  192. leftIndex++;
  193. }
  194. while (rightIndex >= 0
  195. && (token = tokens[rightIndex], token.textAlign === 'right')) {
  196. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right');
  197. usedWidth -= token.width;
  198. lineXRight -= token.width;
  199. rightIndex--;
  200. }
  201. lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2;
  202. while (leftIndex <= rightIndex) {
  203. token = tokens[leftIndex];
  204. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center');
  205. lineXLeft += token.width;
  206. leftIndex++;
  207. }
  208. lineTop += lineHeight;
  209. }
  210. }
  211. function applyTextRotation(ctx, style, rect, x, y) {
  212. if (rect && style.textRotation) {
  213. var origin_1 = style.textOrigin;
  214. if (origin_1 === 'center') {
  215. x = rect.width / 2 + rect.x;
  216. y = rect.height / 2 + rect.y;
  217. }
  218. else if (origin_1) {
  219. x = origin_1[0] + rect.x;
  220. y = origin_1[1] + rect.y;
  221. }
  222. ctx.translate(x, y);
  223. ctx.rotate(-style.textRotation);
  224. ctx.translate(-x, -y);
  225. }
  226. }
  227. function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) {
  228. var tokenStyle = style.rich[token.styleName] || {};
  229. tokenStyle.text = token.text;
  230. var textVerticalAlign = token.textVerticalAlign;
  231. var y = lineTop + lineHeight / 2;
  232. if (textVerticalAlign === 'top') {
  233. y = lineTop + token.height / 2;
  234. }
  235. else if (textVerticalAlign === 'bottom') {
  236. y = lineTop + lineHeight - token.height / 2;
  237. }
  238. !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground(hostEl, ctx, tokenStyle, textAlign === 'right'
  239. ? x - token.width
  240. : textAlign === 'center'
  241. ? x - token.width / 2
  242. : x, y - token.height / 2, token.width, token.height);
  243. var textPadding = token.textPadding;
  244. if (textPadding) {
  245. x = getTextXForPadding(x, textAlign, textPadding);
  246. y -= token.height / 2 - textPadding[2] - token.textHeight / 2;
  247. }
  248. setCtx(ctx, 'shadowBlur', util_1.retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0));
  249. setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent');
  250. setCtx(ctx, 'shadowOffsetX', util_1.retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0));
  251. setCtx(ctx, 'shadowOffsetY', util_1.retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0));
  252. setCtx(ctx, 'textAlign', textAlign);
  253. setCtx(ctx, 'textBaseline', 'middle');
  254. setCtx(ctx, 'font', token.font || DEFAULT_FONT);
  255. var textStrokeWidth = util_1.retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth);
  256. var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth);
  257. var textFill = getFill(tokenStyle.textFill || style.textFill);
  258. if (textStroke) {
  259. setCtx(ctx, 'lineWidth', textStrokeWidth);
  260. setCtx(ctx, 'strokeStyle', textStroke);
  261. ctx.strokeText(token.text, x, y);
  262. }
  263. if (textFill) {
  264. setCtx(ctx, 'fillStyle', textFill);
  265. ctx.fillText(token.text, x, y);
  266. }
  267. }
  268. function needDrawBackground(style) {
  269. return !!(style.textBackgroundColor
  270. || (style.textBorderWidth && style.textBorderColor));
  271. }
  272. function drawBackground(hostEl, ctx, style, x, y, width, height) {
  273. var textBackgroundColor = style.textBackgroundColor;
  274. var textBorderWidth = style.textBorderWidth;
  275. var textBorderColor = style.textBorderColor;
  276. var isPlainBg = util_1.isString(textBackgroundColor);
  277. setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0);
  278. setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent');
  279. setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0);
  280. setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0);
  281. if (isPlainBg || (textBorderWidth && textBorderColor)) {
  282. ctx.beginPath();
  283. var textBorderRadius = style.textBorderRadius;
  284. if (!textBorderRadius) {
  285. ctx.rect(x, y, width, height);
  286. }
  287. else {
  288. roundRectHelper.buildPath(ctx, {
  289. x: x, y: y, width: width, height: height, r: textBorderRadius
  290. });
  291. }
  292. ctx.closePath();
  293. }
  294. if (isPlainBg) {
  295. setCtx(ctx, 'fillStyle', textBackgroundColor);
  296. if (style.fillOpacity != null) {
  297. var originalGlobalAlpha = ctx.globalAlpha;
  298. ctx.globalAlpha = style.fillOpacity * style.opacity;
  299. ctx.fill();
  300. ctx.globalAlpha = originalGlobalAlpha;
  301. }
  302. else {
  303. ctx.fill();
  304. }
  305. }
  306. else if (textBackgroundColor && textBackgroundColor.image) {
  307. var image = textBackgroundColor.image;
  308. image = imageHelper.createOrUpdateImage(image, null, hostEl, onBgImageLoaded, textBackgroundColor);
  309. if (image && imageHelper.isImageReady(image)) {
  310. ctx.drawImage(image, x, y, width, height);
  311. }
  312. }
  313. if (textBorderWidth && textBorderColor) {
  314. setCtx(ctx, 'lineWidth', textBorderWidth);
  315. setCtx(ctx, 'strokeStyle', textBorderColor);
  316. if (style.strokeOpacity != null) {
  317. var originalGlobalAlpha = ctx.globalAlpha;
  318. ctx.globalAlpha = style.strokeOpacity * style.opacity;
  319. ctx.stroke();
  320. ctx.globalAlpha = originalGlobalAlpha;
  321. }
  322. else {
  323. ctx.stroke();
  324. }
  325. }
  326. }
  327. function onBgImageLoaded(image, textBackgroundColor) {
  328. textBackgroundColor.image = image;
  329. }
  330. function getBoxPosition(out, hostEl, style, rect) {
  331. var baseX = style.x || 0;
  332. var baseY = style.y || 0;
  333. var textAlign = style.textAlign;
  334. var textVerticalAlign = style.textVerticalAlign;
  335. if (rect) {
  336. var textPosition = style.textPosition;
  337. if (textPosition instanceof Array) {
  338. baseX = rect.x + parsePercent(textPosition[0], rect.width);
  339. baseY = rect.y + parsePercent(textPosition[1], rect.height);
  340. }
  341. else {
  342. var res = (hostEl && hostEl.calculateTextPosition)
  343. ? hostEl.calculateTextPosition(_tmpTextPositionResult, style, rect)
  344. : textContain.calculateTextPosition(_tmpTextPositionResult, style, rect);
  345. baseX = res.x;
  346. baseY = res.y;
  347. textAlign = textAlign || res.textAlign;
  348. textVerticalAlign = textVerticalAlign || res.textVerticalAlign;
  349. }
  350. var textOffset = style.textOffset;
  351. if (textOffset) {
  352. baseX += textOffset[0];
  353. baseY += textOffset[1];
  354. }
  355. }
  356. out = out || {};
  357. out.baseX = baseX;
  358. out.baseY = baseY;
  359. out.textAlign = textAlign;
  360. out.textVerticalAlign = textVerticalAlign;
  361. return out;
  362. }
  363. exports.getBoxPosition = getBoxPosition;
  364. function setCtx(ctx, prop, value) {
  365. ctx[prop] = fixShadow_1["default"](ctx, prop, value);
  366. return ctx[prop];
  367. }
  368. function getStroke(stroke, lineWidth) {
  369. return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')
  370. ? null
  371. : (stroke.image || stroke.colorStops)
  372. ? '#000'
  373. : stroke;
  374. }
  375. exports.getStroke = getStroke;
  376. function getFill(fill) {
  377. return (fill == null || fill === 'none')
  378. ? null
  379. : (fill.image || fill.colorStops)
  380. ? '#000'
  381. : fill;
  382. }
  383. exports.getFill = getFill;
  384. function parsePercent(value, maxValue) {
  385. if (typeof value === 'string') {
  386. if (value.lastIndexOf('%') >= 0) {
  387. return parseFloat(value) / 100 * maxValue;
  388. }
  389. return parseFloat(value);
  390. }
  391. return value;
  392. }
  393. exports.parsePercent = parsePercent;
  394. function getTextXForPadding(x, textAlign, textPadding) {
  395. return textAlign === 'right'
  396. ? (x - textPadding[1])
  397. : textAlign === 'center'
  398. ? (x + textPadding[3] / 2 - textPadding[1] / 2)
  399. : (x + textPadding[3]);
  400. }
  401. function needDrawText(text, style) {
  402. return text != null
  403. && !!(text
  404. || style.textBackgroundColor
  405. || (style.textBorderWidth && style.textBorderColor)
  406. || style.textPadding);
  407. }
  408. exports.needDrawText = needDrawText;
  409. //# sourceMappingURL=text.js.map