You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2021/10/03 10:52:20 UTC

[echarts] 01/01: wip(ssr): integrate ssr svg output. upgrade ts

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

shenyi pushed a commit to branch svg-ssr
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 44e26a8c9e55adbd33df9a9ad71a2b11cb32a28f
Author: pissang <bm...@gmail.com>
AuthorDate: Sun Oct 3 18:51:17 2021 +0800

    wip(ssr): integrate ssr svg output. upgrade ts
---
 build/dev-fast.js                            |  3 +-
 package-lock.json                            |  6 +--
 package.json                                 |  2 +-
 src/component/toolbox/feature/SaveAsImage.ts |  4 +-
 src/core/echarts.ts                          | 64 +++++++++++++++-----------
 src/util/decal.ts                            | 17 +++++--
 src/util/states.ts                           |  2 +-
 test/node/ssr.js                             | 67 ++++++++++++++++++++++++++++
 8 files changed, 129 insertions(+), 36 deletions(-)

diff --git a/build/dev-fast.js b/build/dev-fast.js
index 2a1aacb..8d417e7 100644
--- a/build/dev-fast.js
+++ b/build/dev-fast.js
@@ -26,8 +26,9 @@ const outFilePath = path.resolve(__dirname, '../dist/echarts.js');
 const umdMark = '// ------------- WRAPPED UMD --------------- //';
 const umdWrapperHead = `
 ${umdMark}
+typeof window !== 'undefined' ? window.__DEV__ = true
+    : typeof global !== 'undefined' ? global.__DEV__ = true : __DEV__ = true;
 (function (root, factory) {
-    window.__DEV__ = true;
     if (typeof define === 'function' && define.amd) {
         // AMD. Register as an anonymous module.
         define(['exports'], factory);
diff --git a/package-lock.json b/package-lock.json
index c319fcf..8236e54 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10619,9 +10619,9 @@
       }
     },
     "typescript": {
-      "version": "4.3.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
-      "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
+      "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==",
       "dev": true
     },
     "unbzip2-stream": {
diff --git a/package.json b/package.json
index 319d57e..7469582 100644
--- a/package.json
+++ b/package.json
@@ -106,6 +106,6 @@
     "socket.io": "2.2.0",
     "terser": "^5.3.8",
     "ts-jest": "^26.4.3",
-    "typescript": "4.3.5"
+    "typescript": "4.4.3"
   }
 }
diff --git a/src/component/toolbox/feature/SaveAsImage.ts b/src/component/toolbox/feature/SaveAsImage.ts
index c59bb86..6410b87 100644
--- a/src/component/toolbox/feature/SaveAsImage.ts
+++ b/src/component/toolbox/feature/SaveAsImage.ts
@@ -74,6 +74,7 @@ class SaveAsImage extends ToolboxFeature<ToolboxSaveAsImageFeatureOption> {
         }
         // IE or old Edge
         else {
+            // @ts-ignore
             if (window.navigator.msSaveOrOpenBlob || isSvg) {
                 const parts = url.split(',');
                 // data:[<mime type>][;charset=<charset>][;base64],<encoded data>
@@ -88,13 +89,14 @@ class SaveAsImage extends ToolboxFeature<ToolboxSaveAsImageFeatureOption> {
                 // (just a url-encoded string through `encodeURIComponent`)
                 base64Encoded && (bstr = window.atob(bstr));
                 const filename = title + '.' + type;
+                // @ts-ignore
                 if (window.navigator.msSaveOrOpenBlob) {
                     let n = bstr.length;
                     const u8arr = new Uint8Array(n);
                     while (n--) {
                         u8arr[n] = bstr.charCodeAt(n);
                     }
-                    const blob = new Blob([u8arr]);
+                    const blob = new Blob([u8arr]);// @ts-ignore
                     window.navigator.msSaveOrOpenBlob(blob, filename);
                 }
                 else {
diff --git a/src/core/echarts.ts b/src/core/echarts.ts
index 8a61c98..b997116 100644
--- a/src/core/echarts.ts
+++ b/src/core/echarts.ts
@@ -327,6 +327,7 @@ type EChartsInitOpts = {
     renderer?: RendererType,
     devicePixelRatio?: number,
     useDirtyRect?: boolean,
+    ssr?: boolean,
     width?: number,
     height?: number
 };
@@ -343,6 +344,8 @@ class ECharts extends Eventful<ECEventDefinition> {
      */
     group: string;
 
+    private _ssr: boolean;
+
     private _zr: zrender.ZRenderType;
 
     private _dom: HTMLElement;
@@ -429,8 +432,10 @@ class ECharts extends Eventful<ECEventDefinition> {
             devicePixelRatio: opts.devicePixelRatio,
             width: opts.width,
             height: opts.height,
+            ssr: opts.ssr,
             useDirtyRect: opts.useDirtyRect == null ? defaultUseDirtyRect : opts.useDirtyRect
         });
+        this._ssr = opts.ssr;
 
         // Expect 60 fps.
         this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);
@@ -634,7 +639,10 @@ class ECharts extends Eventful<ECEventDefinition> {
 
             // Ensure zr refresh sychronously, and then pixel in canvas can be
             // fetched after `setOption`.
-            this._zr.flush();
+            if (!this._ssr) {
+                // not use flush when using ssr mode.
+                this._zr.flush();
+            }
 
             this[PENDING_UPDATE] = null;
             this[IN_MAIN_PROCESS_KEY] = false;
@@ -1121,7 +1129,10 @@ class ECharts extends Eventful<ECEventDefinition> {
         }
         this._disposed = true;
 
-        modelUtil.setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
+        const dom = this.getDom();
+        if (dom) {
+            modelUtil.setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
+        }
 
         const chart = this;
         const api = chart._api;
@@ -2546,32 +2557,35 @@ export function init(
     theme?: string | object,
     opts?: EChartsInitOpts
 ): EChartsType {
-    if (__DEV__) {
-        if (!dom) {
-            throw new Error('Initialize failed: invalid dom.');
+    const isClient = !(opts && opts.ssr);
+    if (isClient) {
+        if (__DEV__) {
+            if (!dom) {
+                throw new Error('Initialize failed: invalid dom.');
+            }
         }
-    }
 
-    const existInstance = getInstanceByDom(dom);
-    if (existInstance) {
-        if (__DEV__) {
-            console.warn('There is a chart instance already initialized on the dom.');
+        const existInstance = getInstanceByDom(dom);
+        if (existInstance) {
+            if (__DEV__) {
+                console.warn('There is a chart instance already initialized on the dom.');
+            }
+            return existInstance;
         }
-        return existInstance;
-    }
 
-    if (__DEV__) {
-        if (isDom(dom)
-            && dom.nodeName.toUpperCase() !== 'CANVAS'
-            && (
-                (!dom.clientWidth && (!opts || opts.width == null))
-                || (!dom.clientHeight && (!opts || opts.height == null))
-            )
-        ) {
-            console.warn('Can\'t get DOM width or height. Please check '
-            + 'dom.clientWidth and dom.clientHeight. They should not be 0.'
-            + 'For example, you may need to call this in the callback '
-            + 'of window.onload.');
+        if (__DEV__) {
+            if (isDom(dom)
+                && dom.nodeName.toUpperCase() !== 'CANVAS'
+                && (
+                    (!dom.clientWidth && (!opts || opts.width == null))
+                    || (!dom.clientHeight && (!opts || opts.height == null))
+                )
+            ) {
+                console.warn('Can\'t get DOM width or height. Please check '
+                + 'dom.clientWidth and dom.clientHeight. They should not be 0.'
+                + 'For example, you may need to call this in the callback '
+                + 'of window.onload.');
+            }
         }
     }
 
@@ -2579,7 +2593,7 @@ export function init(
     chart.id = 'ec_' + idBase++;
     instances[chart.id] = chart;
 
-    modelUtil.setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);
+    isClient && modelUtil.setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);
 
     enableConnect(chart);
 
diff --git a/src/util/decal.ts b/src/util/decal.ts
index 4058db9..dc26506 100644
--- a/src/util/decal.ts
+++ b/src/util/decal.ts
@@ -28,10 +28,11 @@ import ExtensionAPI from '../core/ExtensionAPI';
 import type SVGPainter from 'zrender/src/svg/Painter';
 import { brushSingle } from 'zrender/src/canvas/graphic';
 import {DecalDashArrayX, DecalDashArrayY, InnerDecalObject, DecalObject} from './types';
+import { SVGVNode } from 'zrender/src/svg/core';
 
 const decalMap = new WeakMap<DecalObject, PatternObject>();
 
-const decalCache = new LRU<HTMLCanvasElement | SVGElement>(100);
+const decalCache = new LRU<HTMLCanvasElement | SVGVNode>(100);
 
 const decalKeys = [
     'symbol', 'symbolSize', 'symbolKeepAspect',
@@ -117,7 +118,7 @@ export function createOrUpdatePatternFromDecal(
             cacheKey = keys.join(',') + (isSVG ? '-svg' : '');
             const cache = decalCache.get(cacheKey);
             if (cache) {
-                isSVG ? (pattern as SVGPatternObject).svgElement = cache as SVGElement
+                isSVG ? (pattern as SVGPatternObject).svgElement = cache as SVGVNode
                     : (pattern as ImagePatternObject).image = cache as HTMLCanvasElement;
             }
         }
@@ -129,7 +130,12 @@ export function createOrUpdatePatternFromDecal(
         const lineBlockLengthY = getLineBlockLengthY(dashArrayY);
 
         const canvas = !isSVG && createCanvas();
-        const svgRoot = isSVG && (zr.painter as SVGPainter).createSVGElement('g');
+        const svgRoot: SVGVNode = isSVG && {
+            tag: 'g',
+            attrs: {},
+            key: 'dcl',
+            children: []
+        };
         const pSize = getPatternSize();
         let ctx: CanvasRenderingContext2D;
         if (canvas) {
@@ -284,7 +290,10 @@ export function createOrUpdatePatternFromDecal(
                     decalOpt.symbolKeepAspect
                 );
                 if (isSVG) {
-                    svgRoot.appendChild((zr.painter as SVGPainter).paintOne(symbol));
+                    const symbolVNode = (zr.painter as SVGPainter).renderOneToVNode(symbol);
+                    if (symbolVNode) {
+                        svgRoot.children.push(symbolVNode);
+                    }
                 }
                 else {
                     // Paint to canvas for all other renderers.
diff --git a/src/util/states.ts b/src/util/states.ts
index ac5e01c..3cff899 100644
--- a/src/util/states.ts
+++ b/src/util/states.ts
@@ -216,7 +216,7 @@ function getFromStateStyle(
             // Dont consider the animation to emphasis state.
             && animator.__fromStateTransition.indexOf(toStateName) < 0
             && animator.targetName === 'style') {
-            animator.saveFinalToTarget(fromState, props);
+            animator.saveTo(fromState, props);
         }
     }
     return fromState;
diff --git a/test/node/ssr.js b/test/node/ssr.js
new file mode 100644
index 0000000..6f4673e
--- /dev/null
+++ b/test/node/ssr.js
@@ -0,0 +1,67 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+const echarts = require('../../dist/echarts');
+const chart = echarts.init(null, null, {
+    renderer: 'svg',
+    ssr: true,
+    width: 510,
+    height: 510
+});
+
+chart.setOption({
+    series: [
+        {
+            name: 'Nightingale Chart',
+            type: 'pie',
+            radius: [25, 250],
+            center: ['50%', '50%'],
+            roseType: 'radius',
+            label: {
+                show: false,
+            },
+            itemStyle: {
+                borderColor: 'white',
+                borderWidth: 4,
+            },
+            labelLine: {
+                show: false,
+            },
+            animationType: 'scale',
+            animationDuration: 500,
+            animationEasing: 'cubicOut',
+            animationDelay(idx) {
+                return (1 - idx / 8) * 500;
+            },
+            data: [
+                { value: 40, name: 'rose 1', itemStyle: { borderRadius: [0, 20] } },
+                { value: 32, name: 'rose 2', itemStyle: { borderRadius: [0, 18] } },
+                { value: 28, name: 'rose 3', itemStyle: { borderRadius: [0, 16] } },
+                { value: 24, name: 'rose 4', itemStyle: { borderRadius: [0, 14] } },
+                { value: 19, name: 'rose 5', itemStyle: { borderRadius: [0, 12] } },
+                { value: 15, name: 'rose 6', itemStyle: { borderRadius: [0, 10] } },
+                { value: 12, name: 'rose 7', itemStyle: { borderRadius: [0, 8] } },
+                { value: 10, name: 'rose 8', itemStyle: { borderRadius: [0, 6] } },
+            ],
+        },
+    ],
+});
+// chart.getZr().animation.update(true);
+const str = chart.getZr().painter.renderToString();
+console.log(str);
+chart.dispose();
\ No newline at end of file

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