656d4b58f380673af805edabe1d6279ac2656e394a77c1a99b8553c9636fedb9340e9787fa418cd809f761dc8a2dbf1c079e3cee39df8c184014992b817292 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // TODO
  2. // 1. shadow
  3. // 2. Image: sx, sy, sw, sh
  4. import {createElement, XLINKNS } from '../svg/core';
  5. import { getMatrixStr, TEXT_ALIGN_TO_ANCHOR, adjustTextY } from '../svg/helper';
  6. import * as matrix from '../core/matrix';
  7. import Path, { PathStyleProps } from '../graphic/Path';
  8. import ZRImage, { ImageStyleProps } from '../graphic/Image';
  9. import { getLineHeight } from '../contain/text';
  10. import TSpan, { TSpanStyleProps } from '../graphic/TSpan';
  11. import SVGPathRebuilder from '../svg/SVGPathRebuilder';
  12. import mapStyleToAttrs from '../svg/mapStyleToAttrs';
  13. import { DEFAULT_FONT } from '../core/platform';
  14. export interface SVGProxy<T> {
  15. brush(el: T): void
  16. }
  17. type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps;
  18. function setTransform(svgEl: SVGElement, m: matrix.MatrixArray) {
  19. if (m) {
  20. attr(svgEl, 'transform', getMatrixStr(m));
  21. }
  22. }
  23. function attr(el: SVGElement, key: string, val: string | number) {
  24. if (!val || (val as any).type !== 'linear' && (val as any).type !== 'radial') {
  25. // Don't set attribute for gradient, since it need new dom nodes
  26. el.setAttribute(key, val as any);
  27. }
  28. }
  29. function attrXLink(el: SVGElement, key: string, val: string) {
  30. el.setAttributeNS(XLINKNS, key, val);
  31. }
  32. function attrXML(el: SVGElement, key: string, val: string) {
  33. el.setAttributeNS('http://www.w3.org/XML/1998/namespace', key, val);
  34. }
  35. function bindStyle(svgEl: SVGElement, style: PathStyleProps, el?: Path): void
  36. function bindStyle(svgEl: SVGElement, style: TSpanStyleProps, el?: TSpan): void
  37. function bindStyle(svgEl: SVGElement, style: ImageStyleProps, el?: ZRImage): void
  38. function bindStyle(svgEl: SVGElement, style: AllStyleOption, el?: Path | TSpan | ZRImage) {
  39. mapStyleToAttrs((key, val) => attr(svgEl, key, val), style, el, true);
  40. }
  41. interface PathWithSVGBuildPath extends Path {
  42. __svgPathVersion: number
  43. __svgPathBuilder: SVGPathRebuilder
  44. }
  45. const svgPath: SVGProxy<Path> = {
  46. brush(el: Path) {
  47. const style = el.style;
  48. let svgEl = el.__svgEl;
  49. if (!svgEl) {
  50. svgEl = createElement('path');
  51. el.__svgEl = svgEl;
  52. }
  53. if (!el.path) {
  54. el.createPathProxy();
  55. }
  56. const path = el.path;
  57. if (el.shapeChanged()) {
  58. path.beginPath();
  59. el.buildPath(path, el.shape);
  60. el.pathUpdated();
  61. }
  62. const pathVersion = path.getVersion();
  63. const elExt = el as PathWithSVGBuildPath;
  64. let svgPathBuilder = elExt.__svgPathBuilder;
  65. if (elExt.__svgPathVersion !== pathVersion || !svgPathBuilder || el.style.strokePercent < 1) {
  66. if (!svgPathBuilder) {
  67. svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder();
  68. }
  69. svgPathBuilder.reset();
  70. path.rebuildPath(svgPathBuilder, el.style.strokePercent);
  71. svgPathBuilder.generateStr();
  72. elExt.__svgPathVersion = pathVersion;
  73. }
  74. attr(svgEl, 'd', svgPathBuilder.getStr());
  75. bindStyle(svgEl, style, el);
  76. setTransform(svgEl, el.transform);
  77. }
  78. };
  79. export {svgPath as path};
  80. /***************************************************
  81. * IMAGE
  82. **************************************************/
  83. const svgImage: SVGProxy<ZRImage> = {
  84. brush(el: ZRImage) {
  85. const style = el.style;
  86. let image = style.image;
  87. if (image instanceof HTMLImageElement) {
  88. image = image.src;
  89. }
  90. // heatmap layer in geo may be a canvas
  91. else if (image instanceof HTMLCanvasElement) {
  92. image = image.toDataURL();
  93. }
  94. if (!image) {
  95. return;
  96. }
  97. const x = style.x || 0;
  98. const y = style.y || 0;
  99. const dw = style.width;
  100. const dh = style.height;
  101. let svgEl = el.__svgEl;
  102. if (!svgEl) {
  103. svgEl = createElement('image');
  104. el.__svgEl = svgEl;
  105. }
  106. if (image !== el.__imageSrc) {
  107. attrXLink(svgEl, 'href', image as string);
  108. // Caching image src
  109. el.__imageSrc = image as string;
  110. }
  111. attr(svgEl, 'width', dw + '');
  112. attr(svgEl, 'height', dh + '');
  113. attr(svgEl, 'x', x + '');
  114. attr(svgEl, 'y', y + '');
  115. bindStyle(svgEl, style, el);
  116. setTransform(svgEl, el.transform);
  117. }
  118. };
  119. export {svgImage as image};
  120. /***************************************************
  121. * TEXT
  122. **************************************************/
  123. const svgText: SVGProxy<TSpan> = {
  124. brush(el: TSpan) {
  125. const style = el.style;
  126. let text = style.text;
  127. // Convert to string
  128. text != null && (text += '');
  129. if (!text || isNaN(style.x) || isNaN(style.y)) {
  130. return;
  131. }
  132. let textSvgEl = el.__svgEl as SVGTextElement;
  133. if (!textSvgEl) {
  134. textSvgEl = createElement('text') as SVGTextElement;
  135. attrXML(textSvgEl, 'xml:space', 'preserve');
  136. el.__svgEl = textSvgEl;
  137. }
  138. const font = style.font || DEFAULT_FONT;
  139. // style.font has been normalized by `normalizeTextStyle`.
  140. const textSvgElStyle = textSvgEl.style;
  141. textSvgElStyle.font = font;
  142. textSvgEl.textContent = text;
  143. bindStyle(textSvgEl, style, el);
  144. setTransform(textSvgEl, el.transform);
  145. // Consider different font display differently in vertial align, we always
  146. // set vertialAlign as 'middle', and use 'y' to locate text vertically.
  147. const x = style.x || 0;
  148. const y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline);
  149. const textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign as keyof typeof TEXT_ALIGN_TO_ANCHOR]
  150. || style.textAlign;
  151. attr(textSvgEl, 'dominant-baseline', 'central');
  152. attr(textSvgEl, 'text-anchor', textAlign);
  153. attr(textSvgEl, 'x', x + '');
  154. attr(textSvgEl, 'y', y + '');
  155. }
  156. };
  157. export {svgText as text};