1 | import { createElement, SVGNS, XLINKNS, XMLNS } from '../svg/core.js';
|
2 | import { normalizeColor } from '../svg/helper.js';
|
3 | import * as util from '../core/util.js';
|
4 | import Path from '../graphic/Path.js';
|
5 | import ZRImage from '../graphic/Image.js';
|
6 | import TSpan from '../graphic/TSpan.js';
|
7 | import arrayDiff from '../core/arrayDiff.js';
|
8 | import GradientManager from './helper/GradientManager.js';
|
9 | import PatternManager from './helper/PatternManager.js';
|
10 | import ClippathManager, { hasClipPath } from './helper/ClippathManager.js';
|
11 | import ShadowManager from './helper/ShadowManager.js';
|
12 | import { path as svgPath, image as svgImage, text as svgText } from './graphic.js';
|
13 | import { getSize } from '../canvas/helper.js';
|
14 | function getSvgProxy(el) {
|
15 | if (el instanceof Path) {
|
16 | return svgPath;
|
17 | }
|
18 | else if (el instanceof ZRImage) {
|
19 | return svgImage;
|
20 | }
|
21 | else if (el instanceof TSpan) {
|
22 | return svgText;
|
23 | }
|
24 | else {
|
25 | return svgPath;
|
26 | }
|
27 | }
|
28 | function checkParentAvailable(parent, child) {
|
29 | return child && parent && child.parentNode !== parent;
|
30 | }
|
31 | function insertAfter(parent, child, prevSibling) {
|
32 | if (checkParentAvailable(parent, child) && prevSibling) {
|
33 | var nextSibling = prevSibling.nextSibling;
|
34 | nextSibling ? parent.insertBefore(child, nextSibling)
|
35 | : parent.appendChild(child);
|
36 | }
|
37 | }
|
38 | function prepend(parent, child) {
|
39 | if (checkParentAvailable(parent, child)) {
|
40 | var firstChild = parent.firstChild;
|
41 | firstChild ? parent.insertBefore(child, firstChild)
|
42 | : parent.appendChild(child);
|
43 | }
|
44 | }
|
45 | function remove(parent, child) {
|
46 | if (child && parent && child.parentNode === parent) {
|
47 | parent.removeChild(child);
|
48 | }
|
49 | }
|
50 | function removeFromMyParent(child) {
|
51 | if (child && child.parentNode) {
|
52 | child.parentNode.removeChild(child);
|
53 | }
|
54 | }
|
55 | function getSvgElement(displayable) {
|
56 | return displayable.__svgEl;
|
57 | }
|
58 | var SVGPainter = (function () {
|
59 | function SVGPainter(root, storage, opts, zrId) {
|
60 | this.type = 'svg';
|
61 | this.refreshHover = createMethodNotSupport('refreshHover');
|
62 | this.configLayer = createMethodNotSupport('configLayer');
|
63 | this.root = root;
|
64 | this.storage = storage;
|
65 | this._opts = opts = util.extend({}, opts || {});
|
66 | var svgDom = createElement('svg');
|
67 | svgDom.setAttributeNS(XMLNS, 'xmlns', SVGNS);
|
68 | svgDom.setAttributeNS(XMLNS, 'xmlns:xlink', XLINKNS);
|
69 | svgDom.setAttribute('version', '1.1');
|
70 | svgDom.setAttribute('baseProfile', 'full');
|
71 | svgDom.style.cssText = 'user-select:none;position:absolute;left:0;top:0;';
|
72 | var bgRoot = createElement('g');
|
73 | svgDom.appendChild(bgRoot);
|
74 | var svgRoot = createElement('g');
|
75 | svgDom.appendChild(svgRoot);
|
76 | this._gradientManager = new GradientManager(zrId, svgRoot);
|
77 | this._patternManager = new PatternManager(zrId, svgRoot);
|
78 | this._clipPathManager = new ClippathManager(zrId, svgRoot);
|
79 | this._shadowManager = new ShadowManager(zrId, svgRoot);
|
80 | var viewport = document.createElement('div');
|
81 | viewport.style.cssText = 'overflow:hidden;position:relative';
|
82 | this._svgDom = svgDom;
|
83 | this._svgRoot = svgRoot;
|
84 | this._backgroundRoot = bgRoot;
|
85 | this._viewport = viewport;
|
86 | root.appendChild(viewport);
|
87 | viewport.appendChild(svgDom);
|
88 | this.resize(opts.width, opts.height);
|
89 | this._visibleList = [];
|
90 | }
|
91 | SVGPainter.prototype.getType = function () {
|
92 | return 'svg';
|
93 | };
|
94 | SVGPainter.prototype.getViewportRoot = function () {
|
95 | return this._viewport;
|
96 | };
|
97 | SVGPainter.prototype.getSvgDom = function () {
|
98 | return this._svgDom;
|
99 | };
|
100 | SVGPainter.prototype.getSvgRoot = function () {
|
101 | return this._svgRoot;
|
102 | };
|
103 | SVGPainter.prototype.getViewportRootOffset = function () {
|
104 | var viewportRoot = this.getViewportRoot();
|
105 | if (viewportRoot) {
|
106 | return {
|
107 | offsetLeft: viewportRoot.offsetLeft || 0,
|
108 | offsetTop: viewportRoot.offsetTop || 0
|
109 | };
|
110 | }
|
111 | };
|
112 | SVGPainter.prototype.refresh = function () {
|
113 | var list = this.storage.getDisplayList(true);
|
114 | this._paintList(list);
|
115 | };
|
116 | SVGPainter.prototype.setBackgroundColor = function (backgroundColor) {
|
117 | if (this._backgroundRoot && this._backgroundNode) {
|
118 | this._backgroundRoot.removeChild(this._backgroundNode);
|
119 | }
|
120 | var bgNode = createElement('rect');
|
121 | bgNode.setAttribute('width', this.getWidth());
|
122 | bgNode.setAttribute('height', this.getHeight());
|
123 | bgNode.setAttribute('x', 0);
|
124 | bgNode.setAttribute('y', 0);
|
125 | bgNode.setAttribute('id', 0);
|
126 | var _a = normalizeColor(backgroundColor), color = _a.color, opacity = _a.opacity;
|
127 | bgNode.setAttribute('fill', color);
|
128 | bgNode.setAttribute('fill-opacity', opacity);
|
129 | this._backgroundRoot.appendChild(bgNode);
|
130 | this._backgroundNode = bgNode;
|
131 | };
|
132 | SVGPainter.prototype.createSVGElement = function (tag) {
|
133 | return createElement(tag);
|
134 | };
|
135 | SVGPainter.prototype.paintOne = function (el) {
|
136 | var svgProxy = getSvgProxy(el);
|
137 | svgProxy && svgProxy.brush(el);
|
138 | return getSvgElement(el);
|
139 | };
|
140 | SVGPainter.prototype._paintList = function (list) {
|
141 | var gradientManager = this._gradientManager;
|
142 | var patternManager = this._patternManager;
|
143 | var clipPathManager = this._clipPathManager;
|
144 | var shadowManager = this._shadowManager;
|
145 | gradientManager.markAllUnused();
|
146 | patternManager.markAllUnused();
|
147 | clipPathManager.markAllUnused();
|
148 | shadowManager.markAllUnused();
|
149 | var svgRoot = this._svgRoot;
|
150 | var visibleList = this._visibleList;
|
151 | var listLen = list.length;
|
152 | var newVisibleList = [];
|
153 | for (var i = 0; i < listLen; i++) {
|
154 | var displayable = list[i];
|
155 | var svgProxy = getSvgProxy(displayable);
|
156 | var svgElement = getSvgElement(displayable);
|
157 | if (!displayable.invisible) {
|
158 | if (displayable.__dirty || !svgElement) {
|
159 | svgProxy && svgProxy.brush(displayable);
|
160 | svgElement = getSvgElement(displayable);
|
161 | if (svgElement && displayable.style) {
|
162 | gradientManager.update(displayable.style.fill);
|
163 | gradientManager.update(displayable.style.stroke);
|
164 | patternManager.update(displayable.style.fill);
|
165 | patternManager.update(displayable.style.stroke);
|
166 | shadowManager.update(svgElement, displayable);
|
167 | }
|
168 | displayable.__dirty = 0;
|
169 | }
|
170 | if (svgElement) {
|
171 | newVisibleList.push(displayable);
|
172 | }
|
173 | }
|
174 | }
|
175 | var diff = arrayDiff(visibleList, newVisibleList);
|
176 | var prevSvgElement;
|
177 | var topPrevSvgElement;
|
178 | for (var i = 0; i < diff.length; i++) {
|
179 | var item = diff[i];
|
180 | if (item.removed) {
|
181 | for (var k = 0; k < item.count; k++) {
|
182 | var displayable = visibleList[item.indices[k]];
|
183 | var svgElement = getSvgElement(displayable);
|
184 | hasClipPath(displayable) ? removeFromMyParent(svgElement)
|
185 | : remove(svgRoot, svgElement);
|
186 | }
|
187 | }
|
188 | }
|
189 | var prevDisplayable;
|
190 | var currentClipGroup;
|
191 | for (var i = 0; i < diff.length; i++) {
|
192 | var item = diff[i];
|
193 | if (item.removed) {
|
194 | continue;
|
195 | }
|
196 | for (var k = 0; k < item.count; k++) {
|
197 | var displayable = newVisibleList[item.indices[k]];
|
198 | var clipGroup = clipPathManager.update(displayable, prevDisplayable);
|
199 | if (clipGroup !== currentClipGroup) {
|
200 | prevSvgElement = topPrevSvgElement;
|
201 | if (clipGroup) {
|
202 | prevSvgElement ? insertAfter(svgRoot, clipGroup, prevSvgElement)
|
203 | : prepend(svgRoot, clipGroup);
|
204 | topPrevSvgElement = clipGroup;
|
205 | prevSvgElement = null;
|
206 | }
|
207 | currentClipGroup = clipGroup;
|
208 | }
|
209 | var svgElement = getSvgElement(displayable);
|
210 | prevSvgElement
|
211 | ? insertAfter(currentClipGroup || svgRoot, svgElement, prevSvgElement)
|
212 | : prepend(currentClipGroup || svgRoot, svgElement);
|
213 | prevSvgElement = svgElement || prevSvgElement;
|
214 | if (!currentClipGroup) {
|
215 | topPrevSvgElement = prevSvgElement;
|
216 | }
|
217 | gradientManager.markUsed(displayable);
|
218 | gradientManager.addWithoutUpdate(svgElement, displayable);
|
219 | patternManager.markUsed(displayable);
|
220 | patternManager.addWithoutUpdate(svgElement, displayable);
|
221 | clipPathManager.markUsed(displayable);
|
222 | prevDisplayable = displayable;
|
223 | }
|
224 | }
|
225 | gradientManager.removeUnused();
|
226 | patternManager.removeUnused();
|
227 | clipPathManager.removeUnused();
|
228 | shadowManager.removeUnused();
|
229 | this._visibleList = newVisibleList;
|
230 | };
|
231 | SVGPainter.prototype.resize = function (width, height) {
|
232 | var viewport = this._viewport;
|
233 | viewport.style.display = 'none';
|
234 | var opts = this._opts;
|
235 | width != null && (opts.width = width);
|
236 | height != null && (opts.height = height);
|
237 | width = getSize(this.root, 0, opts);
|
238 | height = getSize(this.root, 1, opts);
|
239 | viewport.style.display = '';
|
240 | if (this._width !== width || this._height !== height) {
|
241 | this._width = width;
|
242 | this._height = height;
|
243 | var viewportStyle = viewport.style;
|
244 | viewportStyle.width = width + 'px';
|
245 | viewportStyle.height = height + 'px';
|
246 | var svgRoot = this._svgDom;
|
247 | svgRoot.setAttribute('width', width + '');
|
248 | svgRoot.setAttribute('height', height + '');
|
249 | }
|
250 | if (this._backgroundNode) {
|
251 | this._backgroundNode.setAttribute('width', width);
|
252 | this._backgroundNode.setAttribute('height', height);
|
253 | }
|
254 | };
|
255 | SVGPainter.prototype.getWidth = function () {
|
256 | return this._width;
|
257 | };
|
258 | SVGPainter.prototype.getHeight = function () {
|
259 | return this._height;
|
260 | };
|
261 | SVGPainter.prototype.dispose = function () {
|
262 | this.root.innerHTML = '';
|
263 | this._svgRoot =
|
264 | this._backgroundRoot =
|
265 | this._svgDom =
|
266 | this._backgroundNode =
|
267 | this._viewport = this.storage = null;
|
268 | };
|
269 | SVGPainter.prototype.clear = function () {
|
270 | var viewportNode = this._viewport;
|
271 | if (viewportNode && viewportNode.parentNode) {
|
272 | viewportNode.parentNode.removeChild(viewportNode);
|
273 | }
|
274 | };
|
275 | SVGPainter.prototype.toDataURL = function () {
|
276 | this.refresh();
|
277 | var svgDom = this._svgDom;
|
278 | var outerHTML = svgDom.outerHTML
|
279 | || (svgDom.parentNode && svgDom.parentNode.innerHTML);
|
280 | var html = encodeURIComponent(outerHTML.replace(/></g, '>\n\r<'));
|
281 | return 'data:image/svg+xml;charset=UTF-8,' + html;
|
282 | };
|
283 | return SVGPainter;
|
284 | }());
|
285 | function createMethodNotSupport(method) {
|
286 | return function () {
|
287 | if (process.env.NODE_ENV !== 'production') {
|
288 | util.logError('In SVG mode painter not support method "' + method + '"');
|
289 | }
|
290 | };
|
291 | }
|
292 | export default SVGPainter;
|