From 53a44fb3bd73c5279ed9699f2dafefaedefae209 Mon Sep 17 00:00:00 2001 From: Ovilia Date: Tue, 14 Mar 2023 18:12:38 +0800 Subject: [PATCH 1/5] WIP(ssr): add emphasis style in ssr css apache/echarts#18334 --- src/Element.ts | 5 +++ src/svg/Painter.ts | 10 ++++- src/svg/core.ts | 10 +++++ src/svg/cssEmphasis.ts | 71 +++++++++++++++++++++++++++++++ src/svg/graphic.ts | 15 ++++++- test/svg-meta-data.html | 92 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 src/svg/cssEmphasis.ts create mode 100644 test/svg-meta-data.html diff --git a/src/Element.ts b/src/Element.ts index eb87cc5f9..ec1211ac1 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -368,6 +368,11 @@ class Element { */ __inHover: boolean + /** + * Any information to be binded on the element when rendering. + */ + __metaData: Record + /** * path to clip the elements and its children, if it is a group. * @see http://www.w3.org/TR/2dcontext/#clipping-region diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index fc85ed939..59f0ab3a9 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -128,6 +128,7 @@ class SVGPainter implements PainterBase { willUpdate?: boolean compress?: boolean, useViewBox?: boolean + emphasis?: boolean }) { opts = opts || {}; @@ -140,6 +141,7 @@ class SVGPainter implements PainterBase { scope.animation = opts.animation; scope.willUpdate = opts.willUpdate; scope.compress = opts.compress; + scope.emphasis = opts.emphasis; const children: SVGVNode[] = []; @@ -173,7 +175,12 @@ class SVGPainter implements PainterBase { * If add css animation. * @default true */ - cssAnimation?: boolean + cssAnimation?: boolean, + /** + * If add css emphasis. + * @default true + */ + cssEmphasis?: boolean, /** * If use viewBox * @default true @@ -183,6 +190,7 @@ class SVGPainter implements PainterBase { opts = opts || {}; return vNodeToString(this.renderToVNode({ animation: retrieve2(opts.cssAnimation, true), + emphasis: retrieve2(opts.cssEmphasis, true), willUpdate: false, compress: true, useViewBox: retrieve2(opts.useViewBox, true) diff --git a/src/svg/core.ts b/src/svg/core.ts index d4b7c4210..d694c4592 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -8,6 +8,7 @@ export const SVGNS = 'http://www.w3.org/2000/svg'; export const XLINKNS = 'http://www.w3.org/1999/xlink'; export const XMLNS = 'http://www.w3.org/2000/xmlns/'; export const XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace'; +export const META_DATA_PREFIX = 'ecmeta_'; export function createElement(name: string) { return document.createElementNS(SVGNS, name); @@ -128,6 +129,10 @@ export interface BrushScope { cssNodes: Record cssAnims: Record>> + /** + * Cache for css style string, mapping from style string to class name. + */ + cssStyleCache: Record cssClassIdx: number cssAnimIdx: number @@ -141,6 +146,10 @@ export interface BrushScope { * If create animates nodes. */ animation?: boolean, + /** + * If create emphasis styles. + */ + emphasis?: boolean, /** * If will update. Some optimization for string generation can't be applied. @@ -164,6 +173,7 @@ export function createBrushScope(zrId: string): BrushScope { cssNodes: {}, cssAnims: {}, + cssStyleCache: {}, cssClassIdx: 0, cssAnimIdx: 0, diff --git a/src/svg/cssEmphasis.ts b/src/svg/cssEmphasis.ts new file mode 100644 index 000000000..354071c32 --- /dev/null +++ b/src/svg/cssEmphasis.ts @@ -0,0 +1,71 @@ +import LRU from '../core/LRU'; +import { extend, isGradientObject, isString, map } from '../core/util'; +import * as colorTool from '../tool/color'; +import Displayable from '../graphic/Displayable'; +import { GradientObject } from '../graphic/Gradient'; +import { BrushScope, SVGVNodeAttrs } from './core'; + +// TODO: Consider deleting the same logic in ECharts and call this method? +const liftedColorCache = new LRU(100); +function liftColor(color: GradientObject): GradientObject; +function liftColor(color: string): string; +function liftColor(color: string | GradientObject): string | GradientObject { + if (isString(color)) { + let liftedColor = liftedColorCache.get(color); + if (!liftedColor) { + liftedColor = colorTool.lift(color, -0.1); + liftedColorCache.put(color, liftedColor); + } + return liftedColor; + } + else if (isGradientObject(color)) { + const ret = extend({}, color) as GradientObject; + ret.colorStops = map(color.colorStops, stop => ({ + offset: stop.offset, + color: colorTool.lift(stop.color, -0.1) + })); + return ret; + } + // Change nothing. + return color; +} + +export function createCSSEmphasis( + el: Displayable, + attrs: SVGVNodeAttrs, + scope: BrushScope +) { + if (el.states.emphasis) { + const empahsisStyle = el.states.emphasis.style; + let fill = empahsisStyle.fill; + if (!fill) { + // No empahsis fill, lift color + const normalFill = el.style.fill; + const selectFill = el.states.select.style.fill; + const fromFill = el.currentStates.indexOf('select') >= 0 + ? (selectFill || normalFill) + : normalFill; + if (fromFill) { + fill = liftColor(fromFill); + } + else { + // No fill information, ignore css + return; + } + } + const style = { + cursor: 'pointer', // TODO: Should be included in el + fill: fill, + stroke: empahsisStyle.stroke, + 'stroke-width': empahsisStyle.lineWidth + }; + const styleKey = JSON.stringify(style); + let className = scope.cssStyleCache[styleKey]; + if (!className) { + className = scope.zrId + '-cls-' + scope.cssClassIdx++; + scope.cssStyleCache[styleKey] = className; + scope.cssNodes['.' + className + ':hover'] = style; + } + attrs.class = attrs.class ? (attrs.class + ' ' + className) : className; + } +} diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 49a0e139b..47ad3cd73 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -26,7 +26,7 @@ import { getLineHeight } from '../contain/text'; import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; import SVGPathRebuilder from './SVGPathRebuilder'; import mapStyleToAttrs from './mapStyleToAttrs'; -import { SVGVNodeAttrs, createVNode, SVGVNode, vNodeToString, BrushScope } from './core'; +import { SVGVNodeAttrs, createVNode, SVGVNode, vNodeToString, BrushScope, META_DATA_PREFIX } from './core'; import { MatrixArray } from '../core/matrix'; import Displayable from '../graphic/Displayable'; import { assert, clone, isFunction, isString, logError, map, retrieve2 } from '../core/util'; @@ -39,6 +39,7 @@ import { ImageLike } from '../core/types'; import { createCSSAnimation } from './cssAnimation'; import { hasSeparateFont, parseFontSize } from '../graphic/Text'; import { DEFAULT_FONT, DEFAULT_FONT_FAMILY } from '../core/platform'; +import { createCSSEmphasis } from './cssEmphasis'; const round = Math.round; @@ -69,6 +70,14 @@ function setStyleAttrs(attrs: SVGVNodeAttrs, style: AllStyleOption, el: Path | T setShadow(el, attrs, scope); } +function setMetaData(attrs: SVGVNodeAttrs, el: Path | TSpan | ZRImage) { + if (el.__metaData) { + for (const key in el.__metaData) { + attrs[META_DATA_PREFIX + key] = el.__metaData[key] + ''; + } + } +} + function noRotateScale(m: MatrixArray) { return isAroundZero(m[0] - 1) && isAroundZero(m[1]) @@ -204,8 +213,10 @@ export function brushSVGPath(el: Path, scope: BrushScope) { setTransform(attrs, el.transform); setStyleAttrs(attrs, style, el, scope); + setMetaData(attrs, el); scope.animation && createCSSAnimation(el, attrs, scope); + scope.emphasis && createCSSEmphasis(el, attrs, scope); return createVNode(svgElType, el.id + '', attrs); } @@ -248,6 +259,7 @@ export function brushSVGImage(el: ZRImage, scope: BrushScope) { setTransform(attrs, el.transform); setStyleAttrs(attrs, style, el, scope); + setMetaData(attrs, el); scope.animation && createCSSAnimation(el, attrs, scope); @@ -319,6 +331,7 @@ export function brushSVGTSpan(el: TSpan, scope: BrushScope) { } setTransform(attrs, el.transform); setStyleAttrs(attrs, style, el, scope); + setMetaData(attrs, el); scope.animation && createCSSAnimation(el, attrs, scope); diff --git a/test/svg-meta-data.html b/test/svg-meta-data.html new file mode 100644 index 000000000..afc4a1d9c --- /dev/null +++ b/test/svg-meta-data.html @@ -0,0 +1,92 @@ + + + + + Image + + + + + +
+ + + + \ No newline at end of file From 26abab131ccfaea50144f2ba0ad190f9946e939d Mon Sep 17 00:00:00 2001 From: Ovilia Date: Fri, 31 Mar 2023 15:31:51 +0800 Subject: [PATCH 2/5] feat(ssr): add style in meta data --- src/svg/Painter.ts | 6 +++--- src/svg/cssEmphasis.ts | 33 ++++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index 59f0ab3a9..c20543854 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -124,10 +124,10 @@ class SVGPainter implements PainterBase { } renderToVNode(opts?: { - animation?: boolean - willUpdate?: boolean + animation?: boolean, + willUpdate?: boolean, compress?: boolean, - useViewBox?: boolean + useViewBox?: boolean, emphasis?: boolean }) { diff --git a/src/svg/cssEmphasis.ts b/src/svg/cssEmphasis.ts index 354071c32..d3ab72d17 100644 --- a/src/svg/cssEmphasis.ts +++ b/src/svg/cssEmphasis.ts @@ -35,30 +35,41 @@ export function createCSSEmphasis( attrs: SVGVNodeAttrs, scope: BrushScope ) { - if (el.states.emphasis) { - const empahsisStyle = el.states.emphasis.style; + if (!el.ignore && el.__metaData) { + const empahsisStyle = el.states.emphasis + ? el.states.emphasis.style + : {}; let fill = empahsisStyle.fill; if (!fill) { // No empahsis fill, lift color const normalFill = el.style.fill; - const selectFill = el.states.select.style.fill; + const selectFill = el.states.select && el.states.select.style.fill; const fromFill = el.currentStates.indexOf('select') >= 0 ? (selectFill || normalFill) : normalFill; if (fromFill) { fill = liftColor(fromFill); } - else { - // No fill information, ignore css - return; - } + } + let lineWidth = empahsisStyle.lineWidth; + if (lineWidth) { + // Symbols use transform to set size, so lineWidth + // should be divided by scaleX + const scaleX = el.transform ? el.transform[0] : 1; + lineWidth = lineWidth / scaleX; } const style = { cursor: 'pointer', // TODO: Should be included in el - fill: fill, - stroke: empahsisStyle.stroke, - 'stroke-width': empahsisStyle.lineWidth - }; + } as any; + if (fill) { + style.fill = fill; + } + if (empahsisStyle.stroke) { + style.stroke = empahsisStyle.stroke; + } + if (lineWidth) { + style['stroke-width'] = lineWidth; + } const styleKey = JSON.stringify(style); let className = scope.cssStyleCache[styleKey]; if (!className) { From d7005b8f125d55a3e18fc779c3b71dca7696d7d9 Mon Sep 17 00:00:00 2001 From: Ovilia Date: Wed, 31 May 2023 17:13:36 +0800 Subject: [PATCH 3/5] feat(ssr): improve code --- src/svg/core.ts | 2 -- src/svg/cssAnimation.ts | 3 ++- src/svg/cssClassId.ts | 5 ++++ src/svg/cssEmphasis.ts | 55 ++++++++++++----------------------------- src/svg/graphic.ts | 9 ++++--- src/tool/color.ts | 28 ++++++++++++++++++++- test/svg-meta-data.html | 15 ++++++----- 7 files changed, 62 insertions(+), 55 deletions(-) create mode 100644 src/svg/cssClassId.ts diff --git a/src/svg/core.ts b/src/svg/core.ts index d694c4592..f29cf532d 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -134,7 +134,6 @@ export interface BrushScope { */ cssStyleCache: Record - cssClassIdx: number cssAnimIdx: number shadowIdx: number @@ -175,7 +174,6 @@ export function createBrushScope(zrId: string): BrushScope { cssAnims: {}, cssStyleCache: {}, - cssClassIdx: 0, cssAnimIdx: 0, shadowIdx: 0, diff --git a/src/svg/cssAnimation.ts b/src/svg/cssAnimation.ts index 38fe5cfe3..f14eea43d 100644 --- a/src/svg/cssAnimation.ts +++ b/src/svg/cssAnimation.ts @@ -10,6 +10,7 @@ import Animator from '../animation/Animator'; import CompoundPath from '../graphic/CompoundPath'; import { AnimationEasing } from '../animation/easing'; import { createCubicEasingFunc } from '../animation/cubicEasing'; +import { getClassId } from './cssClassId'; export const EASING_MAP: Record = { // From https://easings.net/ @@ -355,7 +356,7 @@ export function createCSSAnimation( } if (cssAnimations.length) { - const className = scope.zrId + '-cls-' + scope.cssClassIdx++; + const className = scope.zrId + '-cls-' + getClassId(); scope.cssNodes['.' + className] = { animation: cssAnimations.join(',') }; diff --git a/src/svg/cssClassId.ts b/src/svg/cssClassId.ts new file mode 100644 index 000000000..b45ab167d --- /dev/null +++ b/src/svg/cssClassId.ts @@ -0,0 +1,5 @@ +let cssClassIdx = 0; + +export function getClassId() { + return cssClassIdx++; +} diff --git a/src/svg/cssEmphasis.ts b/src/svg/cssEmphasis.ts index d3ab72d17..e5e3f86db 100644 --- a/src/svg/cssEmphasis.ts +++ b/src/svg/cssEmphasis.ts @@ -1,34 +1,7 @@ -import LRU from '../core/LRU'; -import { extend, isGradientObject, isString, map } from '../core/util'; -import * as colorTool from '../tool/color'; import Displayable from '../graphic/Displayable'; -import { GradientObject } from '../graphic/Gradient'; +import { liftColor } from '../tool/color'; import { BrushScope, SVGVNodeAttrs } from './core'; - -// TODO: Consider deleting the same logic in ECharts and call this method? -const liftedColorCache = new LRU(100); -function liftColor(color: GradientObject): GradientObject; -function liftColor(color: string): string; -function liftColor(color: string | GradientObject): string | GradientObject { - if (isString(color)) { - let liftedColor = liftedColorCache.get(color); - if (!liftedColor) { - liftedColor = colorTool.lift(color, -0.1); - liftedColorCache.put(color, liftedColor); - } - return liftedColor; - } - else if (isGradientObject(color)) { - const ret = extend({}, color) as GradientObject; - ret.colorStops = map(color.colorStops, stop => ({ - offset: stop.offset, - color: colorTool.lift(stop.color, -0.1) - })); - return ret; - } - // Change nothing. - return color; -} +import { getClassId } from './cssClassId'; export function createCSSEmphasis( el: Displayable, @@ -36,14 +9,16 @@ export function createCSSEmphasis( scope: BrushScope ) { if (!el.ignore && el.__metaData) { - const empahsisStyle = el.states.emphasis + const emphasisStyle = el.states.emphasis && el.states.emphasis.style ? el.states.emphasis.style : {}; - let fill = empahsisStyle.fill; + let fill = emphasisStyle.fill; if (!fill) { // No empahsis fill, lift color - const normalFill = el.style.fill; - const selectFill = el.states.select && el.states.select.style.fill; + const normalFill = el.style && el.style.fill; + const selectFill = el.states.select + && el.states.select.style + && el.states.select.style.fill; const fromFill = el.currentStates.indexOf('select') >= 0 ? (selectFill || normalFill) : normalFill; @@ -51,21 +26,23 @@ export function createCSSEmphasis( fill = liftColor(fromFill); } } - let lineWidth = empahsisStyle.lineWidth; + let lineWidth = emphasisStyle.lineWidth; if (lineWidth) { // Symbols use transform to set size, so lineWidth // should be divided by scaleX - const scaleX = el.transform ? el.transform[0] : 1; + const scaleX = (!emphasisStyle.strokeNoScale && el.transform) + ? el.transform[0] + : 1; lineWidth = lineWidth / scaleX; } const style = { - cursor: 'pointer', // TODO: Should be included in el + cursor: 'pointer', // TODO: Should this be customized? } as any; if (fill) { style.fill = fill; } - if (empahsisStyle.stroke) { - style.stroke = empahsisStyle.stroke; + if (emphasisStyle.stroke) { + style.stroke = emphasisStyle.stroke; } if (lineWidth) { style['stroke-width'] = lineWidth; @@ -73,7 +50,7 @@ export function createCSSEmphasis( const styleKey = JSON.stringify(style); let className = scope.cssStyleCache[styleKey]; if (!className) { - className = scope.zrId + '-cls-' + scope.cssClassIdx++; + className = scope.zrId + '-cls-' + getClassId(); scope.cssStyleCache[styleKey] = className; scope.cssNodes['.' + className + ':hover'] = style; } diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 47ad3cd73..7e659a7d7 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -29,7 +29,7 @@ import mapStyleToAttrs from './mapStyleToAttrs'; import { SVGVNodeAttrs, createVNode, SVGVNode, vNodeToString, BrushScope, META_DATA_PREFIX } from './core'; import { MatrixArray } from '../core/matrix'; import Displayable from '../graphic/Displayable'; -import { assert, clone, isFunction, isString, logError, map, retrieve2 } from '../core/util'; +import { assert, clone, each, isFunction, isString, logError, map, retrieve2 } from '../core/util'; import Polyline from '../graphic/shape/Polyline'; import Polygon from '../graphic/shape/Polygon'; import { GradientObject } from '../graphic/Gradient'; @@ -72,9 +72,10 @@ function setStyleAttrs(attrs: SVGVNodeAttrs, style: AllStyleOption, el: Path | T function setMetaData(attrs: SVGVNodeAttrs, el: Path | TSpan | ZRImage) { if (el.__metaData) { - for (const key in el.__metaData) { - attrs[META_DATA_PREFIX + key] = el.__metaData[key] + ''; - } + each(el.__metaData, function (val, key) { + attrs[(META_DATA_PREFIX + key).toLowerCase()] + = val + ''; + }); } } diff --git a/src/tool/color.ts b/src/tool/color.ts index bacf1fa9e..ae912e3fe 100644 --- a/src/tool/color.ts +++ b/src/tool/color.ts @@ -1,4 +1,6 @@ import LRU from '../core/LRU'; +import { extend, isGradientObject, isString, map } from '../core/util'; +import { GradientObject } from '../graphic/Gradient'; const kCSSColorTable = { 'transparent': [0, 0, 0, 0], 'aliceblue': [240, 248, 255, 1], @@ -566,4 +568,28 @@ export function random(): string { Math.round(Math.random() * 255), Math.round(Math.random() * 255) ], 'rgb'); -} \ No newline at end of file +} + +const liftedColorCache = new LRU(100); +export function liftColor(color: GradientObject): GradientObject; +export function liftColor(color: string): string; +export function liftColor(color: string | GradientObject): string | GradientObject { + if (isString(color)) { + let liftedColor = liftedColorCache.get(color); + if (!liftedColor) { + liftedColor = lift(color, -0.1); + liftedColorCache.put(color, liftedColor); + } + return liftedColor; + } + else if (isGradientObject(color)) { + const ret = extend({}, color) as GradientObject; + ret.colorStops = map(color.colorStops, stop => ({ + offset: stop.offset, + color: lift(stop.color, -0.1) + })); + return ret; + } + // Change nothing. + return color; +} diff --git a/test/svg-meta-data.html b/test/svg-meta-data.html index afc4a1d9c..c092cb1c7 100644 --- a/test/svg-meta-data.html +++ b/test/svg-meta-data.html @@ -58,9 +58,9 @@ if (type === 'seriesItem') { child.addEventListener('mouseover', event => { const attrs = event.target.attributes; - const seriesIndex = attrs.ecmeta_seriesIndex.value; - const dataIndex = attrs.ecmeta_dataIndex.value; - const styleType = attrs.ecmeta_styleType.value; + const seriesIndex = attrs.ecmeta_seriesindex.value; + const dataIndex = attrs.ecmeta_dataindex.value; + const styleType = attrs.ecmeta_styletype.value; const emphasis = metaData.series[seriesIndex].emphasis; if (emphasis && emphasis[styleType]) { const style = emphasis[styleType]; @@ -71,9 +71,9 @@ }); child.addEventListener('mouseout', event => { const attrs = event.target.attributes; - const seriesIndex = attrs.ecmeta_seriesIndex.value; - const dataIndex = attrs.ecmeta_dataIndex.value; - const styleType = attrs.ecmeta_styleType.value; + const seriesIndex = attrs.ecmeta_seriesindex.value; + const dataIndex = attrs.ecmeta_dataindex.value; + const styleType = attrs.ecmeta_styletype.value; const normal = metaData.series[seriesIndex].normal; if (normal && normal[styleType]) { const style = normal[styleType]; @@ -84,8 +84,7 @@ }); } } - console.log(root) - }); + }, 100); From fb4c2656eae4effd4f215232fbf94d589ca5c9a4 Mon Sep 17 00:00:00 2001 From: Ovilia Date: Tue, 13 Jun 2023 17:11:45 +0800 Subject: [PATCH 4/5] feat(ssr): provide registerSSRDataGetter --- src/Element.ts | 21 +++++++-- src/svg/cssEmphasis.ts | 104 +++++++++++++++++++++++------------------ src/svg/graphic.ts | 13 +++++- src/zrender.ts | 17 +++++++ 4 files changed, 103 insertions(+), 52 deletions(-) diff --git a/src/Element.ts b/src/Element.ts index ec1211ac1..e148e4434 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -368,11 +368,6 @@ class Element { */ __inHover: boolean - /** - * Any information to be binded on the element when rendering. - */ - __metaData: Record - /** * path to clip the elements and its children, if it is a group. * @see http://www.w3.org/TR/2dcontext/#clipping-region @@ -1022,6 +1017,22 @@ class Element { } } + /** + * Return if el.silent or any ancestor element has silent true. + */ + isSilent() { + let isSilent = this.silent; + let ancestor = this.parent; + while (!isSilent && ancestor) { + if (ancestor.silent) { + isSilent = true; + break; + } + ancestor = ancestor.parent; + } + return isSilent; + } + /** * Update animation targets when reference is changed. */ diff --git a/src/svg/cssEmphasis.ts b/src/svg/cssEmphasis.ts index e5e3f86db..6632c7785 100644 --- a/src/svg/cssEmphasis.ts +++ b/src/svg/cssEmphasis.ts @@ -8,52 +8,66 @@ export function createCSSEmphasis( attrs: SVGVNodeAttrs, scope: BrushScope ) { - if (!el.ignore && el.__metaData) { - const emphasisStyle = el.states.emphasis && el.states.emphasis.style - ? el.states.emphasis.style - : {}; - let fill = emphasisStyle.fill; - if (!fill) { - // No empahsis fill, lift color - const normalFill = el.style && el.style.fill; - const selectFill = el.states.select - && el.states.select.style - && el.states.select.style.fill; - const fromFill = el.currentStates.indexOf('select') >= 0 - ? (selectFill || normalFill) - : normalFill; - if (fromFill) { - fill = liftColor(fromFill); - } - } - let lineWidth = emphasisStyle.lineWidth; - if (lineWidth) { - // Symbols use transform to set size, so lineWidth - // should be divided by scaleX - const scaleX = (!emphasisStyle.strokeNoScale && el.transform) - ? el.transform[0] - : 1; - lineWidth = lineWidth / scaleX; - } - const style = { - cursor: 'pointer', // TODO: Should this be customized? - } as any; - if (fill) { - style.fill = fill; - } - if (emphasisStyle.stroke) { - style.stroke = emphasisStyle.stroke; + if (!el.ignore) { + if (el.isSilent()) { + // If el is silent, it can not be hovered nor selected. + // So set pointer-events to pass through. + const style = { + 'pointer-events': 'none' + }; + setClassAttribute(style, attrs, scope, true); } - if (lineWidth) { - style['stroke-width'] = lineWidth; - } - const styleKey = JSON.stringify(style); - let className = scope.cssStyleCache[styleKey]; - if (!className) { - className = scope.zrId + '-cls-' + getClassId(); - scope.cssStyleCache[styleKey] = className; - scope.cssNodes['.' + className + ':hover'] = style; + else { + const emphasisStyle = el.states.emphasis && el.states.emphasis.style + ? el.states.emphasis.style + : {}; + let fill = emphasisStyle.fill; + if (!fill) { + // No empahsis fill, lift color + const normalFill = el.style && el.style.fill; + const selectFill = el.states.select + && el.states.select.style + && el.states.select.style.fill; + const fromFill = el.currentStates.indexOf('select') >= 0 + ? (selectFill || normalFill) + : normalFill; + if (fromFill) { + fill = liftColor(fromFill); + } + } + let lineWidth = emphasisStyle.lineWidth; + if (lineWidth) { + // Symbols use transform to set size, so lineWidth + // should be divided by scaleX + const scaleX = (!emphasisStyle.strokeNoScale && el.transform) + ? el.transform[0] + : 1; + lineWidth = lineWidth / scaleX; + } + const style = { + cursor: 'pointer', // TODO: Should this be customized? + } as any; + if (fill) { + style.fill = fill; + } + if (emphasisStyle.stroke) { + style.stroke = emphasisStyle.stroke; + } + if (lineWidth) { + style['stroke-width'] = lineWidth; + } + setClassAttribute(style, attrs, scope, true); } - attrs.class = attrs.class ? (attrs.class + ' ' + className) : className; } } + +function setClassAttribute(style: object, attrs: SVGVNodeAttrs, scope: BrushScope, withHover: boolean) { + const styleKey = JSON.stringify(style); + let className = scope.cssStyleCache[styleKey]; + if (!className) { + className = scope.zrId + '-cls-' + getClassId(); + scope.cssStyleCache[styleKey] = className; + scope.cssNodes['.' + className + (withHover ? ':hover' : '')] = style as any; + } + attrs.class = attrs.class ? (attrs.class + ' ' + className) : className; +} diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index 7e659a7d7..c7ffe87f1 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -40,6 +40,7 @@ import { createCSSAnimation } from './cssAnimation'; import { hasSeparateFont, parseFontSize } from '../graphic/Text'; import { DEFAULT_FONT, DEFAULT_FONT_FAMILY } from '../core/platform'; import { createCSSEmphasis } from './cssEmphasis'; +import { getElementSSRData } from '../zrender'; const round = Math.round; @@ -62,6 +63,10 @@ function setStyleAttrs(attrs: SVGVNodeAttrs, style: AllStyleOption, el: Path | T else if (isFillStroke && isPattern(val)) { setPattern(el, attrs, key, scope); } + else if (isFillStroke && val === 'none') { + // When is none, it cannot be interacted when ssr + attrs[key] = 'transparent'; + } else { attrs[key] = val; } @@ -71,11 +76,15 @@ function setStyleAttrs(attrs: SVGVNodeAttrs, style: AllStyleOption, el: Path | T } function setMetaData(attrs: SVGVNodeAttrs, el: Path | TSpan | ZRImage) { - if (el.__metaData) { - each(el.__metaData, function (val, key) { + const metaData = getElementSSRData(el); + if (metaData) { + metaData.each((val, key) => { attrs[(META_DATA_PREFIX + key).toLowerCase()] = val + ''; }); + if (el.isSilent()) { + attrs[META_DATA_PREFIX + 'silent'] = 'true'; + } } } diff --git a/src/zrender.ts b/src/zrender.ts index 44fd886b6..208ab23a2 100644 --- a/src/zrender.ts +++ b/src/zrender.ts @@ -482,6 +482,23 @@ export function registerPainter(name: string, Ctor: PainterBaseCtor) { painterCtors[name] = Ctor; } +export type ElementSSRData = zrUtil.HashMap; +export type ElementSSRDataGetter = (el: Element) => zrUtil.HashMap; + +let ssrDataGetter = function (el: Element): ElementSSRData { + return null; +} + +export function getElementSSRData(el: Element): ElementSSRData { + if (typeof ssrDataGetter === 'function') { + return ssrDataGetter(el); + } +} + +export function registerSSRDataGetter(getter: ElementSSRDataGetter) { + ssrDataGetter = getter; +} + /** * @type {string} */ From a78b088d48ac93249b81ccfb608ebcdedfe02a59 Mon Sep 17 00:00:00 2001 From: Ovilia Date: Thu, 16 Nov 2023 14:40:01 +0800 Subject: [PATCH 5/5] fix(ssr): remove a not used method --- src/svg/graphic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index c7ffe87f1..8f9c08b39 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -29,7 +29,7 @@ import mapStyleToAttrs from './mapStyleToAttrs'; import { SVGVNodeAttrs, createVNode, SVGVNode, vNodeToString, BrushScope, META_DATA_PREFIX } from './core'; import { MatrixArray } from '../core/matrix'; import Displayable from '../graphic/Displayable'; -import { assert, clone, each, isFunction, isString, logError, map, retrieve2 } from '../core/util'; +import { assert, clone, isFunction, isString, logError, map, retrieve2 } from '../core/util'; import Polyline from '../graphic/shape/Polyline'; import Polygon from '../graphic/shape/Polygon'; import { GradientObject } from '../graphic/Gradient';