import { createElement, SVGNS, XLINKNS, XMLNS } from '../svg/core.js'; import { normalizeColor } from '../svg/helper.js'; import * as util from '../core/util.js'; import Path from '../graphic/Path.js'; import ZRImage from '../graphic/Image.js'; import TSpan from '../graphic/TSpan.js'; import arrayDiff from '../core/arrayDiff.js'; import GradientManager from './helper/GradientManager.js'; import PatternManager from './helper/PatternManager.js'; import ClippathManager, { hasClipPath } from './helper/ClippathManager.js'; import ShadowManager from './helper/ShadowManager.js'; import { path as svgPath, image as svgImage, text as svgText } from './graphic.js'; import { getSize } from '../canvas/helper.js'; function getSvgProxy(el) { if (el instanceof Path) { return svgPath; } else if (el instanceof ZRImage) { return svgImage; } else if (el instanceof TSpan) { return svgText; } else { return svgPath; } } function checkParentAvailable(parent, child) { return child && parent && child.parentNode !== parent; } function insertAfter(parent, child, prevSibling) { if (checkParentAvailable(parent, child) && prevSibling) { var nextSibling = prevSibling.nextSibling; nextSibling ? parent.insertBefore(child, nextSibling) : parent.appendChild(child); } } function prepend(parent, child) { if (checkParentAvailable(parent, child)) { var firstChild = parent.firstChild; firstChild ? parent.insertBefore(child, firstChild) : parent.appendChild(child); } } function remove(parent, child) { if (child && parent && child.parentNode === parent) { parent.removeChild(child); } } function removeFromMyParent(child) { if (child && child.parentNode) { child.parentNode.removeChild(child); } } function getSvgElement(displayable) { return displayable.__svgEl; } var SVGPainter = (function () { function SVGPainter(root, storage, opts, zrId) { this.type = 'svg'; this.refreshHover = createMethodNotSupport('refreshHover'); this.configLayer = createMethodNotSupport('configLayer'); this.root = root; this.storage = storage; this._opts = opts = util.extend({}, opts || {}); var svgDom = createElement('svg'); svgDom.setAttributeNS(XMLNS, 'xmlns', SVGNS); svgDom.setAttributeNS(XMLNS, 'xmlns:xlink', XLINKNS); svgDom.setAttribute('version', '1.1'); svgDom.setAttribute('baseProfile', 'full'); svgDom.style.cssText = 'user-select:none;position:absolute;left:0;top:0;'; var bgRoot = createElement('g'); svgDom.appendChild(bgRoot); var svgRoot = createElement('g'); svgDom.appendChild(svgRoot); this._gradientManager = new GradientManager(zrId, svgRoot); this._patternManager = new PatternManager(zrId, svgRoot); this._clipPathManager = new ClippathManager(zrId, svgRoot); this._shadowManager = new ShadowManager(zrId, svgRoot); var viewport = document.createElement('div'); viewport.style.cssText = 'overflow:hidden;position:relative'; this._svgDom = svgDom; this._svgRoot = svgRoot; this._backgroundRoot = bgRoot; this._viewport = viewport; root.appendChild(viewport); viewport.appendChild(svgDom); this.resize(opts.width, opts.height); this._visibleList = []; } SVGPainter.prototype.getType = function () { return 'svg'; }; SVGPainter.prototype.getViewportRoot = function () { return this._viewport; }; SVGPainter.prototype.getSvgDom = function () { return this._svgDom; }; SVGPainter.prototype.getSvgRoot = function () { return this._svgRoot; }; SVGPainter.prototype.getViewportRootOffset = function () { var viewportRoot = this.getViewportRoot(); if (viewportRoot) { return { offsetLeft: viewportRoot.offsetLeft || 0, offsetTop: viewportRoot.offsetTop || 0 }; } }; SVGPainter.prototype.refresh = function () { var list = this.storage.getDisplayList(true); this._paintList(list); }; SVGPainter.prototype.setBackgroundColor = function (backgroundColor) { if (this._backgroundRoot && this._backgroundNode) { this._backgroundRoot.removeChild(this._backgroundNode); } var bgNode = createElement('rect'); bgNode.setAttribute('width', this.getWidth()); bgNode.setAttribute('height', this.getHeight()); bgNode.setAttribute('x', 0); bgNode.setAttribute('y', 0); bgNode.setAttribute('id', 0); var _a = normalizeColor(backgroundColor), color = _a.color, opacity = _a.opacity; bgNode.setAttribute('fill', color); bgNode.setAttribute('fill-opacity', opacity); this._backgroundRoot.appendChild(bgNode); this._backgroundNode = bgNode; }; SVGPainter.prototype.createSVGElement = function (tag) { return createElement(tag); }; SVGPainter.prototype.paintOne = function (el) { var svgProxy = getSvgProxy(el); svgProxy && svgProxy.brush(el); return getSvgElement(el); }; SVGPainter.prototype._paintList = function (list) { var gradientManager = this._gradientManager; var patternManager = this._patternManager; var clipPathManager = this._clipPathManager; var shadowManager = this._shadowManager; gradientManager.markAllUnused(); patternManager.markAllUnused(); clipPathManager.markAllUnused(); shadowManager.markAllUnused(); var svgRoot = this._svgRoot; var visibleList = this._visibleList; var listLen = list.length; var newVisibleList = []; for (var i = 0; i < listLen; i++) { var displayable = list[i]; var svgProxy = getSvgProxy(displayable); var svgElement = getSvgElement(displayable); if (!displayable.invisible) { if (displayable.__dirty || !svgElement) { svgProxy && svgProxy.brush(displayable); svgElement = getSvgElement(displayable); if (svgElement && displayable.style) { gradientManager.update(displayable.style.fill); gradientManager.update(displayable.style.stroke); patternManager.update(displayable.style.fill); patternManager.update(displayable.style.stroke); shadowManager.update(svgElement, displayable); } displayable.__dirty = 0; } if (svgElement) { newVisibleList.push(displayable); } } } var diff = arrayDiff(visibleList, newVisibleList); var prevSvgElement; var topPrevSvgElement; for (var i = 0; i < diff.length; i++) { var item = diff[i]; if (item.removed) { for (var k = 0; k < item.count; k++) { var displayable = visibleList[item.indices[k]]; var svgElement = getSvgElement(displayable); hasClipPath(displayable) ? removeFromMyParent(svgElement) : remove(svgRoot, svgElement); } } } var prevDisplayable; var currentClipGroup; for (var i = 0; i < diff.length; i++) { var item = diff[i]; if (item.removed) { continue; } for (var k = 0; k < item.count; k++) { var displayable = newVisibleList[item.indices[k]]; var clipGroup = clipPathManager.update(displayable, prevDisplayable); if (clipGroup !== currentClipGroup) { prevSvgElement = topPrevSvgElement; if (clipGroup) { prevSvgElement ? insertAfter(svgRoot, clipGroup, prevSvgElement) : prepend(svgRoot, clipGroup); topPrevSvgElement = clipGroup; prevSvgElement = null; } currentClipGroup = clipGroup; } var svgElement = getSvgElement(displayable); prevSvgElement ? insertAfter(currentClipGroup || svgRoot, svgElement, prevSvgElement) : prepend(currentClipGroup || svgRoot, svgElement); prevSvgElement = svgElement || prevSvgElement; if (!currentClipGroup) { topPrevSvgElement = prevSvgElement; } gradientManager.markUsed(displayable); gradientManager.addWithoutUpdate(svgElement, displayable); patternManager.markUsed(displayable); patternManager.addWithoutUpdate(svgElement, displayable); clipPathManager.markUsed(displayable); prevDisplayable = displayable; } } gradientManager.removeUnused(); patternManager.removeUnused(); clipPathManager.removeUnused(); shadowManager.removeUnused(); this._visibleList = newVisibleList; }; SVGPainter.prototype.resize = function (width, height) { var viewport = this._viewport; viewport.style.display = 'none'; var opts = this._opts; width != null && (opts.width = width); height != null && (opts.height = height); width = getSize(this.root, 0, opts); height = getSize(this.root, 1, opts); viewport.style.display = ''; if (this._width !== width || this._height !== height) { this._width = width; this._height = height; var viewportStyle = viewport.style; viewportStyle.width = width + 'px'; viewportStyle.height = height + 'px'; var svgRoot = this._svgDom; svgRoot.setAttribute('width', width + ''); svgRoot.setAttribute('height', height + ''); } if (this._backgroundNode) { this._backgroundNode.setAttribute('width', width); this._backgroundNode.setAttribute('height', height); } }; SVGPainter.prototype.getWidth = function () { return this._width; }; SVGPainter.prototype.getHeight = function () { return this._height; }; SVGPainter.prototype.dispose = function () { this.root.innerHTML = ''; this._svgRoot = this._backgroundRoot = this._svgDom = this._backgroundNode = this._viewport = this.storage = null; }; SVGPainter.prototype.clear = function () { var viewportNode = this._viewport; if (viewportNode && viewportNode.parentNode) { viewportNode.parentNode.removeChild(viewportNode); } }; SVGPainter.prototype.toDataURL = function () { this.refresh(); var svgDom = this._svgDom; var outerHTML = svgDom.outerHTML || (svgDom.parentNode && svgDom.parentNode.innerHTML); var html = encodeURIComponent(outerHTML.replace(/>\n\r<')); return 'data:image/svg+xml;charset=UTF-8,' + html; }; return SVGPainter; }()); function createMethodNotSupport(method) { return function () { if (process.env.NODE_ENV !== 'production') { util.logError('In SVG mode painter not support method "' + method + '"'); } }; } export default SVGPainter;