253 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
		
			
		
	
	
			253 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
| 
								 | 
							
								import { brush, setClipPath, setGradient, setPattern } from './graphic.js';
							 | 
						||
| 
								 | 
							
								import { createElement, createVNode, vNodeToString, getCssString, createBrushScope, createSVGVNode } from './core.js';
							 | 
						||
| 
								 | 
							
								import { normalizeColor, encodeBase64, isGradient, isPattern } from './helper.js';
							 | 
						||
| 
								 | 
							
								import { extend, keys, logError, map, noop, retrieve2 } from '../core/util.js';
							 | 
						||
| 
								 | 
							
								import patch, { updateAttrs } from './patch.js';
							 | 
						||
| 
								 | 
							
								import { getSize } from '../canvas/helper.js';
							 | 
						||
| 
								 | 
							
								var svgId = 0;
							 | 
						||
| 
								 | 
							
								var SVGPainter = (function () {
							 | 
						||
| 
								 | 
							
								    function SVGPainter(root, storage, opts) {
							 | 
						||
| 
								 | 
							
								        this.type = 'svg';
							 | 
						||
| 
								 | 
							
								        this.refreshHover = createMethodNotSupport('refreshHover');
							 | 
						||
| 
								 | 
							
								        this.configLayer = createMethodNotSupport('configLayer');
							 | 
						||
| 
								 | 
							
								        this.storage = storage;
							 | 
						||
| 
								 | 
							
								        this._opts = opts = extend({}, opts);
							 | 
						||
| 
								 | 
							
								        this.root = root;
							 | 
						||
| 
								 | 
							
								        this._id = 'zr' + svgId++;
							 | 
						||
| 
								 | 
							
								        this._oldVNode = createSVGVNode(opts.width, opts.height);
							 | 
						||
| 
								 | 
							
								        if (root && !opts.ssr) {
							 | 
						||
| 
								 | 
							
								            var viewport = this._viewport = document.createElement('div');
							 | 
						||
| 
								 | 
							
								            viewport.style.cssText = 'position:relative;overflow:hidden';
							 | 
						||
| 
								 | 
							
								            var svgDom = this._svgDom = this._oldVNode.elm = createElement('svg');
							 | 
						||
| 
								 | 
							
								            updateAttrs(null, this._oldVNode);
							 | 
						||
| 
								 | 
							
								            viewport.appendChild(svgDom);
							 | 
						||
| 
								 | 
							
								            root.appendChild(viewport);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.resize(opts.width, opts.height);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.getType = function () {
							 | 
						||
| 
								 | 
							
								        return this.type;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.getViewportRoot = function () {
							 | 
						||
| 
								 | 
							
								        return this._viewport;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.getViewportRootOffset = function () {
							 | 
						||
| 
								 | 
							
								        var viewportRoot = this.getViewportRoot();
							 | 
						||
| 
								 | 
							
								        if (viewportRoot) {
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								                offsetLeft: viewportRoot.offsetLeft || 0,
							 | 
						||
| 
								 | 
							
								                offsetTop: viewportRoot.offsetTop || 0
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.getSvgDom = function () {
							 | 
						||
| 
								 | 
							
								        return this._svgDom;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.refresh = function () {
							 | 
						||
| 
								 | 
							
								        if (this.root) {
							 | 
						||
| 
								 | 
							
								            var vnode = this.renderToVNode({
							 | 
						||
| 
								 | 
							
								                willUpdate: true
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								            vnode.attrs.style = 'position:absolute;left:0;top:0;user-select:none';
							 | 
						||
| 
								 | 
							
								            patch(this._oldVNode, vnode);
							 | 
						||
| 
								 | 
							
								            this._oldVNode = vnode;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.renderOneToVNode = function (el) {
							 | 
						||
| 
								 | 
							
								        return brush(el, createBrushScope(this._id));
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.renderToVNode = function (opts) {
							 | 
						||
| 
								 | 
							
								        opts = opts || {};
							 | 
						||
| 
								 | 
							
								        var list = this.storage.getDisplayList(true);
							 | 
						||
| 
								 | 
							
								        var width = this._width;
							 | 
						||
| 
								 | 
							
								        var height = this._height;
							 | 
						||
| 
								 | 
							
								        var scope = createBrushScope(this._id);
							 | 
						||
| 
								 | 
							
								        scope.animation = opts.animation;
							 | 
						||
| 
								 | 
							
								        scope.willUpdate = opts.willUpdate;
							 | 
						||
| 
								 | 
							
								        scope.compress = opts.compress;
							 | 
						||
| 
								 | 
							
								        scope.emphasis = opts.emphasis;
							 | 
						||
| 
								 | 
							
								        var children = [];
							 | 
						||
| 
								 | 
							
								        var bgVNode = this._bgVNode = createBackgroundVNode(width, height, this._backgroundColor, scope);
							 | 
						||
| 
								 | 
							
								        bgVNode && children.push(bgVNode);
							 | 
						||
| 
								 | 
							
								        var mainVNode = !opts.compress
							 | 
						||
| 
								 | 
							
								            ? (this._mainVNode = createVNode('g', 'main', {}, [])) : null;
							 | 
						||
| 
								 | 
							
								        this._paintList(list, scope, mainVNode ? mainVNode.children : children);
							 | 
						||
| 
								 | 
							
								        mainVNode && children.push(mainVNode);
							 | 
						||
| 
								 | 
							
								        var defs = map(keys(scope.defs), function (id) { return scope.defs[id]; });
							 | 
						||
| 
								 | 
							
								        if (defs.length) {
							 | 
						||
| 
								 | 
							
								            children.push(createVNode('defs', 'defs', {}, defs));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (opts.animation) {
							 | 
						||
| 
								 | 
							
								            var animationCssStr = getCssString(scope.cssNodes, scope.cssAnims, { newline: true });
							 | 
						||
| 
								 | 
							
								            if (animationCssStr) {
							 | 
						||
| 
								 | 
							
								                var styleNode = createVNode('style', 'stl', {}, [], animationCssStr);
							 | 
						||
| 
								 | 
							
								                children.push(styleNode);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return createSVGVNode(width, height, children, opts.useViewBox);
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.renderToString = function (opts) {
							 | 
						||
| 
								 | 
							
								        opts = opts || {};
							 | 
						||
| 
								 | 
							
								        return vNodeToString(this.renderToVNode({
							 | 
						||
| 
								 | 
							
								            animation: retrieve2(opts.cssAnimation, true),
							 | 
						||
| 
								 | 
							
								            emphasis: retrieve2(opts.cssEmphasis, true),
							 | 
						||
| 
								 | 
							
								            willUpdate: false,
							 | 
						||
| 
								 | 
							
								            compress: true,
							 | 
						||
| 
								 | 
							
								            useViewBox: retrieve2(opts.useViewBox, true)
							 | 
						||
| 
								 | 
							
								        }), { newline: true });
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.setBackgroundColor = function (backgroundColor) {
							 | 
						||
| 
								 | 
							
								        this._backgroundColor = backgroundColor;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.getSvgRoot = function () {
							 | 
						||
| 
								 | 
							
								        return this._mainVNode && this._mainVNode.elm;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype._paintList = function (list, scope, out) {
							 | 
						||
| 
								 | 
							
								        var listLen = list.length;
							 | 
						||
| 
								 | 
							
								        var clipPathsGroupsStack = [];
							 | 
						||
| 
								 | 
							
								        var clipPathsGroupsStackDepth = 0;
							 | 
						||
| 
								 | 
							
								        var currentClipPathGroup;
							 | 
						||
| 
								 | 
							
								        var prevClipPaths;
							 | 
						||
| 
								 | 
							
								        var clipGroupNodeIdx = 0;
							 | 
						||
| 
								 | 
							
								        for (var i = 0; i < listLen; i++) {
							 | 
						||
| 
								 | 
							
								            var displayable = list[i];
							 | 
						||
| 
								 | 
							
								            if (!displayable.invisible) {
							 | 
						||
| 
								 | 
							
								                var clipPaths = displayable.__clipPaths;
							 | 
						||
| 
								 | 
							
								                var len = clipPaths && clipPaths.length || 0;
							 | 
						||
| 
								 | 
							
								                var prevLen = prevClipPaths && prevClipPaths.length || 0;
							 | 
						||
| 
								 | 
							
								                var lca = void 0;
							 | 
						||
| 
								 | 
							
								                for (lca = Math.max(len - 1, prevLen - 1); lca >= 0; lca--) {
							 | 
						||
| 
								 | 
							
								                    if (clipPaths && prevClipPaths
							 | 
						||
| 
								 | 
							
								                        && clipPaths[lca] === prevClipPaths[lca]) {
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                for (var i_1 = prevLen - 1; i_1 > lca; i_1--) {
							 | 
						||
| 
								 | 
							
								                    clipPathsGroupsStackDepth--;
							 | 
						||
| 
								 | 
							
								                    currentClipPathGroup = clipPathsGroupsStack[clipPathsGroupsStackDepth - 1];
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                for (var i_2 = lca + 1; i_2 < len; i_2++) {
							 | 
						||
| 
								 | 
							
								                    var groupAttrs = {};
							 | 
						||
| 
								 | 
							
								                    setClipPath(clipPaths[i_2], groupAttrs, scope);
							 | 
						||
| 
								 | 
							
								                    var g = createVNode('g', 'clip-g-' + clipGroupNodeIdx++, groupAttrs, []);
							 | 
						||
| 
								 | 
							
								                    (currentClipPathGroup ? currentClipPathGroup.children : out).push(g);
							 | 
						||
| 
								 | 
							
								                    clipPathsGroupsStack[clipPathsGroupsStackDepth++] = g;
							 | 
						||
| 
								 | 
							
								                    currentClipPathGroup = g;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                prevClipPaths = clipPaths;
							 | 
						||
| 
								 | 
							
								                var ret = brush(displayable, scope);
							 | 
						||
| 
								 | 
							
								                if (ret) {
							 | 
						||
| 
								 | 
							
								                    (currentClipPathGroup ? currentClipPathGroup.children : out).push(ret);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.resize = function (width, height) {
							 | 
						||
| 
								 | 
							
								        var opts = this._opts;
							 | 
						||
| 
								 | 
							
								        var root = this.root;
							 | 
						||
| 
								 | 
							
								        var viewport = this._viewport;
							 | 
						||
| 
								 | 
							
								        width != null && (opts.width = width);
							 | 
						||
| 
								 | 
							
								        height != null && (opts.height = height);
							 | 
						||
| 
								 | 
							
								        if (root && viewport) {
							 | 
						||
| 
								 | 
							
								            viewport.style.display = 'none';
							 | 
						||
| 
								 | 
							
								            width = getSize(root, 0, opts);
							 | 
						||
| 
								 | 
							
								            height = getSize(root, 1, opts);
							 | 
						||
| 
								 | 
							
								            viewport.style.display = '';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (this._width !== width || this._height !== height) {
							 | 
						||
| 
								 | 
							
								            this._width = width;
							 | 
						||
| 
								 | 
							
								            this._height = height;
							 | 
						||
| 
								 | 
							
								            if (viewport) {
							 | 
						||
| 
								 | 
							
								                var viewportStyle = viewport.style;
							 | 
						||
| 
								 | 
							
								                viewportStyle.width = width + 'px';
							 | 
						||
| 
								 | 
							
								                viewportStyle.height = height + 'px';
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (!isPattern(this._backgroundColor)) {
							 | 
						||
| 
								 | 
							
								                var svgDom = this._svgDom;
							 | 
						||
| 
								 | 
							
								                if (svgDom) {
							 | 
						||
| 
								 | 
							
								                    svgDom.setAttribute('width', width);
							 | 
						||
| 
								 | 
							
								                    svgDom.setAttribute('height', height);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                var bgEl = this._bgVNode && this._bgVNode.elm;
							 | 
						||
| 
								 | 
							
								                if (bgEl) {
							 | 
						||
| 
								 | 
							
								                    bgEl.setAttribute('width', width);
							 | 
						||
| 
								 | 
							
								                    bgEl.setAttribute('height', height);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                this.refresh();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.getWidth = function () {
							 | 
						||
| 
								 | 
							
								        return this._width;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.getHeight = function () {
							 | 
						||
| 
								 | 
							
								        return this._height;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.dispose = function () {
							 | 
						||
| 
								 | 
							
								        if (this.root) {
							 | 
						||
| 
								 | 
							
								            this.root.innerHTML = '';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this._svgDom =
							 | 
						||
| 
								 | 
							
								            this._viewport =
							 | 
						||
| 
								 | 
							
								                this.storage =
							 | 
						||
| 
								 | 
							
								                    this._oldVNode =
							 | 
						||
| 
								 | 
							
								                        this._bgVNode =
							 | 
						||
| 
								 | 
							
								                            this._mainVNode = null;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.clear = function () {
							 | 
						||
| 
								 | 
							
								        if (this._svgDom) {
							 | 
						||
| 
								 | 
							
								            this._svgDom.innerHTML = null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this._oldVNode = null;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    SVGPainter.prototype.toDataURL = function (base64) {
							 | 
						||
| 
								 | 
							
								        var str = this.renderToString();
							 | 
						||
| 
								 | 
							
								        var prefix = 'data:image/svg+xml;';
							 | 
						||
| 
								 | 
							
								        if (base64) {
							 | 
						||
| 
								 | 
							
								            str = encodeBase64(str);
							 | 
						||
| 
								 | 
							
								            return str && prefix + 'base64,' + str;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return prefix + 'charset=UTF-8,' + encodeURIComponent(str);
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    return SVGPainter;
							 | 
						||
| 
								 | 
							
								}());
							 | 
						||
| 
								 | 
							
								function createMethodNotSupport(method) {
							 | 
						||
| 
								 | 
							
								    return function () {
							 | 
						||
| 
								 | 
							
								        if (process.env.NODE_ENV !== 'production') {
							 | 
						||
| 
								 | 
							
								            logError('In SVG mode painter not support method "' + method + '"');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function createBackgroundVNode(width, height, backgroundColor, scope) {
							 | 
						||
| 
								 | 
							
								    var bgVNode;
							 | 
						||
| 
								 | 
							
								    if (backgroundColor && backgroundColor !== 'none') {
							 | 
						||
| 
								 | 
							
								        bgVNode = createVNode('rect', 'bg', {
							 | 
						||
| 
								 | 
							
								            width: width,
							 | 
						||
| 
								 | 
							
								            height: height,
							 | 
						||
| 
								 | 
							
								            x: '0',
							 | 
						||
| 
								 | 
							
								            y: '0'
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        if (isGradient(backgroundColor)) {
							 | 
						||
| 
								 | 
							
								            setGradient({ fill: backgroundColor }, bgVNode.attrs, 'fill', scope);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (isPattern(backgroundColor)) {
							 | 
						||
| 
								 | 
							
								            setPattern({
							 | 
						||
| 
								 | 
							
								                style: {
							 | 
						||
| 
								 | 
							
								                    fill: backgroundColor
							 | 
						||
| 
								 | 
							
								                },
							 | 
						||
| 
								 | 
							
								                dirty: noop,
							 | 
						||
| 
								 | 
							
								                getBoundingRect: function () { return ({ width: width, height: height }); }
							 | 
						||
| 
								 | 
							
								            }, bgVNode.attrs, 'fill', scope);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            var _a = normalizeColor(backgroundColor), color = _a.color, opacity = _a.opacity;
							 | 
						||
| 
								 | 
							
								            bgVNode.attrs.fill = color;
							 | 
						||
| 
								 | 
							
								            opacity < 1 && (bgVNode.attrs['fill-opacity'] = opacity);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return bgVNode;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								export default SVGPainter;
							 |