zrender
Version:
A lightweight graphic library providing 2d draw for Apache ECharts
206 lines (181 loc) • 5.53 kB
text/typescript
import { keys, map } from '../core/util';
import { encodeHTML } from '../core/dom';
export type CSSSelectorVNode = Record<string, string>
export type CSSAnimationVNode = Record<string, Record<string, string>>
export const SVGNS = 'http://www.w3.org/2000/svg';
export const XLINKNS = 'http://www.w3.org/1999/xlink';
export const XMLNS = 'http://www.w3.org/2000/xmlns/';
export const XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
export const META_DATA_PREFIX = 'ecmeta_';
export function createElement(name: string) {
return document.createElementNS(SVGNS, name);
}
export type SVGVNodeAttrs = Record<string, string | number | undefined | boolean>
export interface SVGVNode {
tag: string,
attrs: SVGVNodeAttrs,
children?: SVGVNode[],
text?: string
// For patching
elm?: Node
key: string
};
export function createVNode(
tag: string,
key: string,
attrs?: SVGVNodeAttrs,
children?: SVGVNode[],
text?: string
): SVGVNode {
return {
tag,
attrs: attrs || {},
children,
text,
key
};
}
function createElementOpen(name: string, attrs?: SVGVNodeAttrs) {
const attrsStr: string[] = [];
if (attrs) {
// eslint-disable-next-line
for (let key in attrs) {
const val = attrs[key];
let part = key;
// Same with the logic in patch.
if (val === false) {
continue;
}
else if (val !== true && val != null) {
part += `="${val}"`;
}
attrsStr.push(part);
}
}
return `<${name} ${attrsStr.join(' ')}>`;
}
function createElementClose(name: string) {
return `</${name}>`;
}
export function vNodeToString(el: SVGVNode, opts?: {
newline?: boolean
}) {
opts = opts || {};
const S = opts.newline ? '\n' : '';
function convertElToString(el: SVGVNode): string {
const {children, tag, attrs, text} = el;
return createElementOpen(tag, attrs)
+ (tag !== 'style' ? encodeHTML(text) : text || '')
+ (children ? `${S}${map(children, child => convertElToString(child)).join(S)}${S}` : '')
+ createElementClose(tag);
}
return convertElToString(el);
}
export function getCssString(
selectorNodes: Record<string, CSSSelectorVNode>,
animationNodes: Record<string, CSSAnimationVNode>,
opts?: {
newline?: boolean
}
) {
opts = opts || {};
const S = opts.newline ? '\n' : '';
const bracketBegin = ` {${S}`;
const bracketEnd = `${S}}`;
const selectors = map(keys(selectorNodes), className => {
return className + bracketBegin + map(keys(selectorNodes[className]), attrName => {
return `${attrName}:${selectorNodes[className][attrName]};`;
}).join(S) + bracketEnd;
}).join(S);
const animations = map(keys(animationNodes), (animationName) => {
return `@keyframes ${animationName}${bracketBegin}` + map(keys(animationNodes[animationName]), percent => {
return percent + bracketBegin + map(keys(animationNodes[animationName][percent]), attrName => {
let val = animationNodes[animationName][percent][attrName];
// postprocess
if (attrName === 'd') {
val = `path("${val}")`;
}
return `${attrName}:${val};`;
}).join(S) + bracketEnd;
}).join(S) + bracketEnd;
}).join(S);
if (!selectors && !animations) {
return '';
}
return ['<![CDATA[', selectors, animations, ']]>'].join(S);
}
export interface BrushScope {
zrId: string
shadowCache: Record<string, string>
gradientCache: Record<string, string>
patternCache: Record<string, string>
clipPathCache: Record<string, string>
defs: Record<string, SVGVNode>
cssNodes: Record<string, CSSSelectorVNode>
cssAnims: Record<string, Record<string, Record<string, string>>>
/**
* Cache for css style string, mapping from style string to class name.
*/
cssStyleCache: Record<string, string>
cssAnimIdx: number
shadowIdx: number
gradientIdx: number
patternIdx: number
clipPathIdx: number
// configs
/**
* If create animates nodes.
*/
animation?: boolean,
/**
* If create emphasis styles.
*/
emphasis?: boolean,
/**
* If will update. Some optimization for string generation can't be applied.
*/
willUpdate?: boolean
/**
* If compress the output string.
*/
compress?: boolean
}
export function createBrushScope(zrId: string): BrushScope {
return {
zrId,
shadowCache: {},
patternCache: {},
gradientCache: {},
clipPathCache: {},
defs: {},
cssNodes: {},
cssAnims: {},
cssStyleCache: {},
cssAnimIdx: 0,
shadowIdx: 0,
gradientIdx: 0,
patternIdx: 0,
clipPathIdx: 0
};
}
export function createSVGVNode(
width: number | string,
height: number | string,
children?: SVGVNode[],
useViewBox?: boolean
) {
return createVNode(
'svg',
'root',
{
'width': width,
'height': height,
'xmlns': SVGNS,
'xmlns:xlink': XLINKNS,
'version': '1.1',
'baseProfile': 'full',
'viewBox': useViewBox ? `0 0 ${width} ${height}` : false
},
children
);
}