From f71100ca3dc048dc4d3ec8819e895e051500afdc Mon Sep 17 00:00:00 2001 From: Liam Jones Date: Mon, 6 Jan 2025 12:28:59 +0000 Subject: [PATCH 1/9] fix(env): improve env detection compatibility for Node The React Native test environment *is* Node but sets up a window global: https://github.com/facebook/react-native/blob/7a85b911254dbae9fac4c6d8a862a20c33e0b36a/packages/react-native/Libraries/Core/setUpGlobals.js#L18 Reintroduce the navigator.userAgent check as a fallback for detecting a Node environment --- src/core/env.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/env.ts b/src/core/env.ts index 52e3699ae..ada4862b3 100644 --- a/src/core/env.ts +++ b/src/core/env.ts @@ -37,7 +37,7 @@ else if (typeof document === 'undefined' && typeof self !== 'undefined') { // In worker env.worker = true; } -else if (!env.hasGlobalWindow || 'Deno' in window) { +else if (!env.hasGlobalWindow || 'Deno' in window || (navigator?.userAgent?.indexOf('Node.js') === 0)) { // In node env.node = true; env.svgSupported = true; From d061b030ef8d15e074d76fe2ddb709f3573fd5f5 Mon Sep 17 00:00:00 2001 From: plainheart Date: Mon, 17 Feb 2025 09:18:56 +0800 Subject: [PATCH 2/9] fix(env): improve env detection compatibility for Node --- src/core/env.ts | 54 +++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/core/env.ts b/src/core/env.ts index ada4862b3..45461a9e0 100644 --- a/src/core/env.ts +++ b/src/core/env.ts @@ -37,7 +37,11 @@ else if (typeof document === 'undefined' && typeof self !== 'undefined') { // In worker env.worker = true; } -else if (!env.hasGlobalWindow || 'Deno' in window || (navigator?.userAgent?.indexOf('Node.js') === 0)) { +else if ( + !env.hasGlobalWindow + || 'Deno' in window + || (typeof navigator !== 'undefined' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Node.js') > -1) +) { // In node env.node = true; env.svgSupported = true; @@ -85,29 +89,31 @@ function detect(ua: string, env: Env) { env.touchEventsSupported = 'ontouchstart' in window && !browser.ie && !browser.edge; env.pointerEventsSupported = 'onpointerdown' in window && (browser.edge || (browser.ie && +browser.version >= 11)); - env.domSupported = typeof document !== 'undefined'; - - const style = document.documentElement.style; - - env.transform3dSupported = ( - // IE9 only supports transform 2D - // transform 3D supported since IE10 - // we detect it by whether 'transition' is in style - (browser.ie && 'transition' in style) - // edge - || browser.edge - // webkit - || (('WebKitCSSMatrix' in window) && ('m11' in new WebKitCSSMatrix())) - // gecko-based browsers - || 'MozPerspective' in style - ) // Opera supports CSS transforms after version 12 - && !('OTransition' in style); - - // except IE 6-8 and very old firefox 2-3 & opera 10.1 - // other browsers all support `transform` - env.transformSupported = env.transform3dSupported - // transform 2D is supported in IE9 - || (browser.ie && +browser.version >= 9); + + const domSupported = env.domSupported = typeof document !== 'undefined'; + if (domSupported) { + const style = document.documentElement.style; + + env.transform3dSupported = ( + // IE9 only supports transform 2D + // transform 3D supported since IE10 + // we detect it by whether 'transition' is in style + (browser.ie && 'transition' in style) + // edge + || browser.edge + // webkit + || (('WebKitCSSMatrix' in window) && ('m11' in new WebKitCSSMatrix())) + // gecko-based browsers + || 'MozPerspective' in style + ) // Opera supports CSS transforms after version 12 + && !('OTransition' in style); + + // except IE 6-8 and very old firefox 2-3 & opera 10.1 + // other browsers all support `transform` + env.transformSupported = env.transform3dSupported + // transform 2D is supported in IE9 + || (browser.ie && +browser.version >= 9); + } } From 66310530293411b1551b47a0aec95430b9f32d14 Mon Sep 17 00:00:00 2001 From: plainheart Date: Mon, 17 Feb 2025 09:24:07 +0800 Subject: [PATCH 3/9] style(env): fix lint issue --- src/core/env.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/env.ts b/src/core/env.ts index 45461a9e0..4d8cc99f9 100644 --- a/src/core/env.ts +++ b/src/core/env.ts @@ -40,7 +40,8 @@ else if (typeof document === 'undefined' && typeof self !== 'undefined') { else if ( !env.hasGlobalWindow || 'Deno' in window - || (typeof navigator !== 'undefined' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Node.js') > -1) + || (typeof navigator !== 'undefined' && typeof navigator.userAgent === 'string' + && navigator.userAgent.indexOf('Node.js') > -1) ) { // In node env.node = true; From 6cb315d099899f81ff4c42fc577e01277c9aada5 Mon Sep 17 00:00:00 2001 From: plainheart Date: Thu, 20 Mar 2025 15:00:26 +0800 Subject: [PATCH 4/9] fix(svg): fix `stop-opacity` is not parsed by SVG parser. (https://github.com/apache/echarts/issues/18407#issuecomment-2738836773) --- src/tool/color.ts | 4 +-- src/tool/parseSVG.ts | 13 +++++++- test/parser-svg-stop-opacity.html | 51 +++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 test/parser-svg-stop-opacity.html diff --git a/src/tool/color.ts b/src/tool/color.ts index ae912e3fe..1be9f87a5 100644 --- a/src/tool/color.ts +++ b/src/tool/color.ts @@ -93,7 +93,7 @@ function clampCssFloat(f: number): number { // Clamp to float 0.0 .. 1.0. return f < 0 ? 0 : f > 1 ? 1 : f; } -function parseCssInt(val: string | number): number { // int or percentage. +export function parseCssInt(val: string | number): number { // int or percentage. let str = val as string; if (str.length && str.charAt(str.length - 1) === '%') { return clampCssByte(parseFloat(str) / 100 * 255); @@ -101,7 +101,7 @@ function parseCssInt(val: string | number): number { // int or percentage. return clampCssByte(parseInt(str, 10)); } -function parseCssFloat(val: string | number): number { // float or percentage. +export function parseCssFloat(val: string | number): number { // float or percentage. let str = val as string; if (str.length && str.charAt(str.length - 1) === '%') { return clampCssFloat(parseFloat(str) / 100); diff --git a/src/tool/parseSVG.ts b/src/tool/parseSVG.ts index 0ee2739a6..5d0587042 100644 --- a/src/tool/parseSVG.ts +++ b/src/tool/parseSVG.ts @@ -19,6 +19,7 @@ import RadialGradient, { RadialGradientObject } from '../graphic/RadialGradient' import Gradient, { GradientObject } from '../graphic/Gradient'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import { parseXML } from './parseXML'; +import * as colorTool from './color'; interface SVGParserOption { @@ -627,9 +628,19 @@ function parseGradientColorStops(xmlNode: SVGElement, gradient: GradientObject): // const styleVals = {} as Dictionary; parseInlineStyle(stop, styleVals, styleVals); - const stopColor = styleVals.stopColor + let stopColor = styleVals.stopColor || stop.getAttribute('stop-color') || '#000000'; + const stopOpacity = styleVals.stopOpacity + || stop.getAttribute('stop-opacity'); + if (stopOpacity) { + const rgba = colorTool.parse(stopColor); + const stopColorOpacity = rgba && rgba[3]; + if (stopColorOpacity) { + rgba[3] *= colorTool.parseCssFloat(stopOpacity); + stopColor = colorTool.stringify(rgba, 'rgba'); + } + } gradient.colorStops.push({ offset: offset, diff --git a/test/parser-svg-stop-opacity.html b/test/parser-svg-stop-opacity.html new file mode 100644 index 000000000..70b30a11b --- /dev/null +++ b/test/parser-svg-stop-opacity.html @@ -0,0 +1,51 @@ + + + + + Parse SVG stop-opacity + + + + +
+ ZRender.parseSVG: +
+ Original SVG: +
+
+ + + \ No newline at end of file From be40616874f392e471a993f7a0a704f46a271969 Mon Sep 17 00:00:00 2001 From: Nikita Besperstov Date: Wed, 26 Mar 2025 15:52:48 +0800 Subject: [PATCH 5/9] fix: custom stroke pattern --- src/canvas/graphic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/canvas/graphic.ts b/src/canvas/graphic.ts index fb3a145eb..72d554fbf 100644 --- a/src/canvas/graphic.ts +++ b/src/canvas/graphic.ts @@ -152,7 +152,7 @@ function brushPath(ctx: CanvasRenderingContext2D, el: Path, style: PathStyleProp strokePattern = (dirtyFlag || !el.__canvasStrokePattern) ? createCanvasPattern(ctx, stroke as ImagePatternObject, el) : el.__canvasStrokePattern; - el.__canvasStrokePattern = fillPattern; + el.__canvasStrokePattern = strokePattern; } // Use the gradient or pattern if (hasFillGradient) { From 827f5d0f464ac6327acc874b79463aa67c2b502c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D1=96=D0=B9=20=D0=A8=D0=BE=D0=B2?= =?UTF-8?q?=D0=BA=D0=BE=D1=88=D0=B8=D1=82=D0=BD=D0=B8=D0=B9?= <198119344+enlightened88@users.noreply.github.com> Date: Sat, 17 May 2025 18:26:25 +0300 Subject: [PATCH 6/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 28ace49ae..80677eb59 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ZRender A lightweight graphic library which provides 2d draw for [Apache ECharts](https://github.com/apache/echarts). -[![](https://img.shields.io/github/actions/workflow/status/ecomfe/zrender/ci.yml)]() [![](https://img.shields.io/npm/dw/zrender.svg?label=npm%20downloads&style=flat)](https://www.npmjs.com/package/zrender) ![Commits Since 4.0.0](https://img.shields.io/github/commits-since/ecomfe/zrender/4.0.0.svg?colorB=%234c1&style=flat) +[![](https://img.shields.io/github/actions/workflow/status/ecomfe/zrender/ci.yml)](https://github.com/ecomfe/zrender/actions) [![](https://img.shields.io/npm/dw/zrender.svg?label=npm%20downloads&style=flat)](https://www.npmjs.com/package/zrender) ![Commits Since 4.0.0](https://img.shields.io/github/commits-since/ecomfe/zrender/4.0.0.svg?colorB=%234c1&style=flat) ## Documentation From c1d98165ce5c8dd4a85860631b18f8b8a351871d Mon Sep 17 00:00:00 2001 From: plainheart Date: Thu, 22 May 2025 01:07:47 +0800 Subject: [PATCH 7/9] fix(dom): support `transformLocalCoord` cleanup --- src/core/dom.ts | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/core/dom.ts b/src/core/dom.ts index fd74b89eb..661fa166b 100644 --- a/src/core/dom.ts +++ b/src/core/dom.ts @@ -2,6 +2,7 @@ import env from './env'; import {buildTransformer} from './fourPointsTransform'; import {Dictionary} from './types'; +import { each } from './util'; const EVENT_SAVED_PROP = '___zrEVENTSAVED'; const _calcOut: number[] = []; @@ -44,7 +45,7 @@ type SavedInfo = { * @param {HTMLElement} elTarget The `out` is based on elTarget. * @param {number} inX * @param {number} inY - * @return {boolean} Whether transform successfully. + * @returns Whether transform successfully. If successful, return a cleanup function array, otherwise `undefined`. */ export function transformLocalCoord( out: number[], @@ -53,8 +54,11 @@ export function transformLocalCoord( inX: number, inY: number ) { - return transformCoordWithViewport(_calcOut, elFrom, inX, inY, true) - && transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]); + const elFromCoordTransformCleanup = transformCoordWithViewport(_calcOut, elFrom, inX, inY, true); + return elFromCoordTransformCleanup && [ + elFromCoordTransformCleanup, + transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]) + ]; } /** @@ -67,17 +71,17 @@ export function transformLocalCoord( * Support the case when CSS transform is used on el. * * @param out [inX: number, inY: number] The output. If `inverse: false`, - * it represents "local coord", otherwise "vireport coord". + * it represents "local coord", otherwise "viewport coord". * If can not transform, `out` will not be modified but return `false`. * @param el The "local coord" is based on the `el`, see comment above. * @param inX If `inverse: false`, - * it represents "vireport coord", otherwise "local coord". + * it represents "viewport coord", otherwise "local coord". * @param inY If `inverse: false`, - * it represents "vireport coord", otherwise "local coord". + * it represents "viewport coord", otherwise "local coord". * @param inverse * `true`: from "viewport coord" to "local coord". * `false`: from "local coord" to "viewport coord". - * @return {boolean} Whether transform successfully. + * @returns Whether transform successfully. If successful, return a cleanup function, otherwise `undefined`. */ export function transformCoordWithViewport( out: number[], @@ -87,15 +91,23 @@ export function transformCoordWithViewport( inverse?: boolean ) { if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) { - const saved = (el as any)[EVENT_SAVED_PROP] || ((el as any)[EVENT_SAVED_PROP] = {}); + const saved: SavedInfo = (el as any)[EVENT_SAVED_PROP] || ((el as any)[EVENT_SAVED_PROP] = {}); const markers = prepareCoordMarkers(el, saved); const transformer = preparePointerTransformer(markers, saved, inverse); if (transformer) { transformer(out, inX, inY); - return true; + return function () { + const saved: SavedInfo = (el as any)[EVENT_SAVED_PROP]; + if (saved) { + each(saved.markers, function (marker) { + const parentNode = marker.parentNode; + parentNode && parentNode.removeChild(marker); + }); + } + delete (el as any)[EVENT_SAVED_PROP]; + }; } } - return false; } function prepareCoordMarkers(el: HTMLElement, saved: SavedInfo) { From 0243cf4df65da4552b05880d8aad384e830dac3f Mon Sep 17 00:00:00 2001 From: plainheart Date: Sun, 25 May 2025 01:47:17 +0800 Subject: [PATCH 8/9] fix(dom): support `transformLocalCoord` cleanup --- src/core/dom.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/core/dom.ts b/src/core/dom.ts index 661fa166b..ce3d24318 100644 --- a/src/core/dom.ts +++ b/src/core/dom.ts @@ -54,11 +54,16 @@ export function transformLocalCoord( inX: number, inY: number ) { - const elFromCoordTransformCleanup = transformCoordWithViewport(_calcOut, elFrom, inX, inY, true); - return elFromCoordTransformCleanup && [ - elFromCoordTransformCleanup, - transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]) - ]; + const clearFromElCoordTransform = transformCoordWithViewport(_calcOut, elFrom, inX, inY, true); + if (clearFromElCoordTransform) { + const clearTargetElCoordTransform = transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]); + if (clearTargetElCoordTransform) { + return () => { + clearTargetElCoordTransform(); + clearFromElCoordTransform(); + }; + } + } } /** From 9e0a1740319a19e78eed75c291d2a66d3d284c1e Mon Sep 17 00:00:00 2001 From: plainheart Date: Sat, 31 May 2025 23:24:19 +0800 Subject: [PATCH 9/9] fix(dom): add `transformLocalCoordClear` function to clear markers created by `transformLocalCoord` --- src/core/dom.ts | 55 ++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/core/dom.ts b/src/core/dom.ts index ce3d24318..53fda5e27 100644 --- a/src/core/dom.ts +++ b/src/core/dom.ts @@ -12,6 +12,7 @@ type SavedInfo = { trans?: ReturnType invTrans?: ReturnType srcCoords?: number[] + clearMarkers?: () => void } /** @@ -45,7 +46,7 @@ type SavedInfo = { * @param {HTMLElement} elTarget The `out` is based on elTarget. * @param {number} inX * @param {number} inY - * @returns Whether transform successfully. If successful, return a cleanup function array, otherwise `undefined`. + * @return {boolean} Whether transform successfully. */ export function transformLocalCoord( out: number[], @@ -54,14 +55,22 @@ export function transformLocalCoord( inX: number, inY: number ) { - const clearFromElCoordTransform = transformCoordWithViewport(_calcOut, elFrom, inX, inY, true); - if (clearFromElCoordTransform) { - const clearTargetElCoordTransform = transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]); - if (clearTargetElCoordTransform) { - return () => { - clearTargetElCoordTransform(); - clearFromElCoordTransform(); - }; + return transformCoordWithViewport(_calcOut, elFrom, inX, inY, true) + && transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]); +} + +export function transformLocalCoordClear( + elFrom: HTMLElement, + elTarget: HTMLElement, +) { + elFrom && dealClear(elFrom); + elTarget && dealClear(elTarget); + + function dealClear(el: HTMLElement) { + const saved: SavedInfo = (el as any)[EVENT_SAVED_PROP]; + if (saved) { + saved.clearMarkers && saved.clearMarkers(); + delete (el as any)[EVENT_SAVED_PROP]; } } } @@ -76,17 +85,17 @@ export function transformLocalCoord( * Support the case when CSS transform is used on el. * * @param out [inX: number, inY: number] The output. If `inverse: false`, - * it represents "local coord", otherwise "viewport coord". + * it represents "local coord", otherwise "vireport coord". * If can not transform, `out` will not be modified but return `false`. * @param el The "local coord" is based on the `el`, see comment above. * @param inX If `inverse: false`, - * it represents "viewport coord", otherwise "local coord". + * it represents "vireport coord", otherwise "local coord". * @param inY If `inverse: false`, - * it represents "viewport coord", otherwise "local coord". + * it represents "vireport coord", otherwise "local coord". * @param inverse * `true`: from "viewport coord" to "local coord". * `false`: from "local coord" to "viewport coord". - * @returns Whether transform successfully. If successful, return a cleanup function, otherwise `undefined`. + * @return {boolean} Whether transform successfully. */ export function transformCoordWithViewport( out: number[], @@ -96,23 +105,15 @@ export function transformCoordWithViewport( inverse?: boolean ) { if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) { - const saved: SavedInfo = (el as any)[EVENT_SAVED_PROP] || ((el as any)[EVENT_SAVED_PROP] = {}); + const saved = (el as any)[EVENT_SAVED_PROP] || ((el as any)[EVENT_SAVED_PROP] = {}); const markers = prepareCoordMarkers(el, saved); const transformer = preparePointerTransformer(markers, saved, inverse); if (transformer) { transformer(out, inX, inY); - return function () { - const saved: SavedInfo = (el as any)[EVENT_SAVED_PROP]; - if (saved) { - each(saved.markers, function (marker) { - const parentNode = marker.parentNode; - parentNode && parentNode.removeChild(marker); - }); - } - delete (el as any)[EVENT_SAVED_PROP]; - }; + return true; } } + return false; } function prepareCoordMarkers(el: HTMLElement, saved: SavedInfo) { @@ -151,6 +152,12 @@ function prepareCoordMarkers(el: HTMLElement, saved: SavedInfo) { markers.push(marker); } + saved.clearMarkers = function () { + each(markers, function (marker) { + marker.parentNode && marker.parentNode.removeChild(marker); + }); + }; + return markers; }