7618364e5ca37a8b697612b8a660d30f926d51b9552f5b77621621bd44804b5720f3ca949411771004173e06c37f81f2b34a16ab1bf9911c128f6f7d5a916f 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import { keys, map } from '../core/util';
  2. import { encodeHTML } from '../core/dom';
  3. export type CSSSelectorVNode = Record<string, string>
  4. export type CSSAnimationVNode = Record<string, Record<string, string>>
  5. export const SVGNS = 'http://www.w3.org/2000/svg';
  6. export const XLINKNS = 'http://www.w3.org/1999/xlink';
  7. export const XMLNS = 'http://www.w3.org/2000/xmlns/';
  8. export const XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
  9. export function createElement(name: string) {
  10. return document.createElementNS(SVGNS, name);
  11. }
  12. export type SVGVNodeAttrs = Record<string, string | number | undefined | boolean>
  13. export interface SVGVNode {
  14. tag: string,
  15. attrs: SVGVNodeAttrs,
  16. children?: SVGVNode[],
  17. text?: string
  18. // For patching
  19. elm?: Node
  20. key: string
  21. };
  22. export function createVNode(
  23. tag: string,
  24. key: string,
  25. attrs?: SVGVNodeAttrs,
  26. children?: SVGVNode[],
  27. text?: string
  28. ): SVGVNode {
  29. return {
  30. tag,
  31. attrs: attrs || {},
  32. children,
  33. text,
  34. key
  35. };
  36. }
  37. function createElementOpen(name: string, attrs?: SVGVNodeAttrs) {
  38. const attrsStr: string[] = [];
  39. if (attrs) {
  40. // eslint-disable-next-line
  41. for (let key in attrs) {
  42. const val = attrs[key];
  43. let part = key;
  44. // Same with the logic in patch.
  45. if (val === false) {
  46. continue;
  47. }
  48. else if (val !== true && val != null) {
  49. part += `="${val}"`;
  50. }
  51. attrsStr.push(part);
  52. }
  53. }
  54. return `<${name} ${attrsStr.join(' ')}>`;
  55. }
  56. function createElementClose(name: string) {
  57. return `</${name}>`;
  58. }
  59. export function vNodeToString(el: SVGVNode, opts?: {
  60. newline?: boolean
  61. }) {
  62. opts = opts || {};
  63. const S = opts.newline ? '\n' : '';
  64. function convertElToString(el: SVGVNode): string {
  65. const {children, tag, attrs} = el;
  66. return createElementOpen(tag, attrs)
  67. + encodeHTML(el.text)
  68. + (children ? `${S}${map(children, child => convertElToString(child)).join(S)}${S}` : '')
  69. + createElementClose(tag);
  70. }
  71. return convertElToString(el);
  72. }
  73. export function getCssString(
  74. selectorNodes: Record<string, CSSSelectorVNode>,
  75. animationNodes: Record<string, CSSAnimationVNode>,
  76. opts?: {
  77. newline?: boolean
  78. }
  79. ) {
  80. opts = opts || {};
  81. const S = opts.newline ? '\n' : '';
  82. const bracketBegin = ` {${S}`;
  83. const bracketEnd = `${S}}`;
  84. const selectors = map(keys(selectorNodes), className => {
  85. return className + bracketBegin + map(keys(selectorNodes[className]), attrName => {
  86. return `${attrName}:${selectorNodes[className][attrName]};`;
  87. }).join(S) + bracketEnd;
  88. }).join(S);
  89. const animations = map(keys(animationNodes), (animationName) => {
  90. return `@keyframes ${animationName}${bracketBegin}` + map(keys(animationNodes[animationName]), percent => {
  91. return percent + bracketBegin + map(keys(animationNodes[animationName][percent]), attrName => {
  92. let val = animationNodes[animationName][percent][attrName];
  93. // postprocess
  94. if (attrName === 'd') {
  95. val = `path("${val}")`;
  96. }
  97. return `${attrName}:${val};`;
  98. }).join(S) + bracketEnd;
  99. }).join(S) + bracketEnd;
  100. }).join(S);
  101. if (!selectors && !animations) {
  102. return '';
  103. }
  104. return ['<![CDATA[', selectors, animations, ']]>'].join(S);
  105. }
  106. export interface BrushScope {
  107. zrId: string
  108. shadowCache: Record<string, string>
  109. gradientCache: Record<string, string>
  110. patternCache: Record<string, string>
  111. clipPathCache: Record<string, string>
  112. defs: Record<string, SVGVNode>
  113. cssNodes: Record<string, CSSSelectorVNode>
  114. cssAnims: Record<string, Record<string, Record<string, string>>>
  115. cssClassIdx: number
  116. cssAnimIdx: number
  117. shadowIdx: number
  118. gradientIdx: number
  119. patternIdx: number
  120. clipPathIdx: number
  121. // configs
  122. /**
  123. * If create animates nodes.
  124. */
  125. animation?: boolean,
  126. /**
  127. * If will update. Some optimization for string generation can't be applied.
  128. */
  129. willUpdate?: boolean
  130. /**
  131. * If compress the output string.
  132. */
  133. compress?: boolean
  134. }
  135. export function createBrushScope(zrId: string): BrushScope {
  136. return {
  137. zrId,
  138. shadowCache: {},
  139. patternCache: {},
  140. gradientCache: {},
  141. clipPathCache: {},
  142. defs: {},
  143. cssNodes: {},
  144. cssAnims: {},
  145. cssClassIdx: 0,
  146. cssAnimIdx: 0,
  147. shadowIdx: 0,
  148. gradientIdx: 0,
  149. patternIdx: 0,
  150. clipPathIdx: 0
  151. };
  152. }
  153. export function createSVGVNode(
  154. width: number | string,
  155. height: number | string,
  156. children?: SVGVNode[],
  157. useViewBox?: boolean
  158. ) {
  159. return createVNode(
  160. 'svg',
  161. 'root',
  162. {
  163. 'width': width,
  164. 'height': height,
  165. 'xmlns': SVGNS,
  166. 'xmlns:xlink': XLINKNS,
  167. 'version': '1.1',
  168. 'baseProfile': 'full',
  169. 'viewBox': useViewBox ? `0 0 ${width} ${height}` : false
  170. },
  171. children
  172. );
  173. }