core.ts 5.4 KB

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