graphic.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. import env from '../core/env.js';
  2. import { applyTransform } from '../core/vector.js';
  3. import BoundingRect from '../core/BoundingRect.js';
  4. import * as colorTool from '../tool/color.js';
  5. import * as textContain from '../graphic/text/parse.js';
  6. import Displayable from '../graphic/Displayable.js';
  7. import ZRImage from '../graphic/Image.js';
  8. import Text from '../graphic/TSpan.js';
  9. import Path from '../graphic/Path.js';
  10. import PathProxy from '../core/PathProxy.js';
  11. import Gradient from '../graphic/Gradient.js';
  12. import * as vmlCore from './core.js';
  13. var CMD = PathProxy.CMD;
  14. var round = Math.round;
  15. var sqrt = Math.sqrt;
  16. var abs = Math.abs;
  17. var cos = Math.cos;
  18. var sin = Math.sin;
  19. var mathMax = Math.max;
  20. if (!env.canvasSupported) {
  21. var comma = ',';
  22. var imageTransformPrefix = 'progid:DXImageTransform.Microsoft';
  23. var Z = 21600;
  24. var Z2 = Z / 2;
  25. var ZLEVEL_BASE = 100000;
  26. var Z_BASE = 1000;
  27. var initRootElStyle = function (el) {
  28. el.style.cssText = 'position:absolute;left:0;top:0;width:1px;height:1px;';
  29. el.coordsize = Z + ',' + Z;
  30. el.coordorigin = '0,0';
  31. };
  32. var encodeHtmlAttribute = function (s) {
  33. return String(s).replace(/&/g, '&').replace(/"/g, '"');
  34. };
  35. var rgb2Str = function (r, g, b) {
  36. return 'rgb(' + [r, g, b].join(',') + ')';
  37. };
  38. var append = function (parent, child) {
  39. if (child && parent && child.parentNode !== parent) {
  40. parent.appendChild(child);
  41. }
  42. };
  43. var remove = function (parent, child) {
  44. if (child && parent && child.parentNode === parent) {
  45. parent.removeChild(child);
  46. }
  47. };
  48. var getZIndex = function (zlevel, z, z2) {
  49. return (parseFloat(zlevel) || 0) * ZLEVEL_BASE + (parseFloat(z) || 0) * Z_BASE + z2;
  50. };
  51. var parsePercent = textHelper.parsePercent;
  52. var setColorAndOpacity = function (el, color, opacity) {
  53. var colorArr = colorTool.parse(color);
  54. opacity = +opacity;
  55. if (isNaN(opacity)) {
  56. opacity = 1;
  57. }
  58. if (colorArr) {
  59. el.color = rgb2Str(colorArr[0], colorArr[1], colorArr[2]);
  60. el.opacity = opacity * colorArr[3];
  61. }
  62. };
  63. var getColorAndAlpha = function (color) {
  64. var colorArr = colorTool.parse(color);
  65. return [
  66. rgb2Str(colorArr[0], colorArr[1], colorArr[2]),
  67. colorArr[3]
  68. ];
  69. };
  70. var updateFillNode = function (el, style, zrEl) {
  71. var fill = style.fill;
  72. if (fill != null) {
  73. if (fill instanceof Gradient) {
  74. var gradientType;
  75. var angle = 0;
  76. var focus = [0, 0];
  77. var shift = 0;
  78. var expansion = 1;
  79. var rect = zrEl.getBoundingRect();
  80. var rectWidth = rect.width;
  81. var rectHeight = rect.height;
  82. if (fill.type === 'linear') {
  83. gradientType = 'gradient';
  84. var transform = zrEl.transform;
  85. var p0 = [fill.x * rectWidth, fill.y * rectHeight];
  86. var p1 = [fill.x2 * rectWidth, fill.y2 * rectHeight];
  87. if (transform) {
  88. applyTransform(p0, p0, transform);
  89. applyTransform(p1, p1, transform);
  90. }
  91. var dx = p1[0] - p0[0];
  92. var dy = p1[1] - p0[1];
  93. angle = Math.atan2(dx, dy) * 180 / Math.PI;
  94. if (angle < 0) {
  95. angle += 360;
  96. }
  97. if (angle < 1e-6) {
  98. angle = 0;
  99. }
  100. }
  101. else {
  102. gradientType = 'gradientradial';
  103. var p0 = [fill.x * rectWidth, fill.y * rectHeight];
  104. var transform = zrEl.transform;
  105. var scale = zrEl.scale;
  106. var width = rectWidth;
  107. var height = rectHeight;
  108. focus = [
  109. (p0[0] - rect.x) / width,
  110. (p0[1] - rect.y) / height
  111. ];
  112. if (transform) {
  113. applyTransform(p0, p0, transform);
  114. }
  115. width /= scale[0] * Z;
  116. height /= scale[1] * Z;
  117. var dimension = mathMax(width, height);
  118. shift = 2 * 0 / dimension;
  119. expansion = 2 * fill.r / dimension - shift;
  120. }
  121. var stops = fill.colorStops.slice();
  122. stops.sort(function (cs1, cs2) {
  123. return cs1.offset - cs2.offset;
  124. });
  125. var length = stops.length;
  126. var colorAndAlphaList = [];
  127. var colors = [];
  128. for (var i = 0; i < length; i++) {
  129. var stop = stops[i];
  130. var colorAndAlpha = getColorAndAlpha(stop.color);
  131. colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]);
  132. if (i === 0 || i === length - 1) {
  133. colorAndAlphaList.push(colorAndAlpha);
  134. }
  135. }
  136. if (length >= 2) {
  137. var color1 = colorAndAlphaList[0][0];
  138. var color2 = colorAndAlphaList[1][0];
  139. var opacity1 = colorAndAlphaList[0][1] * style.opacity;
  140. var opacity2 = colorAndAlphaList[1][1] * style.opacity;
  141. el.type = gradientType;
  142. el.method = 'none';
  143. el.focus = '100%';
  144. el.angle = angle;
  145. el.color = color1;
  146. el.color2 = color2;
  147. el.colors = colors.join(',');
  148. el.opacity = opacity2;
  149. el.opacity2 = opacity1;
  150. }
  151. if (gradientType === 'radial') {
  152. el.focusposition = focus.join(',');
  153. }
  154. }
  155. else {
  156. setColorAndOpacity(el, fill, style.opacity);
  157. }
  158. }
  159. };
  160. var updateStrokeNode = function (el, style) {
  161. if (style.lineDash) {
  162. el.dashstyle = style.lineDash.join(' ');
  163. }
  164. if (style.stroke != null && !(style.stroke instanceof Gradient)) {
  165. setColorAndOpacity(el, style.stroke, style.opacity);
  166. }
  167. };
  168. var updateFillAndStroke = function (vmlEl, type, style, zrEl) {
  169. var isFill = type === 'fill';
  170. var el = vmlEl.getElementsByTagName(type)[0];
  171. if (style[type] != null && style[type] !== 'none' && (isFill || (!isFill && style.lineWidth))) {
  172. vmlEl[isFill ? 'filled' : 'stroked'] = 'true';
  173. if (style[type] instanceof Gradient) {
  174. remove(vmlEl, el);
  175. }
  176. if (!el) {
  177. el = vmlCore.createNode(type);
  178. }
  179. isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style);
  180. append(vmlEl, el);
  181. }
  182. else {
  183. vmlEl[isFill ? 'filled' : 'stroked'] = 'false';
  184. remove(vmlEl, el);
  185. }
  186. };
  187. var points = [[], [], []];
  188. var pathDataToString = function (path, m) {
  189. var M = CMD.M;
  190. var C = CMD.C;
  191. var L = CMD.L;
  192. var A = CMD.A;
  193. var Q = CMD.Q;
  194. var str = [];
  195. var nPoint;
  196. var cmdStr;
  197. var cmd;
  198. var i;
  199. var xi;
  200. var yi;
  201. var data = path.data;
  202. var dataLength = path.len();
  203. for (i = 0; i < dataLength;) {
  204. cmd = data[i++];
  205. cmdStr = '';
  206. nPoint = 0;
  207. switch (cmd) {
  208. case M:
  209. cmdStr = ' m ';
  210. nPoint = 1;
  211. xi = data[i++];
  212. yi = data[i++];
  213. points[0][0] = xi;
  214. points[0][1] = yi;
  215. break;
  216. case L:
  217. cmdStr = ' l ';
  218. nPoint = 1;
  219. xi = data[i++];
  220. yi = data[i++];
  221. points[0][0] = xi;
  222. points[0][1] = yi;
  223. break;
  224. case Q:
  225. case C:
  226. cmdStr = ' c ';
  227. nPoint = 3;
  228. var x1 = data[i++];
  229. var y1 = data[i++];
  230. var x2 = data[i++];
  231. var y2 = data[i++];
  232. var x3;
  233. var y3;
  234. if (cmd === Q) {
  235. x3 = x2;
  236. y3 = y2;
  237. x2 = (x2 + 2 * x1) / 3;
  238. y2 = (y2 + 2 * y1) / 3;
  239. x1 = (xi + 2 * x1) / 3;
  240. y1 = (yi + 2 * y1) / 3;
  241. }
  242. else {
  243. x3 = data[i++];
  244. y3 = data[i++];
  245. }
  246. points[0][0] = x1;
  247. points[0][1] = y1;
  248. points[1][0] = x2;
  249. points[1][1] = y2;
  250. points[2][0] = x3;
  251. points[2][1] = y3;
  252. xi = x3;
  253. yi = y3;
  254. break;
  255. case A:
  256. var x = 0;
  257. var y = 0;
  258. var sx = 1;
  259. var sy = 1;
  260. var angle = 0;
  261. if (m) {
  262. x = m[4];
  263. y = m[5];
  264. sx = sqrt(m[0] * m[0] + m[1] * m[1]);
  265. sy = sqrt(m[2] * m[2] + m[3] * m[3]);
  266. angle = Math.atan2(-m[1] / sy, m[0] / sx);
  267. }
  268. var cx = data[i++];
  269. var cy = data[i++];
  270. var rx = data[i++];
  271. var ry = data[i++];
  272. var startAngle = data[i++] + angle;
  273. var endAngle = data[i++] + startAngle + angle;
  274. i++;
  275. var clockwise = data[i++];
  276. var x0 = cx + cos(startAngle) * rx;
  277. var y0 = cy + sin(startAngle) * ry;
  278. var x1 = cx + cos(endAngle) * rx;
  279. var y1 = cy + sin(endAngle) * ry;
  280. var type = clockwise ? ' wa ' : ' at ';
  281. if (Math.abs(x0 - x1) < 1e-4) {
  282. if (Math.abs(endAngle - startAngle) > 1e-2) {
  283. if (clockwise) {
  284. x0 += 270 / Z;
  285. }
  286. }
  287. else {
  288. if (Math.abs(y0 - cy) < 1e-4) {
  289. if ((clockwise && x0 < cx) || (!clockwise && x0 > cx)) {
  290. y1 -= 270 / Z;
  291. }
  292. else {
  293. y1 += 270 / Z;
  294. }
  295. }
  296. else if ((clockwise && y0 < cy) || (!clockwise && y0 > cy)) {
  297. x1 += 270 / Z;
  298. }
  299. else {
  300. x1 -= 270 / Z;
  301. }
  302. }
  303. }
  304. str.push(type, round(((cx - rx) * sx + x) * Z - Z2), comma, round(((cy - ry) * sy + y) * Z - Z2), comma, round(((cx + rx) * sx + x) * Z - Z2), comma, round(((cy + ry) * sy + y) * Z - Z2), comma, round((x0 * sx + x) * Z - Z2), comma, round((y0 * sy + y) * Z - Z2), comma, round((x1 * sx + x) * Z - Z2), comma, round((y1 * sy + y) * Z - Z2));
  305. xi = x1;
  306. yi = y1;
  307. break;
  308. case CMD.R:
  309. var p0 = points[0];
  310. var p1 = points[1];
  311. p0[0] = data[i++];
  312. p0[1] = data[i++];
  313. p1[0] = p0[0] + data[i++];
  314. p1[1] = p0[1] + data[i++];
  315. if (m) {
  316. applyTransform(p0, p0, m);
  317. applyTransform(p1, p1, m);
  318. }
  319. p0[0] = round(p0[0] * Z - Z2);
  320. p1[0] = round(p1[0] * Z - Z2);
  321. p0[1] = round(p0[1] * Z - Z2);
  322. p1[1] = round(p1[1] * Z - Z2);
  323. str.push(' m ', p0[0], comma, p0[1], ' l ', p1[0], comma, p0[1], ' l ', p1[0], comma, p1[1], ' l ', p0[0], comma, p1[1]);
  324. break;
  325. case CMD.Z:
  326. str.push(' x ');
  327. }
  328. if (nPoint > 0) {
  329. str.push(cmdStr);
  330. for (var k = 0; k < nPoint; k++) {
  331. var p = points[k];
  332. m && applyTransform(p, p, m);
  333. str.push(round(p[0] * Z - Z2), comma, round(p[1] * Z - Z2), k < nPoint - 1 ? comma : '');
  334. }
  335. }
  336. }
  337. return str.join('');
  338. };
  339. Path.prototype.brushVML = function (vmlRoot) {
  340. var style = this.style;
  341. var vmlEl = this._vmlEl;
  342. if (!vmlEl) {
  343. vmlEl = vmlCore.createNode('shape');
  344. initRootElStyle(vmlEl);
  345. this._vmlEl = vmlEl;
  346. }
  347. updateFillAndStroke(vmlEl, 'fill', style, this);
  348. updateFillAndStroke(vmlEl, 'stroke', style, this);
  349. var m = this.transform;
  350. var needTransform = m != null;
  351. var strokeEl = vmlEl.getElementsByTagName('stroke')[0];
  352. if (strokeEl) {
  353. var lineWidth = style.lineWidth;
  354. if (needTransform && !style.strokeNoScale) {
  355. var det = m[0] * m[3] - m[1] * m[2];
  356. lineWidth *= sqrt(abs(det));
  357. }
  358. strokeEl.weight = lineWidth + 'px';
  359. }
  360. var path = this.path || (this.path = new PathProxy());
  361. if (this.__dirtyPath) {
  362. path.beginPath();
  363. path.subPixelOptimize = false;
  364. this.buildPath(path, this.shape);
  365. path.toStatic();
  366. this.__dirtyPath = false;
  367. }
  368. vmlEl.path = pathDataToString(path, this.transform);
  369. vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
  370. append(vmlRoot, vmlEl);
  371. if (style.text != null) {
  372. this.drawRectText(vmlRoot, this.getBoundingRect());
  373. }
  374. else {
  375. this.removeRectText(vmlRoot);
  376. }
  377. };
  378. Path.prototype.onRemove = function (vmlRoot) {
  379. remove(vmlRoot, this._vmlEl);
  380. this.removeRectText(vmlRoot);
  381. };
  382. Path.prototype.onAdd = function (vmlRoot) {
  383. append(vmlRoot, this._vmlEl);
  384. this.appendRectText(vmlRoot);
  385. };
  386. var isImage = function (img) {
  387. return (typeof img === 'object') && img.tagName && img.tagName.toUpperCase() === 'IMG';
  388. };
  389. ZRImage.prototype.brushVML = function (vmlRoot) {
  390. var style = this.style;
  391. var image = style.image;
  392. var ow;
  393. var oh;
  394. if (isImage(image)) {
  395. var src = image.src;
  396. if (src === this._imageSrc) {
  397. ow = this._imageWidth;
  398. oh = this._imageHeight;
  399. }
  400. else {
  401. var imageRuntimeStyle = image.runtimeStyle;
  402. var oldRuntimeWidth = imageRuntimeStyle.width;
  403. var oldRuntimeHeight = imageRuntimeStyle.height;
  404. imageRuntimeStyle.width = 'auto';
  405. imageRuntimeStyle.height = 'auto';
  406. ow = image.width;
  407. oh = image.height;
  408. imageRuntimeStyle.width = oldRuntimeWidth;
  409. imageRuntimeStyle.height = oldRuntimeHeight;
  410. this._imageSrc = src;
  411. this._imageWidth = ow;
  412. this._imageHeight = oh;
  413. }
  414. image = src;
  415. }
  416. else {
  417. if (image === this._imageSrc) {
  418. ow = this._imageWidth;
  419. oh = this._imageHeight;
  420. }
  421. }
  422. if (!image) {
  423. return;
  424. }
  425. var x = style.x || 0;
  426. var y = style.y || 0;
  427. var dw = style.width;
  428. var dh = style.height;
  429. var sw = style.sWidth;
  430. var sh = style.sHeight;
  431. var sx = style.sx || 0;
  432. var sy = style.sy || 0;
  433. var hasCrop = sw && sh;
  434. var vmlEl = this._vmlEl;
  435. if (!vmlEl) {
  436. vmlEl = vmlCore.doc.createElement('div');
  437. initRootElStyle(vmlEl);
  438. this._vmlEl = vmlEl;
  439. }
  440. var vmlElStyle = vmlEl.style;
  441. var hasRotation = false;
  442. var m;
  443. var scaleX = 1;
  444. var scaleY = 1;
  445. if (this.transform) {
  446. m = this.transform;
  447. scaleX = sqrt(m[0] * m[0] + m[1] * m[1]);
  448. scaleY = sqrt(m[2] * m[2] + m[3] * m[3]);
  449. hasRotation = m[1] || m[2];
  450. }
  451. if (hasRotation) {
  452. var p0 = [x, y];
  453. var p1 = [x + dw, y];
  454. var p2 = [x, y + dh];
  455. var p3 = [x + dw, y + dh];
  456. applyTransform(p0, p0, m);
  457. applyTransform(p1, p1, m);
  458. applyTransform(p2, p2, m);
  459. applyTransform(p3, p3, m);
  460. var maxX = mathMax(p0[0], p1[0], p2[0], p3[0]);
  461. var maxY = mathMax(p0[1], p1[1], p2[1], p3[1]);
  462. var transformFilter = [];
  463. transformFilter.push('M11=', m[0] / scaleX, comma, 'M12=', m[2] / scaleY, comma, 'M21=', m[1] / scaleX, comma, 'M22=', m[3] / scaleY, comma, 'Dx=', round(x * scaleX + m[4]), comma, 'Dy=', round(y * scaleY + m[5]));
  464. vmlElStyle.padding = '0 ' + round(maxX) + 'px ' + round(maxY) + 'px 0';
  465. vmlElStyle.filter = imageTransformPrefix + '.Matrix('
  466. + transformFilter.join('') + ', SizingMethod=clip)';
  467. }
  468. else {
  469. if (m) {
  470. x = x * scaleX + m[4];
  471. y = y * scaleY + m[5];
  472. }
  473. vmlElStyle.filter = '';
  474. vmlElStyle.left = round(x) + 'px';
  475. vmlElStyle.top = round(y) + 'px';
  476. }
  477. var imageEl = this._imageEl;
  478. var cropEl = this._cropEl;
  479. if (!imageEl) {
  480. imageEl = vmlCore.doc.createElement('div');
  481. this._imageEl = imageEl;
  482. }
  483. var imageELStyle = imageEl.style;
  484. if (hasCrop) {
  485. if (!(ow && oh)) {
  486. var tmpImage = new Image();
  487. var self = this;
  488. tmpImage.onload = function () {
  489. tmpImage.onload = null;
  490. ow = tmpImage.width;
  491. oh = tmpImage.height;
  492. imageELStyle.width = round(scaleX * ow * dw / sw) + 'px';
  493. imageELStyle.height = round(scaleY * oh * dh / sh) + 'px';
  494. self._imageWidth = ow;
  495. self._imageHeight = oh;
  496. self._imageSrc = image;
  497. };
  498. tmpImage.src = image;
  499. }
  500. else {
  501. imageELStyle.width = round(scaleX * ow * dw / sw) + 'px';
  502. imageELStyle.height = round(scaleY * oh * dh / sh) + 'px';
  503. }
  504. if (!cropEl) {
  505. cropEl = vmlCore.doc.createElement('div');
  506. cropEl.style.overflow = 'hidden';
  507. this._cropEl = cropEl;
  508. }
  509. var cropElStyle = cropEl.style;
  510. cropElStyle.width = round((dw + sx * dw / sw) * scaleX);
  511. cropElStyle.height = round((dh + sy * dh / sh) * scaleY);
  512. cropElStyle.filter = imageTransformPrefix + '.Matrix(Dx='
  513. + (-sx * dw / sw * scaleX) + ',Dy=' + (-sy * dh / sh * scaleY) + ')';
  514. if (!cropEl.parentNode) {
  515. vmlEl.appendChild(cropEl);
  516. }
  517. if (imageEl.parentNode !== cropEl) {
  518. cropEl.appendChild(imageEl);
  519. }
  520. }
  521. else {
  522. imageELStyle.width = round(scaleX * dw) + 'px';
  523. imageELStyle.height = round(scaleY * dh) + 'px';
  524. vmlEl.appendChild(imageEl);
  525. if (cropEl && cropEl.parentNode) {
  526. vmlEl.removeChild(cropEl);
  527. this._cropEl = null;
  528. }
  529. }
  530. var filterStr = '';
  531. var alpha = style.opacity;
  532. if (alpha < 1) {
  533. filterStr += '.Alpha(opacity=' + round(alpha * 100) + ') ';
  534. }
  535. filterStr += imageTransformPrefix + '.AlphaImageLoader(src=' + image + ', SizingMethod=scale)';
  536. imageELStyle.filter = filterStr;
  537. vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
  538. append(vmlRoot, vmlEl);
  539. if (style.text != null) {
  540. this.drawRectText(vmlRoot, this.getBoundingRect());
  541. }
  542. };
  543. ZRImage.prototype.onRemove = function (vmlRoot) {
  544. remove(vmlRoot, this._vmlEl);
  545. this._vmlEl = null;
  546. this._cropEl = null;
  547. this._imageEl = null;
  548. this.removeRectText(vmlRoot);
  549. };
  550. ZRImage.prototype.onAdd = function (vmlRoot) {
  551. append(vmlRoot, this._vmlEl);
  552. this.appendRectText(vmlRoot);
  553. };
  554. var DEFAULT_STYLE_NORMAL = 'normal';
  555. var fontStyleCache = {};
  556. var fontStyleCacheCount = 0;
  557. var MAX_FONT_CACHE_SIZE = 100;
  558. var fontEl = document.createElement('div');
  559. var getFontStyle = function (fontString) {
  560. var fontStyle = fontStyleCache[fontString];
  561. if (!fontStyle) {
  562. if (fontStyleCacheCount > MAX_FONT_CACHE_SIZE) {
  563. fontStyleCacheCount = 0;
  564. fontStyleCache = {};
  565. }
  566. var style = fontEl.style;
  567. var fontFamily;
  568. try {
  569. style.font = fontString;
  570. fontFamily = style.fontFamily.split(',')[0];
  571. }
  572. catch (e) {
  573. }
  574. fontStyle = {
  575. style: style.fontStyle || DEFAULT_STYLE_NORMAL,
  576. variant: style.fontVariant || DEFAULT_STYLE_NORMAL,
  577. weight: style.fontWeight || DEFAULT_STYLE_NORMAL,
  578. size: parseFloat(style.fontSize || 12) | 0,
  579. family: fontFamily || 'Microsoft YaHei'
  580. };
  581. fontStyleCache[fontString] = fontStyle;
  582. fontStyleCacheCount++;
  583. }
  584. return fontStyle;
  585. };
  586. var textMeasureEl;
  587. textContain.$override('measureText', function (text, textFont) {
  588. var doc = vmlCore.doc;
  589. if (!textMeasureEl) {
  590. textMeasureEl = doc.createElement('div');
  591. textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;'
  592. + 'padding:0;margin:0;border:none;white-space:pre;';
  593. vmlCore.doc.body.appendChild(textMeasureEl);
  594. }
  595. try {
  596. textMeasureEl.style.font = textFont;
  597. }
  598. catch (ex) {
  599. }
  600. textMeasureEl.innerHTML = '';
  601. textMeasureEl.appendChild(doc.createTextNode(text));
  602. return {
  603. width: textMeasureEl.offsetWidth
  604. };
  605. });
  606. var tmpRect = new BoundingRect(0, 0, 0, 0);
  607. var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) {
  608. var style = this.style;
  609. this.__dirty && textHelper.normalizeTextStyle(style, true);
  610. var text = style.text;
  611. text != null && (text += '');
  612. if (!text) {
  613. return;
  614. }
  615. if (style.rich) {
  616. var contentBlock = textContain.parseRichText(text, style);
  617. text = [];
  618. for (var i = 0; i < contentBlock.lines.length; i++) {
  619. var tokens = contentBlock.lines[i].tokens;
  620. var textLine = [];
  621. for (var j = 0; j < tokens.length; j++) {
  622. textLine.push(tokens[j].text);
  623. }
  624. text.push(textLine.join(''));
  625. }
  626. text = text.join('\n');
  627. }
  628. var x;
  629. var y;
  630. var align = style.textAlign;
  631. var verticalAlign = style.textVerticalAlign;
  632. var fontStyle = getFontStyle(style.font);
  633. var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' '
  634. + fontStyle.size + 'px "' + fontStyle.family + '"';
  635. textRect = textRect || textContain.getBoundingRect(text, font, align, verticalAlign, style.textPadding, style.textLineHeight);
  636. var m = this.transform;
  637. if (m && !fromTextEl) {
  638. tmpRect.copy(rect);
  639. tmpRect.applyTransform(m);
  640. rect = tmpRect;
  641. }
  642. if (!fromTextEl) {
  643. var textPosition = style.textPosition;
  644. if (textPosition instanceof Array) {
  645. x = rect.x + parsePercent(textPosition[0], rect.width);
  646. y = rect.y + parsePercent(textPosition[1], rect.height);
  647. align = align || 'left';
  648. }
  649. else {
  650. var res = this.calculateTextPosition
  651. ? this.calculateTextPosition({}, style, rect)
  652. : textContain.calculateTextPosition({}, style, rect);
  653. x = res.x;
  654. y = res.y;
  655. align = align || res.align;
  656. verticalAlign = verticalAlign || res.verticalAlign;
  657. }
  658. }
  659. else {
  660. x = rect.x;
  661. y = rect.y;
  662. }
  663. x = textContain.adjustTextX(x, textRect.width, align);
  664. y = textContain.adjustTextY(y, textRect.height, verticalAlign);
  665. y += textRect.height / 2;
  666. var createNode = vmlCore.createNode;
  667. var textVmlEl = this._textVmlEl;
  668. var pathEl;
  669. var textPathEl;
  670. var skewEl;
  671. if (!textVmlEl) {
  672. textVmlEl = createNode('line');
  673. pathEl = createNode('path');
  674. textPathEl = createNode('textpath');
  675. skewEl = createNode('skew');
  676. textPathEl.style['v-text-align'] = 'left';
  677. initRootElStyle(textVmlEl);
  678. pathEl.textpathok = true;
  679. textPathEl.on = true;
  680. textVmlEl.from = '0 0';
  681. textVmlEl.to = '1000 0.05';
  682. append(textVmlEl, skewEl);
  683. append(textVmlEl, pathEl);
  684. append(textVmlEl, textPathEl);
  685. this._textVmlEl = textVmlEl;
  686. }
  687. else {
  688. skewEl = textVmlEl.firstChild;
  689. pathEl = skewEl.nextSibling;
  690. textPathEl = pathEl.nextSibling;
  691. }
  692. var coords = [x, y];
  693. var textVmlElStyle = textVmlEl.style;
  694. if (m && fromTextEl) {
  695. applyTransform(coords, coords, m);
  696. skewEl.on = true;
  697. skewEl.matrix = m[0].toFixed(3) + comma + m[2].toFixed(3) + comma
  698. + m[1].toFixed(3) + comma + m[3].toFixed(3) + ',0,0';
  699. skewEl.offset = (round(coords[0]) || 0) + ',' + (round(coords[1]) || 0);
  700. skewEl.origin = '0 0';
  701. textVmlElStyle.left = '0px';
  702. textVmlElStyle.top = '0px';
  703. }
  704. else {
  705. skewEl.on = false;
  706. textVmlElStyle.left = round(x) + 'px';
  707. textVmlElStyle.top = round(y) + 'px';
  708. }
  709. textPathEl.string = encodeHtmlAttribute(text);
  710. try {
  711. textPathEl.style.font = font;
  712. }
  713. catch (e) { }
  714. updateFillAndStroke(textVmlEl, 'fill', {
  715. fill: style.textFill,
  716. opacity: style.opacity
  717. }, this);
  718. updateFillAndStroke(textVmlEl, 'stroke', {
  719. stroke: style.textStroke,
  720. opacity: style.opacity,
  721. lineDash: style.lineDash || null
  722. }, this);
  723. textVmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
  724. append(vmlRoot, textVmlEl);
  725. };
  726. var removeRectText = function (vmlRoot) {
  727. remove(vmlRoot, this._textVmlEl);
  728. this._textVmlEl = null;
  729. };
  730. var appendRectText = function (vmlRoot) {
  731. append(vmlRoot, this._textVmlEl);
  732. };
  733. var list = [RectText, Displayable, ZRImage, Path, Text];
  734. for (var i = 0; i < list.length; i++) {
  735. var proto = list[i].prototype;
  736. proto.drawRectText = drawRectText;
  737. proto.removeRectText = removeRectText;
  738. proto.appendRectText = appendRectText;
  739. }
  740. Text.prototype.brushVML = function (vmlRoot) {
  741. var style = this.style;
  742. if (style.text != null) {
  743. this.drawRectText(vmlRoot, {
  744. x: style.x || 0, y: style.y || 0,
  745. width: 0, height: 0
  746. }, this.getBoundingRect(), true);
  747. }
  748. else {
  749. this.removeRectText(vmlRoot);
  750. }
  751. };
  752. Text.prototype.onRemove = function (vmlRoot) {
  753. this.removeRectText(vmlRoot);
  754. };
  755. Text.prototype.onAdd = function (vmlRoot) {
  756. this.appendRectText(vmlRoot);
  757. };
  758. }