You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
12 KiB
292 lines
12 KiB
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(/></g, '>\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;
|
|
|