This is an automated email from the ASF dual-hosted git repository.

wangzx pushed a commit to branch fix/getConnectedDataURL
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit c722a8b1e7b081c143c0b26af94f6e0cfcd120e3
Author: plainheart <y...@all-my-life.cn>
AuthorDate: Mon Jan 1 00:10:29 2024 +0800

    fix(core): improve `getConnectedDataURL` & `getDataURL`
    
    1) fix `backgroundColor` specified by `toolbox.saveAsImage` is not working 
when using SVG renderer
    2) fix temporary zr instance for `getConnectedDataURL` is never disposed - 
it may cause unexpected memory leak
    3) fix toolbox component is not excluded by `getConnectedDataURL` (Resolves 
#19422)
    4) fix `getConnectedDataURL` fails when connected charts are using SVG 
renderer (Resolves #18028, Resolves #19278)
---
 src/core/echarts.ts | 153 ++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 106 insertions(+), 47 deletions(-)

diff --git a/src/core/echarts.ts b/src/core/echarts.ts
index aaf538c72..14d30ba55 100644
--- a/src/core/echarts.ts
+++ b/src/core/echarts.ts
@@ -88,7 +88,11 @@ import loadingDefault from '../loading/default';
 import Scheduler from './Scheduler';
 import lightTheme from '../theme/light';
 import darkTheme from '../theme/dark';
-import {CoordinateSystemMaster, CoordinateSystemCreator, 
CoordinateSystemHostModel} from '../coord/CoordinateSystem';
+import type {
+    CoordinateSystemMaster,
+    CoordinateSystemCreator,
+    CoordinateSystemHostModel
+} from '../coord/CoordinateSystem';
 import { parseClassType } from '../util/clazz';
 import {ECEventProcessor} from '../util/ECEventProcessor';
 import {
@@ -109,7 +113,7 @@ import {
     ECElementEvent,
     AnimationOption
 } from '../util/types';
-import Displayable from 'zrender/src/graphic/Displayable';
+import type Displayable from 'zrender/src/graphic/Displayable';
 import { seriesSymbolTask, dataSymbolTask } from '../visual/symbol';
 import { getVisualFromData, getItemVisualFromData } from '../visual/helper';
 import { deprecateLog, deprecateReplaceLog, error, warn } from '../util/log';
@@ -121,8 +125,9 @@ import { createLocaleObject, SYSTEM_LANG, LocaleOption } 
from './locale';
 import type {EChartsOption} from '../export/option';
 import { findEventDispatcher } from '../util/event';
 import decal from '../visual/decal';
-import CanvasPainter from 'zrender/src/canvas/Painter';
-import SVGPainter from 'zrender/src/svg/Painter';
+import type CanvasPainter from 'zrender/src/canvas/Painter';
+import type SVGPainter from 'zrender/src/svg/Painter';
+import { encodeBase64 } from 'zrender/src/svg/helper';
 import lifecycle, {
     LifecycleEvents,
     UpdateLifecycleTransitionItem,
@@ -806,16 +811,27 @@ class ECharts extends Eventful<ECEventDefinition> {
             });
         });
 
-        const url = this._zr.painter.getType() === 'svg'
+        const isSvg = this._zr.painter.getType() === 'svg';
+        let backgroundColorToRecover;
+        if (isSvg && opts.backgroundColor) {
+            backgroundColorToRecover = this._zr.getBackgroundColor();
+            this._zr.setBackgroundColor(opts.backgroundColor);
+        }
+
+        const url = isSvg
             ? this.getSvgDataURL()
             : this.renderToCanvas(opts).toDataURL(
-                'image/' + (opts && opts.type || 'png')
+                'image/' + (opts.type || 'png')
             );
 
         each(excludesComponentViews, function (view) {
             view.group.ignore = false;
         });
 
+        backgroundColorToRecover && 
this._zr.setBackgroundColor(backgroundColorToRecover);
+
+        excludesComponentViews.length && this._zr.refreshImmediately();
+
         return url;
     }
 
@@ -832,24 +848,58 @@ class ECharts extends Eventful<ECEventDefinition> {
             return;
         }
 
-        const isSvg = opts.type === 'svg';
         const groupId = this.group;
-        const mathMin = Math.min;
-        const mathMax = Math.max;
-        const MAX_NUMBER = Infinity;
         if (connectedGroups[groupId]) {
+            opts = opts || {};
+
+            const isSvg = opts.type === 'svg';
+            const MAX_NUMBER = Infinity;
+            const mathMin = Math.min;
+            const mathMax = Math.max;
             let left = MAX_NUMBER;
             let top = MAX_NUMBER;
             let right = -MAX_NUMBER;
             let bottom = -MAX_NUMBER;
-            const canvasList: {dom: HTMLCanvasElement | string, left: number, 
top: number}[] = [];
-            const dpr = (opts && opts.pixelRatio) || 
this.getDevicePixelRatio();
-
-            each(instances, function (chart, id) {
+            const canvasList: {
+                dom: HTMLCanvasElement | string,
+                left: number,
+                top: number,
+                width: number,
+                height: number
+            }[] = [];
+            const dpr = isSvg
+                ? 1
+                : (opts.pixelRatio || this.getDevicePixelRatio());
+
+            each(instances, function (chart) {
                 if (chart.group === groupId) {
+                    const excludesComponentViews: ComponentView[] = [];
+                    each(opts.excludeComponents, function (componentType) {
+                        chart._model.eachComponent({
+                            mainType: componentType
+                        }, function (component) {
+                            const view = 
chart._componentsMap[component.__viewId];
+                            if (!view.group.ignore) {
+                                excludesComponentViews.push(view);
+                                view.group.ignore = true;
+                            }
+                        });
+                    });
+
+                    const currZr = chart._zr;
+
+                    const currChartIsSvg = currZr.painter.type === 'svg';
+                    let chartBackgroundColorToRecover;
+                    if (currChartIsSvg && opts.backgroundColor) {
+                        chartBackgroundColorToRecover = 
currZr.getBackgroundColor();
+                        currZr.setBackgroundColor(opts.backgroundColor);
+                    }
+
+                    // TODO: allow different renderers in one group
+                    // TODO: respect each chart's own backgroundColor
                     const canvas = isSvg
-                        ? (chart.getZr().painter as 
SVGPainter).getSvgDom().innerHTML
-                        : chart.renderToCanvas(clone(opts));
+                        ? currZr.painter.renderToString()
+                        : chart.renderToCanvas(opts);
                     const boundingRect = 
chart.getDom().getBoundingClientRect();
                     left = mathMin(boundingRect.left, left);
                     top = mathMin(boundingRect.top, top);
@@ -858,8 +908,22 @@ class ECharts extends Eventful<ECEventDefinition> {
                     canvasList.push({
                         dom: canvas,
                         left: boundingRect.left,
-                        top: boundingRect.top
+                        top: boundingRect.top,
+                        width: boundingRect.width,
+                        height: boundingRect.height
                     });
+
+                    if (chartBackgroundColorToRecover) {
+                        
currZr.setBackgroundColor(chartBackgroundColorToRecover);
+                    }
+
+                    if (excludesComponentViews.length) {
+                        each(excludesComponentViews, function (view) {
+                            view.group.ignore = false;
+                        });
+
+                        currZr.refresh();
+                    }
                 }
             });
 
@@ -867,50 +931,40 @@ class ECharts extends Eventful<ECEventDefinition> {
             top *= dpr;
             right *= dpr;
             bottom *= dpr;
+
             const width = right - left;
             const height = bottom - top;
-            const targetCanvas = platformApi.createCanvas();
+
+            /* global document */
+            const targetCanvas = isSvg
+                ? document.createElement('div')
+                : platformApi.createCanvas();
             const zr = zrender.init(targetCanvas, {
-                renderer: isSvg ? 'svg' : 'canvas'
-            });
-            zr.resize({
-                width: width,
-                height: height
+                renderer: isSvg ? 'svg' : 'canvas',
+                width,
+                height
             });
 
+            // Background between the charts
+            opts.connectedBackgroundColor && 
zr.setBackgroundColor(opts.connectedBackgroundColor);
+
             if (isSvg) {
                 let content = '';
                 each(canvasList, function (item) {
                     const x = item.left - left;
                     const y = item.top - top;
-                    content += '<g transform="translate(' + x + ','
-                        + y + ')">' + item.dom + '</g>';
+                    // eslint-disable-next-line max-len
+                    content += `<foreignObject x="${x}" y="${y}" 
width="${item.width}" height="${item.height}">${item.dom}</foreignObject>`;
                 });
-                (zr.painter as SVGPainter).getSvgRoot().innerHTML = content;
 
-                if (opts.connectedBackgroundColor) {
-                    (zr.painter as 
SVGPainter).setBackgroundColor(opts.connectedBackgroundColor as string);
-                }
+                const svgStr = (zr.painter as SVGPainter).renderToString()
+                    .replace('</svg>', content + '</svg>');
 
-                zr.refreshImmediately();
-                return (zr.painter as SVGPainter).toDataURL();
+                zr.dispose();
+
+                return 'data:image/svg+xml;base64,' + encodeBase64(svgStr);
             }
             else {
-                // Background between the charts
-                if (opts.connectedBackgroundColor) {
-                    zr.add(new graphic.Rect({
-                        shape: {
-                            x: 0,
-                            y: 0,
-                            width: width,
-                            height: height
-                        },
-                        style: {
-                            fill: opts.connectedBackgroundColor
-                        }
-                    }));
-                }
-
                 each(canvasList, function (item) {
                     const img = new graphic.Image({
                         style: {
@@ -921,9 +975,14 @@ class ECharts extends Eventful<ECEventDefinition> {
                     });
                     zr.add(img);
                 });
+
                 zr.refreshImmediately();
 
-                return targetCanvas.toDataURL('image/' + (opts && opts.type || 
'png'));
+                const dataURL = (targetCanvas as 
HTMLCanvasElement).toDataURL('image/' + (opts && opts.type || 'png'));
+
+                zr.dispose();
+
+                return dataURL;
             }
         }
         else {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org
For additional commands, e-mail: commits-h...@echarts.apache.org

Reply via email to