diff --git a/build/dev-fast.js b/build/dev-fast.js index 2a1aacb9d4..4ebe775829 100644 --- a/build/dev-fast.js +++ b/build/dev-fast.js @@ -20,14 +20,10 @@ const path = require('path'); const {build} = require('esbuild'); - +const commander = require('commander'); const outFilePath = path.resolve(__dirname, '../dist/echarts.js'); -const umdMark = '// ------------- WRAPPED UMD --------------- //'; -const umdWrapperHead = ` -${umdMark} -(function (root, factory) { - window.__DEV__ = true; +const umdWrapperHead = `(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['exports'], factory); @@ -52,15 +48,20 @@ build({ bundle: true, banner: umdWrapperHead, footer: umdWrapperTail, + define: { + 'process.env.NODE_ENV': '"development"', + '__DEV__': 'true' + }, watch: { async onRebuild(error) { if (error) { console.error('watch build failed:', error) - } else { - console.log('build done') + } + else { + console.log('Bundled with esbuild') } }, }, }).then(async () => { - console.log('build done') + console.log('Bundled with esbuild') }).catch(e => console.error(e.toString())) diff --git a/build/source-release/template/announce-release.tpl b/build/source-release/template/announce-release.tpl index a19a6683df..5030eaecf3 100755 --- a/build/source-release/template/announce-release.tpl +++ b/build/source-release/template/announce-release.tpl @@ -13,11 +13,11 @@ The Apache ECharts team is proud to announce Apache ECharts {{ECHARTS_RELEASE_VE Apache ECharts is a powerful, interactive charting and data visualization library for browser. -Download Links: https://dist.apache.org/repos/dist/release/echarts/{{ECHARTS_RELEASE_VERSION}}/ +Download Links: https://echarts.apache.org/download.html -Release Notes: http://www.apache.org/dist/echarts/{{ECHARTS_RELEASE_VERSION}}/RELEASE_NOTE.txt +Release Notes: https://echarts.apache.org/changelog.html -Website: http://echarts.apache.org/ +Website: https://echarts.apache.org/ ECharts Resources: diff --git a/package-lock.json b/package-lock.json index c319fcf0b7..5d6b8cacf3 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": { @@ -11122,9 +11122,9 @@ } }, "zrender": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.2.1.tgz", - "integrity": "sha512-M3bPGZuyLTNBC6LiNKXJwSCtglMp8XUEqEBG+2MdICDI3d1s500Y4P0CzldQGsqpRVB7fkvf3BKQQRxsEaTlsw==", + "version": "npm:zrender-nightly@5.3.0-dev.20211015", + "resolved": "https://registry.npmjs.org/zrender-nightly/-/zrender-nightly-5.3.0-dev.20211015.tgz", + "integrity": "sha512-BX4rQhpIpOyihCjFknNFLGy1Zv7cSqsQEfR9TMDzPzbDx7LyadH1sChTIvj+1HEw1uMxuDroDTsaCM5C34PwrA==", "requires": { "tslib": "2.3.0" } diff --git a/package.json b/package.json index 319d57e861..be6f7dd7ff 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ }, "dependencies": { "tslib": "2.3.0", - "zrender": "5.2.1" + "zrender": "npm:zrender-nightly@^5.3.0-dev.20211015" }, "devDependencies": { "@babel/code-frame": "7.10.4", @@ -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/chart/heatmap/HeatmapLayer.ts b/src/chart/heatmap/HeatmapLayer.ts index f85130102b..b5506d1e39 100644 --- a/src/chart/heatmap/HeatmapLayer.ts +++ b/src/chart/heatmap/HeatmapLayer.ts @@ -19,7 +19,7 @@ /* global Uint8ClampedArray */ -import * as zrUtil from 'zrender/src/core/util'; +import { platformApi } from 'zrender/src/core/platform'; const GRADIENT_LEVELS = 256; @@ -43,7 +43,7 @@ class HeatmapLayer { }; constructor() { - const canvas = zrUtil.createCanvas(); + const canvas = platformApi.createCanvas(); this.canvas = canvas; } @@ -127,7 +127,7 @@ class HeatmapLayer { * get canvas of a black circle brush used for canvas to draw later */ _getBrush() { - const brushCanvas = this._brushCanvas || (this._brushCanvas = zrUtil.createCanvas()); + const brushCanvas = this._brushCanvas || (this._brushCanvas = platformApi.createCanvas()); // set brush size const r = this.pointSize + this.blurSize; const d = r * 2; diff --git a/src/chart/pie/PieView.ts b/src/chart/pie/PieView.ts index 66c07e8fc1..2b17747804 100644 --- a/src/chart/pie/PieView.ts +++ b/src/chart/pie/PieView.ts @@ -77,7 +77,17 @@ class PiePiece extends graphic.Sector { sector.setShape(sectorShape); const animationType = seriesModel.getShallow('animationType'); - if (animationType === 'scale') { + if (seriesModel.ecModel.ssr) { + // Use scale animation in SSR mode(opacity?) + // Because CSS SVG animation doesn't support very customized shape animation. + graphic.initProps(sector, { + scaleX: 0, + scaleY: 0 + }, seriesModel, { dataIndex: idx, isFrom: true}); + sector.originX = sectorShape.cx; + sector.originY = sectorShape.cy; + } + else if (animationType === 'scale') { sector.shape.r = layout.r0; graphic.initProps(sector, { shape: { diff --git a/src/component/dataZoom/DataZoomModel.ts b/src/component/dataZoom/DataZoomModel.ts index 6ed6ccda0a..48e01cfa6b 100644 --- a/src/component/dataZoom/DataZoomModel.ts +++ b/src/component/dataZoom/DataZoomModel.ts @@ -244,10 +244,6 @@ class DataZoomModel extends Compon private _doInit(inputRawOption: Opts): void { const thisOption = this.option; - // if (!env.canvasSupported) { - // thisOption.realtime = false; - // } - this._setDefaultThrottle(inputRawOption); this._updateRangeUse(inputRawOption); diff --git a/src/component/toolbox/feature/SaveAsImage.ts b/src/component/toolbox/feature/SaveAsImage.ts index c59bb860b4..d6bc36c630 100644 --- a/src/component/toolbox/feature/SaveAsImage.ts +++ b/src/component/toolbox/feature/SaveAsImage.ts @@ -74,6 +74,7 @@ class SaveAsImage extends ToolboxFeature { } // IE or old Edge else { + // @ts-ignore if (window.navigator.msSaveOrOpenBlob || isSvg) { const parts = url.split(','); // data:[][;charset=][;base64], @@ -88,13 +89,14 @@ class SaveAsImage extends ToolboxFeature { // (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 { @@ -143,6 +145,4 @@ class SaveAsImage extends ToolboxFeature { } } -SaveAsImage.prototype.unusable = !env.canvasSupported; - export default SaveAsImage; diff --git a/src/component/tooltip/TooltipHTMLContent.ts b/src/component/tooltip/TooltipHTMLContent.ts index 3e060cb5c0..b324d6adab 100644 --- a/src/component/tooltip/TooltipHTMLContent.ts +++ b/src/component/tooltip/TooltipHTMLContent.ts @@ -189,16 +189,7 @@ function assembleCssText(tooltipModel: Model, enableTransition?: enableTransition && transitionDuration && cssText.push(assembleTransition(transitionDuration, onlyFade)); if (backgroundColor) { - if (env.canvasSupported) { - cssText.push('background-color:' + backgroundColor); - } - else { - // for ie - cssText.push( - 'background-color:#' + toHex(backgroundColor) - ); - cssText.push('filter:alpha(opacity=70)'); - } + cssText.push('background-color:' + backgroundColor); } // Border style diff --git a/src/component/tooltip/TooltipView.ts b/src/component/tooltip/TooltipView.ts index e83ca60fde..1523a182e6 100644 --- a/src/component/tooltip/TooltipView.ts +++ b/src/component/tooltip/TooltipView.ts @@ -171,7 +171,7 @@ class TooltipView extends ComponentView { private _updatePosition: ReturnType | TooltipView['_doUpdatePosition']; init(ecModel: GlobalModel, api: ExtensionAPI) { - if (env.node) { + if (env.node || !api.getDom()) { return; } @@ -191,7 +191,7 @@ class TooltipView extends ComponentView { ecModel: GlobalModel, api: ExtensionAPI ) { - if (env.node) { + if (env.node || !api.getDom()) { return; } @@ -301,7 +301,7 @@ class TooltipView extends ComponentView { api: ExtensionAPI, payload: ShowTipPayload ) { - if (payload.from === this.uid || env.node) { + if (payload.from === this.uid || env.node || !api.getDom()) { return; } @@ -1013,7 +1013,7 @@ class TooltipView extends ComponentView { } dispose(ecModel: GlobalModel, api: ExtensionAPI) { - if (env.node) { + if (env.node || !api.getDom()) { return; } this._tooltipContent.dispose(); diff --git a/src/component/visualMap/VisualMapModel.ts b/src/component/visualMap/VisualMapModel.ts index 8534b29708..21116003a5 100644 --- a/src/component/visualMap/VisualMapModel.ts +++ b/src/component/visualMap/VisualMapModel.ts @@ -202,13 +202,6 @@ class VisualMapModel extends Com optionUpdated(newOption: Opts, isInit?: boolean) { const thisOption = this.option; - // FIXME - // necessary? - // Disable realtime view update if canvas is not supported. - if (!env.canvasSupported) { - thisOption.realtime = false; - } - !isInit && visualSolution.replaceVisualOption( thisOption, newOption, this.replacableOptionKeys ); diff --git a/src/core/ExtensionAPI.ts b/src/core/ExtensionAPI.ts index c52fe7d8df..98f15e5cab 100644 --- a/src/core/ExtensionAPI.ts +++ b/src/core/ExtensionAPI.ts @@ -35,6 +35,7 @@ const availableMethods: (keyof EChartsType)[] = [ 'getHeight', 'getDevicePixelRatio', 'dispatchAction', + 'isSSR', 'isDisposed', 'on', 'off', diff --git a/src/core/echarts.ts b/src/core/echarts.ts index 8a61c985d3..c0aef93c0e 100644 --- a/src/core/echarts.ts +++ b/src/core/echarts.ts @@ -26,7 +26,6 @@ import { bind, clone, setAsPrimitive, - createCanvas, extend, HashMap, createHashMap, @@ -34,10 +33,8 @@ import { defaults, isDom, isArray, - $override, noop } from 'zrender/src/core/util'; -import * as colorTool from 'zrender/src/tool/color'; import env from 'zrender/src/core/env'; import timsort from 'zrender/src/core/timsort'; import Eventful, { EventCallbackSingleParam } from 'zrender/src/core/Eventful'; @@ -114,7 +111,7 @@ import Displayable from 'zrender/src/graphic/Displayable'; import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable'; import { seriesSymbolTask, dataSymbolTask } from '../visual/symbol'; import { getVisualFromData, getItemVisualFromData } from '../visual/helper'; -import { deprecateLog } from '../util/log'; +import { deprecateLog, deprecateReplaceLog } from '../util/log'; import { handleLegacySelectEvents } from '../legacy/dataSelectAction'; import { registerExternalTransform } from '../data/helper/transform'; @@ -132,6 +129,7 @@ import lifecycle, { UpdateLifecycleParams, UpdateLifecycleTransitionOpt } from './lifecycle'; +import { platformApi, setPlatformAPI } from 'zrender/src/core/platform'; declare let global: any; @@ -327,6 +325,7 @@ type EChartsInitOpts = { renderer?: RendererType, devicePixelRatio?: number, useDirtyRect?: boolean, + ssr?: boolean, width?: number, height?: number }; @@ -343,6 +342,8 @@ class ECharts extends Eventful { */ group: string; + private _ssr: boolean; + private _zr: zrender.ZRenderType; private _dom: HTMLElement; @@ -429,8 +430,10 @@ class ECharts extends Eventful { 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); @@ -560,6 +563,10 @@ class ECharts extends Eventful { return this._zr; } + isSSR(): boolean { + return this._ssr; + } + /** * Usage: * chart.setOption(option, notMerge, lazyUpdate); @@ -606,6 +613,7 @@ class ECharts extends Eventful { const theme = this._theme; const ecModel = this._model = new GlobalModel(); ecModel.scheduler = this._scheduler; + ecModel.ssr = this._ssr; ecModel.init(null, null, null, theme, this._locale, optionManager); } @@ -634,7 +642,10 @@ class ECharts extends Eventful { // 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; @@ -645,10 +656,10 @@ class ECharts extends Eventful { } /** - * @DEPRECATED + * @deprecated */ private setTheme(): void { - console.error('ECharts#setTheme() is DEPRECATED in ECharts 3.0'); + deprecateLog('ECharts#setTheme() is DEPRECATED in ECharts 3.0'); } // We don't want developers to use getModel directly. @@ -676,21 +687,45 @@ class ECharts extends Eventful { /** * Get canvas which has all thing rendered + * @deprecated Use renderToCanvas instead. */ - getRenderedCanvas(opts?: { + getRenderedCanvas(opts?: any): HTMLCanvasElement { + if (__DEV__) { + deprecateReplaceLog('getRenderedCanvas', 'renderToCanvas'); + } + return this.renderToCanvas(opts); + } + + renderToCanvas(opts?: { backgroundColor?: ZRColor pixelRatio?: number }): HTMLCanvasElement { - if (!env.canvasSupported) { - return; - } opts = opts || {}; - return (this._zr.painter as CanvasPainter).getRenderedCanvas({ + const painter = this._zr.painter; + if (__DEV__) { + if (painter.type !== 'canvas') { + throw new Error('renderToCanvas can only been used in the canvas renderer.'); + } + } + return (painter as CanvasPainter).getRenderedCanvas({ backgroundColor: (opts.backgroundColor || this._model.get('backgroundColor')) as ColorString, pixelRatio: opts.pixelRatio || this.getDevicePixelRatio() }); } + renderToSVGString(): string { + const painter = this._zr.painter; + if (__DEV__) { + if (!this._ssr) { + throw new Error('renderToSVGString can only been used in SSR mode.'); + } + if (painter.type !== 'svg') { + throw new Error('renderToSVGString can only been used in the svg renderer.'); + } + } + return painter.renderToString() as any; + } + /** * Get svg data url */ @@ -742,7 +777,7 @@ class ECharts extends Eventful { const url = this._zr.painter.getType() === 'svg' ? this.getSvgDataURL() - : this.getRenderedCanvas(opts).toDataURL( + : this.renderToCanvas(opts).toDataURL( 'image/' + (opts && opts.type || 'png') ); @@ -766,9 +801,6 @@ class ECharts extends Eventful { return; } - if (!env.canvasSupported) { - return; - } const isSvg = opts.type === 'svg'; const groupId = this.group; const mathMin = Math.min; @@ -786,7 +818,7 @@ class ECharts extends Eventful { if (chart.group === groupId) { const canvas = isSvg ? (chart.getZr().painter as SVGPainter).getSvgDom().innerHTML - : chart.getRenderedCanvas(clone(opts)); + : chart.renderToCanvas(clone(opts)); const boundingRect = chart.getDom().getBoundingClientRect(); left = mathMin(boundingRect.left, left); top = mathMin(boundingRect.top, top); @@ -806,7 +838,7 @@ class ECharts extends Eventful { bottom *= dpr; const width = right - left; const height = bottom - top; - const targetCanvas = createCanvas(); + const targetCanvas = platformApi.createCanvas(); const zr = zrender.init(targetCanvas, { renderer: isSvg ? 'svg' : 'canvas' }); @@ -1121,7 +1153,10 @@ class ECharts extends Eventful { } 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; @@ -1631,24 +1666,14 @@ class ECharts extends Eventful { render(this, ecModel, api, payload, updateParams); // Set background - let backgroundColor = ecModel.get('backgroundColor') || 'transparent'; + const backgroundColor = ecModel.get('backgroundColor') || 'transparent'; const darkMode = ecModel.get('darkMode'); - // In IE8 - if (!env.canvasSupported) { - const colorArr = colorTool.parse(backgroundColor as ColorString); - backgroundColor = colorTool.stringify(colorArr, 'rgb'); - if (colorArr[3] === 0) { - backgroundColor = 'transparent'; - } - } - else { - zr.setBackgroundColor(backgroundColor); + zr.setBackgroundColor(backgroundColor); - // Force set dark mode. - if (darkMode != null && darkMode !== 'auto') { - zr.setDarkMode(darkMode); - } + // Force set dark mode. + if (darkMode != null && darkMode !== 'auto') { + zr.setDarkMode(darkMode); } lifecycle.trigger('afterupdate', ecModel, api); @@ -2199,11 +2224,6 @@ class ECharts extends Eventful { */ function updateBlend(seriesModel: SeriesModel, chartView: ChartView): void { const blendMode = seriesModel.get('blendMode') || null; - if (__DEV__) { - if (!env.canvasSupported && blendMode && blendMode !== 'source-over') { - console.warn('Only canvas support blendMode'); - } - } chartView.group.traverse(function (el: Displayable) { // FIXME marker and other components if (!el.isGroup) { @@ -2546,32 +2566,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 +2602,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); @@ -2858,7 +2881,8 @@ export function registerLoading( * But in node environment canvas may be created by node-canvas. * So we need to specify how to create a canvas instead of using document.createElement('canvas') * - * Be careful of using it in the browser. + * + * @deprecated use setPlatformAPI({ createCanvas }) instead. * * @example * let Canvas = require('canvas'); @@ -2869,7 +2893,12 @@ export function registerLoading( * }); */ export function setCanvasCreator(creator: () => HTMLCanvasElement): void { - $override('createCanvas', creator); + if (__DEV__) { + deprecateLog('setCanvasCreator is deprecated. Use setPlatformAPI({ createCanvas }) instead.'); + } + setPlatformAPI({ + createCanvas: creator + }); } /** diff --git a/src/export/api.ts b/src/export/api.ts index d7f7cf0e33..4b777a2370 100644 --- a/src/export/api.ts +++ b/src/export/api.ts @@ -39,6 +39,8 @@ export * as helper from './api/helper'; export {use} from '../extension'; +export {setPlatformAPI} from 'zrender/src/core/platform'; + //////////////// Helper Methods ///////////////////// export {default as parseGeoJSON} from '../coord/geo/parseGeoJson'; export {default as parseGeoJson} from '../coord/geo/parseGeoJson'; diff --git a/src/model/Global.ts b/src/model/Global.ts index 312a067c09..e8515633f4 100644 --- a/src/model/Global.ts +++ b/src/model/Global.ts @@ -192,6 +192,9 @@ class GlobalModel extends Model { // Injectable properties: scheduler: Scheduler; + // If in ssr mode. + // TODO put in a better place? + ssr: boolean; init( option: ECBasicOption, diff --git a/src/model/Series.ts b/src/model/Series.ts index a11bc5c6de..5fbce936d6 100644 --- a/src/model/Series.ts +++ b/src/model/Series.ts @@ -458,7 +458,10 @@ class SeriesModel extends ComponentMode } isAnimationEnabled(): boolean { - if (env.node) { + const ecModel = this.ecModel; + // Disable animation if using echarts in node but not give ssr flag. + // In ssr mode, renderToString will generate svg with css animation. + if (env.node && !(ecModel && ecModel.ssr)) { return false; } let animationEnabled = this.getShallow('animation'); diff --git a/src/util/clazz.ts b/src/util/clazz.ts index e9f29b2c53..13532ba5e3 100644 --- a/src/util/clazz.ts +++ b/src/util/clazz.ts @@ -91,40 +91,36 @@ export function enableClassExtend(rootClz: ExtendableConstructor, mandatoryMetho } const superClass = this; - // For backward compat, we both support ts class inheritance and this - // "extend" approach. - // The constructor should keep the same behavior as ts class inheritance: - // If this constructor/$constructor is not declared, auto invoke the super - // constructor. - // If this constructor/$constructor is declared, it is responsible for - // calling the super constructor. - function ExtendedClass(this: any, ...args: any[]) { - if (!proto.$constructor) { - - if (!isESClass(superClass)) { - // Will throw error if superClass is an es6 native class. - superClass.apply(this, arguments); - } - else { - const ins = zrUtil.createObject( - // @ts-ignore - ExtendedClass.prototype, new superClass(...args) - ); - return ins; + let ExtendedClass: any; + + if (isESClass(superClass)) { + ExtendedClass = class extends superClass { + constructor() { + super(...arguments as any); } - } - else { - proto.$constructor.apply(this, arguments); - } + }; + } + else { + // For backward compat, we both support ts class inheritance and this + // "extend" approach. + // The constructor should keep the same behavior as ts class inheritance: + // If this constructor/$constructor is not declared, auto invoke the super + // constructor. + // If this constructor/$constructor is declared, it is responsible for + // calling the super constructor. + ExtendedClass = function (this: any) { + (proto.$constructor || superClass).apply(this, arguments); + }; + + zrUtil.inherits(ExtendedClass, this); } - ExtendedClass[IS_EXTENDED_CLASS] = true; zrUtil.extend(ExtendedClass.prototype, proto); + ExtendedClass[IS_EXTENDED_CLASS] = true; ExtendedClass.extend = this.extend; ExtendedClass.superCall = superCall; ExtendedClass.superApply = superApply; - zrUtil.inherits(ExtendedClass, this); ExtendedClass.superClass = superClass; return ExtendedClass as unknown as ExtendableConstructor; diff --git a/src/util/decal.ts b/src/util/decal.ts index 4058db9098..632660caa3 100644 --- a/src/util/decal.ts +++ b/src/util/decal.ts @@ -21,17 +21,19 @@ import WeakMap from 'zrender/src/core/WeakMap'; import { ImagePatternObject, PatternObject, SVGPatternObject } from 'zrender/src/graphic/Pattern'; import LRU from 'zrender/src/core/LRU'; -import {defaults, createCanvas, map, isArray} from 'zrender/src/core/util'; +import {defaults, map, isArray} from 'zrender/src/core/util'; import {getLeastCommonMultiple} from './number'; import {createSymbol} from './symbol'; 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'; +import { platformApi } from 'zrender/src/core/platform'; const decalMap = new WeakMap(); -const decalCache = new LRU(100); +const decalCache = new LRU(100); const decalKeys = [ 'symbol', 'symbolSize', 'symbolKeepAspect', @@ -117,7 +119,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; } } @@ -128,8 +130,13 @@ export function createOrUpdatePatternFromDecal( const lineBlockLengthsX = getLineBlockLengthX(dashArrayX); const lineBlockLengthY = getLineBlockLengthY(dashArrayY); - const canvas = !isSVG && createCanvas(); - const svgRoot = isSVG && (zr.painter as SVGPainter).createSVGElement('g'); + const canvas = !isSVG && platformApi.createCanvas(); + const svgRoot: SVGVNode = isSVG && { + tag: 'g', + attrs: {}, + key: 'dcl', + children: [] + }; const pSize = getPatternSize(); let ctx: CanvasRenderingContext2D; if (canvas) { @@ -284,7 +291,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 ac5e01c890..3cff89901f 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/src/view/Chart.ts b/src/view/Chart.ts index 8f32838f3f..b31253b7b3 100644 --- a/src/view/Chart.ts +++ b/src/view/Chart.ts @@ -146,7 +146,11 @@ class ChartView { init(ecModel: GlobalModel, api: ExtensionAPI): void {} - render(seriesModel: SeriesModel, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload): void {} + render(seriesModel: SeriesModel, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload): void { + if (__DEV__) { + throw new Error('render method must been implemented'); + } + } /** * Highlight series or specified data item. diff --git a/test/node/ssr.js b/test/node/ssr.js new file mode 100644 index 0000000000..19312e4080 --- /dev/null +++ b/test/node/ssr.js @@ -0,0 +1,66 @@ +/* +* 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] } }, + ], + }, + ], +}); +const str = chart.renderToString(); +console.log(str); +chart.dispose(); \ No newline at end of file diff --git a/test/runTest/actions/__meta__.json b/test/runTest/actions/__meta__.json index 37aea6b857..bc8bb8dce0 100644 --- a/test/runTest/actions/__meta__.json +++ b/test/runTest/actions/__meta__.json @@ -68,7 +68,7 @@ "dataZoom-axis-type": 3, "dataZoom-clip": 3, "dataZoom-extreme": 1, - "dataZoom-rainfall": 2, + "dataZoom-rainfall": 1, "dataZoom-rainfall-connect": 1, "dataZoom-rainfall-inside": 1, "dataZoom-scatter-category": 2, diff --git a/test/runTest/actions/dataZoom-rainfall.json b/test/runTest/actions/dataZoom-rainfall.json index 7e3dddf9db..50f75714de 100644 --- a/test/runTest/actions/dataZoom-rainfall.json +++ b/test/runTest/actions/dataZoom-rainfall.json @@ -1 +1 @@ -[{"name":"Action 1","ops":[{"type":"mousemove","time":248,"x":678,"y":575},{"type":"mousemove","time":448,"x":680,"y":575},{"type":"mousedown","time":458,"x":680,"y":575},{"type":"mousemove","time":665,"x":597,"y":579},{"type":"mousemove","time":866,"x":408,"y":567},{"type":"mouseup","time":1065,"x":389,"y":566},{"time":1066,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":1169,"x":373,"y":570},{"type":"mousemove","time":1369,"x":116,"y":582},{"type":"mousemove","time":1581,"x":102,"y":584},{"type":"mousemove","time":1782,"x":90,"y":580},{"type":"mousemove","time":1982,"x":83,"y":580},{"type":"mousedown","time":2033,"x":83,"y":580},{"type":"mousemove","time":2192,"x":124,"y":580},{"type":"mousemove","time":2400,"x":230,"y":583},{"type":"mouseup","time":2588,"x":246,"y":583},{"time":2589,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":2655,"x":246,"y":582},{"type":"mousemove","time":2856,"x":737,"y":247},{"type":"mousemove","time":3240,"x":798,"y":55},{"type":"mousemove","time":3440,"x":787,"y":44},{"type":"mousemove","time":3650,"x":781,"y":60},{"type":"mousedown","time":3863,"x":781,"y":60},{"type":"mousemove","time":4008,"x":781,"y":62},{"type":"mousemove","time":4211,"x":777,"y":192},{"type":"mousemove","time":4411,"x":779,"y":224},{"type":"mouseup","time":4500,"x":779,"y":224},{"time":4501,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":4611,"x":779,"y":315},{"type":"mousemove","time":4811,"x":778,"y":491},{"type":"mousemove","time":5018,"x":774,"y":547},{"type":"mousemove","time":5218,"x":776,"y":543},{"type":"mousemove","time":5427,"x":776,"y":536},{"type":"mousemove","time":5627,"x":776,"y":530},{"type":"mousedown","time":5769,"x":776,"y":530},{"type":"mousemove","time":5837,"x":776,"y":530},{"type":"mousemove","time":6065,"x":781,"y":400},{"type":"mousemove","time":6277,"x":783,"y":389},{"type":"mouseup","time":6345,"x":783,"y":389},{"time":6346,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":6479,"x":779,"y":387},{"type":"mousemove","time":6680,"x":2,"y":30},{"type":"mousemove","time":6879,"x":1,"y":32},{"type":"mousemove","time":7079,"x":8,"y":44},{"type":"mousemove","time":7306,"x":19,"y":59},{"type":"mousedown","time":7430,"x":19,"y":59},{"type":"mouseup","time":7703,"x":19,"y":59},{"time":7704,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":8087,"x":20,"y":59},{"type":"mousemove","time":8288,"x":24,"y":60},{"type":"mousedown","time":8343,"x":24,"y":60},{"type":"mousemove","time":8423,"x":24,"y":60},{"type":"mousemove","time":8626,"x":25,"y":162},{"type":"mousemove","time":8850,"x":36,"y":243},{"type":"mouseup","time":8898,"x":36,"y":243},{"time":8899,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":9058,"x":24,"y":347},{"type":"mousemove","time":9258,"x":26,"y":465},{"type":"mousemove","time":9458,"x":27,"y":509},{"type":"mousemove","time":9658,"x":28,"y":520},{"type":"mousemove","time":9867,"x":25,"y":544},{"type":"mousemove","time":10082,"x":23,"y":538},{"type":"mousemove","time":10283,"x":23,"y":532},{"type":"mousedown","time":10382,"x":23,"y":532},{"type":"mousemove","time":10483,"x":23,"y":531},{"type":"mousemove","time":10697,"x":44,"y":401},{"type":"mousemove","time":10899,"x":44,"y":401},{"type":"mouseup","time":10938,"x":44,"y":401},{"time":10939,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":11155,"x":43,"y":400},{"type":"mousemove","time":11355,"x":15,"y":182},{"type":"mousemove","time":11558,"x":14,"y":162},{"type":"mousemove","time":11759,"x":24,"y":145},{"type":"mousedown","time":11904,"x":23,"y":143},{"type":"mousemove","time":11973,"x":19,"y":189},{"type":"mousemove","time":12174,"x":18,"y":214},{"type":"mouseup","time":12219,"x":18,"y":214},{"time":12220,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":12375,"x":72,"y":266},{"type":"mousemove","time":12590,"x":328,"y":499},{"type":"mousemove","time":12796,"x":316,"y":540},{"type":"mousemove","time":13002,"x":318,"y":574},{"type":"mousedown","time":13125,"x":318,"y":574},{"type":"mousemove","time":13155,"x":318,"y":574},{"type":"mousemove","time":13373,"x":450,"y":576},{"type":"mousemove","time":13579,"x":575,"y":586},{"type":"mousemove","time":13710,"x":575,"y":586},{"type":"mouseup","time":13744,"x":575,"y":586},{"time":13745,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":13919,"x":631,"y":504},{"type":"mousemove","time":14120,"x":770,"y":257},{"type":"mousemove","time":14328,"x":784,"y":154},{"type":"mousedown","time":14512,"x":784,"y":141},{"type":"mousemove","time":14528,"x":784,"y":142},{"type":"mousemove","time":14733,"x":770,"y":291},{"type":"mousemove","time":14933,"x":772,"y":300},{"type":"mouseup","time":15061,"x":772,"y":300},{"time":15062,"delay":400,"type":"screenshot-auto"}],"scrollY":0,"scrollX":0,"timestamp":1603889925271},{"name":"Action 2","ops":[{"type":"mousedown","time":255,"x":402,"y":561},{"type":"mousemove","time":360,"x":401,"y":561},{"type":"mousemove","time":579,"x":257,"y":554},{"type":"mousemove","time":780,"x":254,"y":554},{"type":"mouseup","time":795,"x":254,"y":554},{"time":796,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":854,"x":253,"y":554},{"type":"mousemove","time":1057,"x":36,"y":332},{"type":"mousemove","time":1341,"x":2,"y":158},{"type":"mousemove","time":1541,"x":88,"y":143},{"type":"mousemove","time":1741,"x":70,"y":162},{"type":"mousemove","time":1941,"x":57,"y":165},{"type":"mousemove","time":2141,"x":46,"y":167},{"type":"mousedown","time":2374,"x":44,"y":168},{"type":"mousemove","time":2384,"x":44,"y":168},{"type":"mousemove","time":2413,"x":44,"y":168},{"type":"mousemove","time":2623,"x":26,"y":373},{"type":"mousemove","time":2825,"x":26,"y":398},{"type":"mouseup","time":2907,"x":26,"y":398},{"time":2908,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":2979,"x":26,"y":398},{"type":"mousemove","time":3195,"x":520,"y":243},{"type":"mousemove","time":3396,"x":661,"y":217},{"type":"mousemove","time":3601,"x":755,"y":194},{"type":"mousemove","time":3802,"x":769,"y":185},{"type":"mousemove","time":4009,"x":787,"y":178},{"type":"mousemove","time":4209,"x":788,"y":179},{"type":"mousedown","time":4310,"x":788,"y":179},{"type":"mousemove","time":4421,"x":790,"y":203},{"type":"mousemove","time":4636,"x":791,"y":366},{"type":"mousemove","time":4851,"x":791,"y":378},{"type":"mouseup","time":4869,"x":791,"y":378},{"time":4870,"delay":400,"type":"screenshot-auto"}],"scrollY":0,"scrollX":0,"timestamp":1603889951011}] \ No newline at end of file +[{"name":"Action 1","ops":[{"type":"mousedown","time":363,"x":778,"y":64},{"type":"mousemove","time":504,"x":778,"y":65},{"type":"mousemove","time":706,"x":778,"y":123},{"type":"mousemove","time":910,"x":789,"y":189},{"type":"mousemove","time":1113,"x":794,"y":242},{"type":"mousemove","time":1315,"x":794,"y":254},{"type":"mouseup","time":1348,"x":794,"y":254},{"time":1349,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":1520,"x":777,"y":406},{"type":"mousemove","time":1721,"x":774,"y":465},{"type":"mousemove","time":1921,"x":771,"y":519},{"type":"mousemove","time":2121,"x":775,"y":522},{"type":"mousemove","time":2322,"x":777,"y":534},{"type":"mousemove","time":2525,"x":777,"y":536},{"type":"mousemove","time":2737,"x":776,"y":530},{"type":"mousedown","time":2786,"x":776,"y":530},{"type":"mousemove","time":2938,"x":776,"y":530},{"type":"mousemove","time":3146,"x":779,"y":433},{"type":"mousemove","time":3358,"x":776,"y":338},{"type":"mousemove","time":3558,"x":777,"y":335},{"type":"mouseup","time":3651,"x":777,"y":335},{"time":3652,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":3771,"x":779,"y":322},{"type":"mousemove","time":3971,"x":779,"y":304},{"type":"mousedown","time":4059,"x":779,"y":301},{"type":"mousemove","time":4176,"x":779,"y":301},{"type":"mousemove","time":4388,"x":779,"y":296},{"type":"mousemove","time":4588,"x":779,"y":224},{"type":"mousemove","time":4788,"x":777,"y":189},{"type":"mousemove","time":4988,"x":776,"y":173},{"type":"mouseup","time":5136,"x":776,"y":172},{"time":5137,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":5191,"x":776,"y":172},{"type":"mousemove","time":5404,"x":788,"y":181},{"type":"mousemove","time":5605,"x":788,"y":181},{"type":"mousedown","time":5632,"x":788,"y":181},{"type":"mousemove","time":5811,"x":784,"y":223},{"type":"mousemove","time":6018,"x":785,"y":328},{"type":"mousemove","time":6225,"x":789,"y":354},{"type":"mouseup","time":6298,"x":789,"y":355},{"time":6299,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":6439,"x":669,"y":562},{"type":"mousemove","time":6639,"x":665,"y":595},{"type":"mousemove","time":6842,"x":677,"y":580},{"type":"mousemove","time":7059,"x":677,"y":579},{"type":"mousemove","time":7071,"x":678,"y":579},{"type":"mousemove","time":7275,"x":682,"y":578},{"type":"mousedown","time":7420,"x":682,"y":578},{"type":"mousemove","time":7488,"x":682,"y":578},{"type":"mousemove","time":7688,"x":591,"y":592},{"type":"mousemove","time":7889,"x":375,"y":586},{"type":"mousemove","time":8091,"x":363,"y":587},{"type":"mouseup","time":8209,"x":361,"y":587},{"time":8210,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":8305,"x":294,"y":585},{"type":"mousemove","time":8505,"x":162,"y":572},{"type":"mousemove","time":8705,"x":141,"y":573},{"type":"mousemove","time":8905,"x":77,"y":585},{"type":"mousemove","time":9111,"x":76,"y":585},{"type":"mousemove","time":9122,"x":77,"y":584},{"type":"mousemove","time":9326,"x":84,"y":578},{"type":"mousedown","time":9413,"x":84,"y":578},{"type":"mousemove","time":9455,"x":85,"y":578},{"type":"mousemove","time":9655,"x":172,"y":574},{"type":"mousemove","time":9855,"x":278,"y":582},{"type":"mousemove","time":10059,"x":279,"y":582},{"type":"mouseup","time":10076,"x":279,"y":582},{"time":10077,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":10272,"x":303,"y":572},{"type":"mousemove","time":10476,"x":310,"y":567},{"type":"mousedown","time":10581,"x":310,"y":567},{"type":"mousemove","time":10688,"x":310,"y":567},{"type":"mousemove","time":10888,"x":210,"y":565},{"type":"mousemove","time":11092,"x":148,"y":560},{"type":"mouseup","time":11281,"x":144,"y":560},{"time":11282,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":11309,"x":144,"y":560},{"type":"mousemove","time":11521,"x":28,"y":518},{"type":"mousemove","time":11722,"x":28,"y":518},{"type":"mousemove","time":11923,"x":28,"y":524},{"type":"mousemove","time":12126,"x":24,"y":535},{"type":"mousemove","time":12322,"x":24,"y":535},{"type":"mousemove","time":12527,"x":25,"y":533},{"type":"mousedown","time":12677,"x":25,"y":533},{"type":"mousemove","time":12743,"x":25,"y":533},{"type":"mousemove","time":12788,"x":25,"y":531},{"type":"mousemove","time":12993,"x":41,"y":385},{"type":"mousemove","time":13193,"x":44,"y":338},{"type":"mousemove","time":13393,"x":44,"y":332},{"type":"mouseup","time":13410,"x":44,"y":332},{"time":13411,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":13605,"x":36,"y":199},{"type":"mousemove","time":13805,"x":40,"y":116},{"type":"mousemove","time":14005,"x":39,"y":77},{"type":"mousemove","time":14205,"x":37,"y":72},{"type":"mousemove","time":14410,"x":33,"y":63},{"type":"mousedown","time":14517,"x":33,"y":63},{"type":"mousemove","time":14622,"x":33,"y":65},{"type":"mousemove","time":14822,"x":27,"y":176},{"type":"mousemove","time":15022,"x":20,"y":219},{"type":"mousemove","time":15222,"x":20,"y":230},{"type":"mouseup","time":15252,"x":20,"y":230},{"time":15253,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":15427,"x":22,"y":243},{"type":"mousemove","time":15643,"x":32,"y":252},{"type":"mousemove","time":15860,"x":38,"y":254},{"type":"mousedown","time":15961,"x":38,"y":254},{"type":"mousemove","time":16072,"x":28,"y":291},{"type":"mousemove","time":16272,"x":31,"y":382},{"type":"mousemove","time":16472,"x":35,"y":415},{"type":"mousemove","time":16677,"x":36,"y":420},{"type":"mouseup","time":16883,"x":36,"y":420},{"time":16884,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":16971,"x":36,"y":419},{"type":"mousemove","time":17171,"x":306,"y":79},{"type":"mousemove","time":17373,"x":348,"y":15},{"type":"mousemove","time":17577,"x":362,"y":1},{"type":"mousemove","time":17739,"x":362,"y":1},{"type":"mousemove","time":17939,"x":371,"y":8},{"type":"mousedown","time":18095,"x":373,"y":9},{"type":"mousemove","time":18143,"x":373,"y":9},{"type":"mouseup","time":18211,"x":373,"y":9},{"time":18212,"delay":400,"type":"screenshot-auto"},{"type":"mousedown","time":18704,"x":373,"y":9},{"type":"mouseup","time":18797,"x":373,"y":9},{"time":18798,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":18889,"x":373,"y":9},{"type":"mousemove","time":19089,"x":401,"y":10},{"type":"mousemove","time":19289,"x":455,"y":12},{"type":"mousedown","time":19466,"x":458,"y":12},{"type":"mousemove","time":19493,"x":458,"y":12},{"type":"mouseup","time":19561,"x":458,"y":12},{"time":19562,"delay":400,"type":"screenshot-auto"},{"type":"mousedown","time":20062,"x":458,"y":12},{"type":"mouseup","time":20168,"x":458,"y":12},{"time":20169,"delay":400,"type":"screenshot-auto"}],"scrollY":0,"scrollX":0,"timestamp":1634189291998}] \ No newline at end of file