From ca96e42f819e3e53b1b5b447f024f836dd3520d8 Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 28 May 2021 14:32:35 +0800 Subject: [PATCH 1/2] fix(svg): 1) fix apache/echarts#15023, rect path can't be closed. 2) fix apache/echarts#15029 normalize color when using SVG renderer to support more cases, for example, some tools/platforms can't recognize the alpha in color. 3) fix eslint error about `for-in`. --- src/svg/Painter.ts | 7 +++++-- src/svg/core.ts | 22 ++++++++++++++++++++- src/svg/graphic.ts | 33 +++++++++++++++++++++---------- src/svg/helper/ClippathManager.ts | 24 +++++++++++++--------- src/svg/helper/ShadowManager.ts | 16 +++++++++------ 5 files changed, 74 insertions(+), 28 deletions(-) diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index fe61daa63..8cb19150d 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -3,7 +3,7 @@ * @module zrender/svg/Painter */ -import {createElement} from './core'; +import {createElement, normalizeColor} from './core'; import * as util from '../core/util'; import Path from '../graphic/Path'; import ZRImage from '../graphic/Image'; @@ -194,7 +194,10 @@ class SVGPainter implements PainterBase { bgNode.setAttribute('x', 0 as any); bgNode.setAttribute('y', 0 as any); bgNode.setAttribute('id', 0 as any); - bgNode.style.fill = backgroundColor; + const { color, opacity } = normalizeColor(backgroundColor); + bgNode.setAttribute('fill', color); + bgNode.setAttribute('fill-opacity', opacity as any); + this._backgroundRoot.appendChild(bgNode); this._backgroundNode = bgNode; } diff --git a/src/svg/core.ts b/src/svg/core.ts index cef8e2093..ad6caf479 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -1,3 +1,23 @@ +import { parse } from '../tool/color' + export function createElement(name: string) { return document.createElementNS('http://www.w3.org/2000/svg', name); -} \ No newline at end of file +} + +export function normalizeColor(color: string): { color: string, opacity: number } { + let opacity; + if (!color || color === 'transparent') { + color = 'none'; + } + else if (color.indexOf('rgba') > -1) { + const arr = parse(color); + if (arr) { + color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')'; + opacity = arr[3]; + } + } + return { + color, + opacity: opacity == null ? 1 : opacity + }; +} diff --git a/src/svg/graphic.ts b/src/svg/graphic.ts index ed3921bb2..709b0a544 100644 --- a/src/svg/graphic.ts +++ b/src/svg/graphic.ts @@ -2,7 +2,7 @@ // 1. shadow // 2. Image: sx, sy, sw, sh -import {createElement} from './core'; +import {createElement, normalizeColor} from './core'; import { PathRebuilder } from '../core/PathProxy'; import * as matrix from '../core/matrix'; import Path, { PathStyleProps } from '../graphic/Path'; @@ -92,12 +92,14 @@ function bindStyle(svgEl: SVGElement, style: AllStyleOption, el?: Path | TSpan | } if (pathHasFill(style)) { - let fill = style.fill; - fill = fill === 'transparent' ? NONE : fill; - attr(svgEl, 'fill', fill as string); + const fill = normalizeColor(style.fill as string); + attr(svgEl, 'fill', fill.color); attr(svgEl, 'fill-opacity', - (style.fillOpacity != null ? style.fillOpacity * opacity : opacity) + '' + (style.fillOpacity != null + ? style.fillOpacity * fill.opacity * opacity + : fill.opacity * opacity + ) + '' ); } else { @@ -105,9 +107,8 @@ function bindStyle(svgEl: SVGElement, style: AllStyleOption, el?: Path | TSpan | } if (pathHasStroke(style)) { - let stroke = style.stroke; - stroke = stroke === 'transparent' ? NONE : stroke; - attr(svgEl, 'stroke', stroke as string); + const stroke = normalizeColor(style.stroke as string); + attr(svgEl, 'stroke', stroke.color); const strokeWidth = style.lineWidth; const strokeScale = style.strokeNoScale ? (el as Path).getLineScale() @@ -115,7 +116,11 @@ function bindStyle(svgEl: SVGElement, style: AllStyleOption, el?: Path | TSpan | attr(svgEl, 'stroke-width', (strokeScale ? strokeWidth / strokeScale : 0) + ''); // stroke then fill for text; fill then stroke for others attr(svgEl, 'paint-order', style.strokeFirst ? 'stroke' : 'fill'); - attr(svgEl, 'stroke-opacity', (style.strokeOpacity != null ? style.strokeOpacity * opacity : opacity) + ''); + attr(svgEl, 'stroke-opacity', ( + style.strokeOpacity != null + ? style.strokeOpacity * stroke.opacity * opacity + : stroke.opacity * opacity + ) + ''); let lineDash = style.lineDash && strokeWidth > 0 && normalizeLineDash(style.lineDash, strokeWidth); if (lineDash) { let lineDashOffset = style.lineDashOffset; @@ -169,7 +174,14 @@ class SVGPathRebuilder implements PathRebuilder { arc(cx: number, cy: number, r: number, startAngle: number, endAngle: number, anticlockwise: boolean) { this.ellipse(cx, cy, r, r, 0, startAngle, endAngle, anticlockwise); } - ellipse(cx: number, cy: number, rx: number, ry: number, psi: number, startAngle: number, endAngle: number, anticlockwise: boolean) { + ellipse( + cx: number, cy: number, + rx: number, ry: number, + psi: number, + startAngle: number, + endAngle: number, + anticlockwise: boolean + ) { const firstCmd = this._d.length === 0; @@ -237,6 +249,7 @@ class SVGPathRebuilder implements PathRebuilder { this._add('L', x + w, y + h); this._add('L', x, y + h); this._add('L', x, y); + this._add('Z'); } closePath() { // Not use Z as first command diff --git a/src/svg/helper/ClippathManager.ts b/src/svg/helper/ClippathManager.ts index 774878aeb..2e2fe3345 100644 --- a/src/svg/helper/ClippathManager.ts +++ b/src/svg/helper/ClippathManager.ts @@ -44,8 +44,11 @@ export default class ClippathManager extends Definable { markAllUnused() { super.markAllUnused(); - for (let key in this._refGroups) { - this.markDomUnused(this._refGroups[key]); + const refGroups = this._refGroups; + for (let key in refGroups) { + if (refGroups.hasOwnProperty(key)) { + this.markDomUnused(refGroups[key]); + } } this._keyDuplicateCount = {}; } @@ -163,13 +166,16 @@ export default class ClippathManager extends Definable { super.removeUnused(); const newRefGroupsMap: Dictionary = {}; - for (let key in this._refGroups) { - const group = this._refGroups[key]; - if (!this.isDomUnused(group)) { - newRefGroupsMap[key] = group; - } - else if (group.parentNode) { - group.parentNode.removeChild(group); + const refGroups = this._refGroups; + for (let key in refGroups) { + if (refGroups.hasOwnProperty(key)) { + const group = refGroups[key]; + if (!this.isDomUnused(group)) { + newRefGroupsMap[key] = group; + } + else if (group.parentNode) { + group.parentNode.removeChild(group); + } } } this._refGroups = newRefGroupsMap; diff --git a/src/svg/helper/ShadowManager.ts b/src/svg/helper/ShadowManager.ts index 3baf3345d..16814c0b6 100644 --- a/src/svg/helper/ShadowManager.ts +++ b/src/svg/helper/ShadowManager.ts @@ -7,6 +7,7 @@ import Definable from './Definable'; import Displayable from '../../graphic/Displayable'; import { PathStyleProps } from '../../graphic/Path'; import { Dictionary } from '../../core/types'; +import { normalizeColor } from '../core'; type DisplayableExtended = Displayable & { _shadowDom: SVGElement @@ -35,7 +36,7 @@ export default class ShadowManager extends Definable { if (!shadowDom) { shadowDom = this.createElement('filter') as SVGFilterElement; shadowDom.setAttribute('id', 'zr' + this._zrId + '-shadow-' + this.nextId++); - const domChild = this.createElement('feDropShadow') + const domChild = this.createElement('feDropShadow'); shadowDom.appendChild(domChild); this.addDom(shadowDom); } @@ -98,11 +99,12 @@ export default class ShadowManager extends Definable { let offsetX = style.shadowOffsetX || 0; let offsetY = style.shadowOffsetY || 0; let blur = style.shadowBlur; - let color = style.shadowColor; + const normalizedColor = normalizeColor(style.shadowColor); domChild.setAttribute('dx', offsetX / scaleX + ''); domChild.setAttribute('dy', offsetY / scaleY + ''); - domChild.setAttribute('flood-color', color); + domChild.setAttribute('flood-color', normalizedColor.color); + domChild.setAttribute('flood-opacity', normalizedColor.opacity + ''); // Divide by two here so that it looks the same as in canvas // See: https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur @@ -134,9 +136,11 @@ export default class ShadowManager extends Definable { let shadowDomsPool = this._shadowDomPool; // let currentUsedShadow = 0; - for (let key in this._shadowDomMap) { - const dom = this._shadowDomMap[key]; - shadowDomsPool.push(dom); + const shadowDomMap = this._shadowDomMap; + for (let key in shadowDomMap) { + if (shadowDomMap.hasOwnProperty(key)) { + shadowDomsPool.push(shadowDomMap[key]); + } // currentUsedShadow++; } From 6a9b4af9cd0ce6fd16eaedef9ca8477f2fc37e56 Mon Sep 17 00:00:00 2001 From: plainheart Date: Fri, 28 May 2021 14:46:49 +0800 Subject: [PATCH 2/2] fix(svg): add missing comma. --- src/svg/core.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/svg/core.ts b/src/svg/core.ts index ad6caf479..99839014c 100644 --- a/src/svg/core.ts +++ b/src/svg/core.ts @@ -1,4 +1,4 @@ -import { parse } from '../tool/color' +import { parse } from '../tool/color'; export function createElement(name: string) { return document.createElementNS('http://www.w3.org/2000/svg', name);