diff --git a/build/package.json b/build/package.json new file mode 100644 index 000000000..6a0d2ef2a --- /dev/null +++ b/build/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} \ No newline at end of file diff --git a/dist/package.json b/dist/package.json new file mode 100644 index 000000000..6a0d2ef2a --- /dev/null +++ b/dist/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} \ No newline at end of file diff --git a/package.json b/package.json index a56e276b7..a779adeac 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ }, "sideEffects": [ "lib/canvas/canvas.js", - "lib/svg/svg.js" + "lib/svg/svg.js", + "lib/all.js" ], "devDependencies": { "@microsoft/api-extractor": "^7.7.2", @@ -58,5 +59,17 @@ "ts-jest": "^27.0.6", "typescript": "^4.4.3", "uglify-js": "^3.10.0" + }, + "type": "module", + "exports": { + ".": { + "types": "./index.d.ts", + "require": "./dist/zrender.js", + "import": "./index.js" + }, + "./*.js": "./*.js", + "./*.ts": "./*.ts", + "./*.json": "./*.json", + "./*": "./*.js" } -} +} \ No newline at end of file diff --git a/src/.eslintrc.yaml b/src/.eslintrc.yaml index 8fb078f0f..752deedd0 100644 --- a/src/.eslintrc.yaml +++ b/src/.eslintrc.yaml @@ -21,7 +21,7 @@ rules: - "warn" - "error" no-constant-condition: 0 - comma-dangle: 2 + comma-dangle: 0 no-debugger: 2 no-dupe-keys: 2 no-empty-character-class: 2 diff --git a/src/Element.ts b/src/Element.ts index eb87cc5f9..e148e4434 100644 --- a/src/Element.ts +++ b/src/Element.ts @@ -1017,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/canvas/Layer.ts b/src/canvas/Layer.ts index ed67e8622..c887d884b 100644 --- a/src/canvas/Layer.ts +++ b/src/canvas/Layer.ts @@ -89,8 +89,6 @@ export default class Layer extends Eventful { private _paintRects: BoundingRect[] - __painter: CanvasPainter - __dirty = true __firstTimePaint = true @@ -311,7 +309,7 @@ export default class Layer extends Eventful { * rect if and only if it's not painted this frame and was * previously painted on the canvas. */ - const shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true); + const shouldPaint = el && el.shouldBePainted(viewWidth, viewHeight, true, true); if (el && (!shouldPaint || !el.__zr) && el.__isRendered) { // el was removed const prevRect = el.getPrevPaintRect(); @@ -449,9 +447,8 @@ export default class Layer extends Eventful { clearColorGradientOrPattern = createCanvasPattern( ctx, clearColor, { dirty() { - // TODO self.setUnpainted(); - self.__painter.refresh(); + self.painter.refresh(); } } ); diff --git a/src/canvas/Painter.ts b/src/canvas/Painter.ts index 4bc818182..568885692 100644 --- a/src/canvas/Painter.ts +++ b/src/canvas/Painter.ts @@ -597,7 +597,7 @@ export default class CanvasPainter implements PainterBase { layersMap[zlevel] = layer; - // Vitual layer will not directly show on the screen. + // Virtual layer will not directly show on the screen. // (It can be a WebGL layer and assigned to a ZRImage element) // But it still under management of zrender. if (!layer.virtual) { @@ -623,7 +623,7 @@ export default class CanvasPainter implements PainterBase { } } - layer.__painter = this; + layer.painter || (layer.painter = this); } // Iterate each layer diff --git a/src/core/env.ts b/src/core/env.ts index cd52e3d91..92c7f9e74 100644 --- a/src/core/env.ts +++ b/src/core/env.ts @@ -37,7 +37,10 @@ else if (typeof document === 'undefined' && typeof self !== 'undefined') { // In worker env.worker = true; } -else if (typeof navigator === 'undefined') { +else if ( + typeof navigator === 'undefined' || + navigator.userAgent.indexOf('Node.js') === 0 +) { // In node env.node = true; env.svgSupported = true; diff --git a/src/core/matrix.ts b/src/core/matrix.ts index 78009f815..13b5b9e19 100644 --- a/src/core/matrix.ts +++ b/src/core/matrix.ts @@ -79,7 +79,12 @@ export function translate(out: MatrixArray, a: MatrixArray, v: VectorArray): Mat /** * 旋转变换 */ -export function rotate(out: MatrixArray, a: MatrixArray, rad: number): MatrixArray { +export function rotate( + out: MatrixArray, + a: MatrixArray, + rad: number, + pivot: VectorArray = [0, 0] +): MatrixArray { const aa = a[0]; const ac = a[2]; const atx = a[4]; @@ -93,8 +98,8 @@ export function rotate(out: MatrixArray, a: MatrixArray, rad: number): MatrixArr out[1] = -aa * st + ab * ct; out[2] = ac * ct + ad * st; out[3] = -ac * st + ct * ad; - out[4] = ct * atx + st * aty; - out[5] = ct * aty - st * atx; + out[4] = ct * (atx - pivot[0]) + st * (aty - pivot[1]) + pivot[0]; + out[5] = ct * (aty - pivot[1]) - st * (atx - pivot[0]) + pivot[1]; return out; } diff --git a/src/graphic/helper/parseText.ts b/src/graphic/helper/parseText.ts index 201d8fc00..275e36b0d 100644 --- a/src/graphic/helper/parseText.ts +++ b/src/graphic/helper/parseText.ts @@ -41,8 +41,8 @@ export function truncateText( text: string, containerWidth: number, font: string, - ellipsis: string, - options: InnerTruncateOption + ellipsis?: string, + options?: InnerTruncateOption ): string { if (!containerWidth) { return ''; @@ -63,8 +63,8 @@ export function truncateText( function prepareTruncateOptions( containerWidth: number, font: string, - ellipsis: string, - options: InnerTruncateOption + ellipsis?: string, + options?: InnerTruncateOption ): InnerPreparedTruncateOption { options = options || {}; let preparedOpts = extend({}, options) as InnerPreparedTruncateOption; diff --git a/src/svg/Painter.ts b/src/svg/Painter.ts index fc85ed939..243b3ce50 100644 --- a/src/svg/Painter.ts +++ b/src/svg/Painter.ts @@ -124,10 +124,11 @@ class SVGPainter implements PainterBase { } renderToVNode(opts?: { - animation?: boolean - willUpdate?: boolean + animation?: boolean, + willUpdate?: boolean, compress?: boolean, - useViewBox?: 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) @@ -374,8 +382,7 @@ function createBackgroundVNode( width, height, x: '0', - y: '0', - id: '0' + y: '0' } ); if (isGradient(backgroundColor)) { diff --git a/src/svg/core.ts b/src/svg/core.ts index d4b7c4210..f29cf532d 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,8 +129,11 @@ 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 shadowIdx: number @@ -141,6 +145,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,8 +172,8 @@ export function createBrushScope(zrId: string): BrushScope { cssNodes: {}, 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 new file mode 100644 index 000000000..6632c7785 --- /dev/null +++ b/src/svg/cssEmphasis.ts @@ -0,0 +1,73 @@ +import Displayable from '../graphic/Displayable'; +import { liftColor } from '../tool/color'; +import { BrushScope, SVGVNodeAttrs } from './core'; +import { getClassId } from './cssClassId'; + +export function createCSSEmphasis( + el: Displayable, + attrs: SVGVNodeAttrs, + scope: BrushScope +) { + 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); + } + 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); + } + } +} + +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 49a0e139b..096014967 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,8 @@ 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'; +import { getElementSSRData } from '../zrender'; const round = Math.round; @@ -61,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; } @@ -69,6 +75,18 @@ function setStyleAttrs(attrs: SVGVNodeAttrs, style: AllStyleOption, el: Path | T setShadow(el, attrs, scope); } +function setMetaData(attrs: SVGVNodeAttrs, el: Path | TSpan | ZRImage) { + const metaData = getElementSSRData(el); + if (metaData) { + metaData.each((val, key) => { + val != null && (attrs[(META_DATA_PREFIX + key).toLowerCase()] = val + ''); + }); + if (el.isSilent()) { + attrs[META_DATA_PREFIX + 'silent'] = 'true'; + } + } +} + function noRotateScale(m: MatrixArray) { return isAroundZero(m[0] - 1) && isAroundZero(m[1]) @@ -204,8 +222,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 +268,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 +340,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/src/svg/patch.ts b/src/svg/patch.ts index aeba565c8..edea6ae86 100644 --- a/src/svg/patch.ts +++ b/src/svg/patch.ts @@ -122,7 +122,7 @@ function removeVnodes(parentElm: Node, vnodes: SVGVNode[], startIdx: number, end export function updateAttrs(oldVnode: SVGVNode, vnode: SVGVNode): void { let key: string; - const elm = vnode.elm as Element; + const elm = vnode.elm as SVGElement; const oldAttrs = oldVnode && oldVnode.attrs || {}; const attrs = vnode.attrs || {}; @@ -143,7 +143,10 @@ export function updateAttrs(oldVnode: SVGVNode, vnode: SVGVNode): void { elm.removeAttribute(key); } else { - if (key.charCodeAt(0) !== xChar) { + if (key === 'style') { + elm.style.cssText = cur as string + } + else if (key.charCodeAt(0) !== xChar) { elm.setAttribute(key, cur as any); } // TODO 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/src/tool/parseSVG.ts b/src/tool/parseSVG.ts index cbcc2a804..0ee2739a6 100644 --- a/src/tool/parseSVG.ts +++ b/src/tool/parseSVG.ts @@ -845,7 +845,10 @@ function parseTransformAttribute(xmlNode: SVGElement, node: Element): void { break; case 'rotate': // TODO: zrender use different hand in coordinate system. - matrix.rotate(mt, mt, -parseFloat(valueArr[0]) * DEGREE_TO_ANGLE); + matrix.rotate(mt, mt, -parseFloat(valueArr[0]) * DEGREE_TO_ANGLE, [ + parseFloat(valueArr[1] || '0'), + parseFloat(valueArr[2] || '0') + ]); break; case 'skewX': const sx = Math.tan(parseFloat(valueArr[0]) * DEGREE_TO_ANGLE); diff --git a/src/zrender.ts b/src/zrender.ts index ff0676ac4..b8b86a804 100644 --- a/src/zrender.ts +++ b/src/zrender.ts @@ -81,7 +81,7 @@ class ZRender { private _needsRefresh = true private _needsRefreshHover = true - + private _disposed: boolean; /** * If theme is dark mode. It will determine the color strategy for labels. */ @@ -123,7 +123,7 @@ class ZRender { this.storage = storage; this.painter = painter; - const handerProxy = (!env.node && !env.worker && !ssrMode) + const handlerProxy = (!env.node && !env.worker && !ssrMode) ? new HandlerProxy(painter.getViewportRoot(), painter.root) : null; @@ -137,7 +137,7 @@ class ZRender { pointerSize = zrUtil.retrieve2(opts.pointerSize, defaultPointerSize); } - this.handler = new Handler(storage, painter, handerProxy, painter.root, pointerSize); + this.handler = new Handler(storage, painter, handlerProxy, painter.root, pointerSize); this.animation = new Animation({ stage: { @@ -154,7 +154,7 @@ class ZRender { * 添加元素 */ add(el: Element) { - if (!el) { + if (this._disposed || !el) { return; } this.storage.addRoot(el); @@ -166,7 +166,7 @@ class ZRender { * 删除元素 */ remove(el: Element) { - if (!el) { + if (this._disposed || !el) { return; } this.storage.delRoot(el); @@ -178,6 +178,9 @@ class ZRender { * Change configuration of layer */ configLayer(zLevel: number, config: LayerConfig) { + if (this._disposed) { + return; + } if (this.painter.configLayer) { this.painter.configLayer(zLevel, config); } @@ -188,6 +191,9 @@ class ZRender { * Set background color */ setBackgroundColor(backgroundColor: string | GradientObject | PatternObject) { + if (this._disposed) { + return; + } if (this.painter.setBackgroundColor) { this.painter.setBackgroundColor(backgroundColor); } @@ -215,6 +221,9 @@ class ZRender { * Repaint the canvas immediately */ refreshImmediately(fromInside?: boolean) { + if (this._disposed) { + return; + } // const start = new Date(); if (!fromInside) { // Update animation if refreshImmediately is invoked from outside. @@ -234,6 +243,9 @@ class ZRender { * Mark and repaint the canvas in the next frame of browser */ refresh() { + if (this._disposed) { + return; + } this._needsRefresh = true; // Active the animation again. this.animation.start(); @@ -243,6 +255,9 @@ class ZRender { * Perform all refresh */ flush() { + if (this._disposed) { + return; + } this._flush(false); } @@ -269,7 +284,7 @@ class ZRender { } else if (this._sleepAfterStill > 0) { this._stillFrameAccum++; - // Stop the animiation after still for 10 frames. + // Stop the animation after still for 10 frames. if (this._stillFrameAccum > this._sleepAfterStill) { this.animation.stop(); } @@ -288,6 +303,9 @@ class ZRender { * Wake up animation loop. But not render. */ wakeUp() { + if (this._disposed) { + return; + } this.animation.start(); // Reset the frame count. this._stillFrameAccum = 0; @@ -304,6 +322,9 @@ class ZRender { * Refresh hover immediately */ refreshHoverImmediately() { + if (this._disposed) { + return; + } this._needsRefreshHover = false; if (this.painter.refreshHover && this.painter.getType() === 'canvas') { this.painter.refreshHover(); @@ -318,6 +339,9 @@ class ZRender { width?: number| string height?: number | string }) { + if (this._disposed) { + return; + } opts = opts || {}; this.painter.resize(opts.width, opts.height); this.handler.resize(); @@ -327,20 +351,29 @@ class ZRender { * Stop and clear all animation immediately */ clearAnimation() { + if (this._disposed) { + return; + } this.animation.clear(); } /** * Get container width */ - getWidth(): number { + getWidth(): number | undefined { + if (this._disposed) { + return; + } return this.painter.getWidth(); } /** * Get container height */ - getHeight(): number { + getHeight(): number | undefined { + if (this._disposed) { + return; + } return this.painter.getHeight(); } @@ -349,6 +382,9 @@ class ZRender { * @param cursorStyle='default' 例如 crosshair */ setCursorStyle(cursorStyle: string) { + if (this._disposed) { + return; + } this.handler.setCursorStyle(cursorStyle); } @@ -361,7 +397,10 @@ class ZRender { findHover(x: number, y: number): { target: Displayable topTarget: Displayable - } { + } | undefined { + if (this._disposed) { + return; + } return this.handler.findHover(x, y); } @@ -370,7 +409,9 @@ class ZRender { on(eventName: string, eventHandler: WithThisType, unknown extends Ctx ? ZRenderType : Ctx>, context?: Ctx): this // eslint-disable-next-line max-len on(eventName: string, eventHandler: (...args: any) => any, context?: Ctx): this { - this.handler.on(eventName, eventHandler, context); + if (!this._disposed) { + this.handler.on(eventName, eventHandler, context); + } return this; } @@ -381,6 +422,9 @@ class ZRender { */ // eslint-disable-next-line max-len off(eventName?: string, eventHandler?: EventCallback) { + if (this._disposed) { + return; + } this.handler.off(eventName, eventHandler); } @@ -391,6 +435,9 @@ class ZRender { * @param event Event object */ trigger(eventName: string, event?: unknown) { + if (this._disposed) { + return; + } this.handler.trigger(eventName, event); } @@ -399,6 +446,9 @@ class ZRender { * Clear all objects and the canvas. */ clear() { + if (this._disposed) { + return; + } const roots = this.storage.getRoots(); for (let i = 0; i < roots.length; i++) { if (roots[i] instanceof Group) { @@ -413,6 +463,10 @@ class ZRender { * Dispose self. */ dispose() { + if (this._disposed) { + return; + } + this.animation.stop(); this.clear(); @@ -425,6 +479,8 @@ class ZRender { this.painter = this.handler = null; + this._disposed = true; + delInstance(this.id); } } @@ -482,6 +538,21 @@ export function registerPainter(name: string, Ctor: PainterBaseCtor) { painterCtors[name] = Ctor; } +export type ElementSSRData = zrUtil.HashMap; +export type ElementSSRDataGetter = (el: Element) => zrUtil.HashMap; + +let ssrDataGetter: ElementSSRDataGetter; + +export function getElementSSRData(el: Element): ElementSSRData { + if (typeof ssrDataGetter === 'function') { + return ssrDataGetter(el); + } +} + +export function registerSSRDataGetter(getter: ElementSSRDataGetter) { + ssrDataGetter = getter; +} + /** * @type {string} */ diff --git a/test/package.json b/test/package.json new file mode 100644 index 000000000..6a0d2ef2a --- /dev/null +++ b/test/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} \ No newline at end of file diff --git a/test/svg-backgroundColor.html b/test/svg-backgroundColor.html index b8603e37f..c6f6b08f3 100644 --- a/test/svg-backgroundColor.html +++ b/test/svg-backgroundColor.html @@ -42,7 +42,7 @@ } }); zr.add(txt); - + var linearGradient = new zrender.LinearGradient(); linearGradient.addColorStop(0, '#a598e1'); linearGradient.addColorStop(1, '#14c4ba'); @@ -95,7 +95,6 @@ else { bg = text = 'none'; } - console.log(text); zr.setBackgroundColor(bg); txt.setStyle({ text: 'SVG BackgroundColor: ' + text }); i++; diff --git a/test/svg-meta-data.html b/test/svg-meta-data.html new file mode 100644 index 000000000..c092cb1c7 --- /dev/null +++ b/test/svg-meta-data.html @@ -0,0 +1,91 @@ + + + + + Image + + + + + +
+ + + + \ No newline at end of file diff --git a/test/ut/spec/core/matrix.test.ts b/test/ut/spec/core/matrix.test.ts new file mode 100644 index 000000000..e3c609d74 --- /dev/null +++ b/test/ut/spec/core/matrix.test.ts @@ -0,0 +1,21 @@ +import { rotate } from '../../../../src/core/matrix'; + +describe('matrix', function () { + it('should rotate relative to a pivot point', function () { + const matrix = [ + 0.9659258262890683, 0.25881904510252074, -0.25881904510252074, 0.9659258262890683, + 40.213201392710246, -26.96358986452364 + ]; + const rad = -0.2617993877991494; + const pivot = [122.511, 139.243]; + + const result = rotate(matrix, matrix, rad, pivot); + + expect(result[0]).toBeCloseTo(0.8660254037844387, 5); + expect(result[1]).toBeCloseTo(0.49999999999999994, 5); + expect(result[2]).toBeCloseTo(-0.49999999999999994, 5); + expect(result[3]).toBeCloseTo(0.8660254037844387, 5); + expect(result[4]).toBeCloseTo(86.03486175696463, 5); + expect(result[5]).toBeCloseTo(-42.600475299156585, 5); + }); +});