You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2020/11/01 09:39:09 UTC
[incubator-echarts] 04/05: feature: [decal] support decal to custom
series.
This is an automated email from the ASF dual-hosted git repository.
sushuang pushed a commit to branch decal-custom
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit 59e0ad35a53d6e6b31dfcf53d32c13c1811997b0
Author: 100pah <su...@gmail.com>
AuthorDate: Sun Nov 1 17:33:30 2020 +0800
feature: [decal] support decal to custom series.
---
src/chart/custom.ts | 111 +++++++++++++++++------
test/ut/jest.config.js | 4 +-
test/ut/spec/component/graphic/setOption.test.ts | 1 -
test/ut/spec/series/custom.test.ts | 72 +++++++++++++++
4 files changed, 156 insertions(+), 32 deletions(-)
diff --git a/src/chart/custom.ts b/src/chart/custom.ts
index 4d4a1a6..8db0f0f 100644
--- a/src/chart/custom.ts
+++ b/src/chart/custom.ts
@@ -53,7 +53,9 @@ import {
BlurScope,
SeriesDataType,
OrdinalRawValue,
- PayloadAnimationPart
+ PayloadAnimationPart,
+ DecalObject,
+ InnerDecalObject
} from '../util/types';
import Element, { ElementProps, ElementTextConfig } from 'zrender/src/Element';
import prepareCartesian2d from '../coord/cartesian/prepareCustom';
@@ -69,7 +71,7 @@ import ExtensionAPI from '../ExtensionAPI';
import Displayable from 'zrender/src/graphic/Displayable';
import Axis2D from '../coord/cartesian/Axis2D';
import { RectLike } from 'zrender/src/core/BoundingRect';
-import { PathProps } from 'zrender/src/graphic/Path';
+import { PathProps, PathStyleProps } from 'zrender/src/graphic/Path';
import { ImageStyleProps } from 'zrender/src/graphic/Image';
import { CoordinateSystem } from '../coord/CoordinateSystem';
import { TextStyleProps } from 'zrender/src/graphic/Text';
@@ -89,6 +91,8 @@ import {
} from 'zrender/src/tool/morphPath';
import { AnimationEasing } from 'zrender/src/animation/easing';
import * as matrix from 'zrender/src/core/matrix';
+import { PatternObject } from 'zrender/src/graphic/Pattern';
+import { createOrUpdatePatternFromDecal } from '../util/decal';
const inner = makeInner<{
@@ -187,6 +191,11 @@ interface CustomGroupOption extends CustomBaseElementOption {
}
interface CustomZRPathOption extends CustomDisplayableOption, ShapeMorphingOption {
shape?: PathProps['shape'] & TransitionAnyOption;
+ style?: CustomDisplayableOption['style'] & {
+ decal?: DecalObject;
+ // Only internal usage. Any user specified value will be overwritten.
+ __decalPattern?: PatternObject;
+ };
}
interface CustomSVGPathOption extends CustomDisplayableOption, ShapeMorphingOption {
type: 'path';
@@ -219,14 +228,19 @@ type CustomElementOption = CustomZRPathOption | CustomSVGPathOption | CustomImag
type CustomElementOptionOnState = CustomDisplayableOptionOnState | CustomImageOptionOnState;
-interface CustomSeriesRenderItemAPI extends
+export interface CustomSeriesRenderItemAPI extends
CustomSeriesRenderItemCoordinateSystemAPI,
Pick<ExtensionAPI, 'getWidth' | 'getHeight' | 'getZr' | 'getDevicePixelRatio'> {
value(dim: DimensionLoose, dataIndexInside?: number): ParsedValue;
ordinalRawValue(dim: DimensionLoose, dataIndexInside?: number): ParsedValue | OrdinalRawValue;
style(userProps?: ZRStyleProps, dataIndexInside?: number): ZRStyleProps;
styleEmphasis(userProps?: ZRStyleProps, dataIndexInside?: number): ZRStyleProps;
- visual(visualType: string, dataIndexInside?: number): ReturnType<List['getItemVisual']>;
+ visual<VT extends NonStyleVisualProps | StyleVisualProps>(
+ visualType: VT,
+ dataIndexInside?: number
+ ): VT extends NonStyleVisualProps ? DefaultDataVisual[VT]
+ : VT extends StyleVisualProps ? PathStyleProps[typeof STYLE_VISUAL_TYPE[VT]]
+ : void;
barLayout(opt: Omit<Parameters<typeof getLayoutOnAxis>[0], 'axis'>): ReturnType<typeof getLayoutOnAxis>;
currentSeriesIndices(): ReturnType<GlobalModel['getCurrentSeriesIndices']>;
font(opt: Parameters<typeof labelStyleHelper.getFont>[0]): ReturnType<typeof labelStyleHelper.getFont>;
@@ -245,7 +259,7 @@ interface CustomSeriesRenderItemCoordinateSystemAPI {
dataItem: OptionDataValue | OptionDataValue[]
): number | number[];
}
-interface CustomSeriesRenderItemParams {
+export interface CustomSeriesRenderItemParams {
context: Dictionary<unknown>;
seriesId: string;
seriesName: string;
@@ -301,15 +315,18 @@ const STYLE_VISUAL_TYPE = {
color: 'fill',
borderColor: 'stroke'
} as const;
+type StyleVisualProps = keyof typeof STYLE_VISUAL_TYPE;
-const VISUAL_PROPS = {
+const NON_STYLE_VISUAL_PROPS = {
symbol: 1,
symbolSize: 1,
symbolKeepAspect: 1,
legendSymbol: 1,
visualMeta: 1,
- liftZ: 1
+ liftZ: 1,
+ decal: 1
} as const;
+type NonStyleVisualProps = keyof typeof NON_STYLE_VISUAL_PROPS;
const EMPHASIS = 'emphasis' as const;
const NORMAL = 'normal' as const;
@@ -482,7 +499,7 @@ class CustomSeriesView extends ChartView {
});
data.each(function (newIdx) {
createOrUpdateItem(
- null, newIdx, renderItem(newIdx, payload), customSeries, group, data, null
+ api, null, newIdx, renderItem(newIdx, payload), customSeries, group, data, null
);
});
}
@@ -500,7 +517,7 @@ class CustomSeriesView extends ChartView {
))
.add(function (newIdx) {
createOrUpdateItem(
- null, newIdx, renderItem(newIdx, payload), customSeries, group,
+ api, null, newIdx, renderItem(newIdx, payload), customSeries, group,
data, null
);
})
@@ -523,7 +540,7 @@ class CustomSeriesView extends ChartView {
oldEl = null;
}
createOrUpdateItem(
- oldEl, newIdx, renderItem(newIdx, payload), customSeries, group,
+ api, oldEl, newIdx, renderItem(newIdx, payload), customSeries, group,
data, morphPreparation
);
morphPreparation.applyMorphing();
@@ -536,7 +553,7 @@ class CustomSeriesView extends ChartView {
removeElementDirectly(oldEl, group);
}
createOrUpdateItem(
- null, newIdx, renderItem(newIdx, payload), customSeries, group,
+ api, null, newIdx, renderItem(newIdx, payload), customSeries, group,
data, morphPreparation
);
morphPreparation.applyMorphing();
@@ -550,7 +567,7 @@ class CustomSeriesView extends ChartView {
for (let i = 0; i < newLen; i++) {
createOrUpdateItem(
- null, newIndices[i], renderItem(newIndices[i], payload), customSeries, group,
+ api, null, newIndices[i], renderItem(newIndices[i], payload), customSeries, group,
data, morphPreparation
);
}
@@ -599,7 +616,7 @@ class CustomSeriesView extends ChartView {
}
for (let idx = params.start; idx < params.end; idx++) {
const el = createOrUpdateItem(
- null, idx, renderItem(idx, payload), customSeries, this.group, data, null
+ null, null, idx, renderItem(idx, payload), customSeries, this.group, data, null
);
el.traverse(setIncrementalAndHoverLayer);
}
@@ -779,6 +796,8 @@ function createEl(elOption: CustomElementOption): Element {
* @return if `isMorphTo`, return `allPropsFinal`.
*/
function updateElNormal(
+ // Can be null/undefined
+ api: ExtensionAPI,
el: Element,
// Whether be a morph target.
isMorphTo: boolean,
@@ -823,6 +842,17 @@ function updateElNormal(
);
}
+ if (styleOpt) {
+ let decalPattern;
+ const decalObj = isPath(el) ? (styleOpt as CustomZRPathOption['style']).decal : null;
+ if (api && decalObj) {
+ (decalObj as InnerDecalObject).dirty = true;
+ decalPattern = createOrUpdatePatternFromDecal(decalObj, api);
+ }
+ // Always overwrite in case user specify this prop.
+ (styleOpt as CustomZRPathOption['style']).__decalPattern = decalPattern;
+ }
+
!isMorphTo && prepareStyleTransitionFrom(el, null, elOption, styleOpt, transFromProps, isInit);
if (elDisplayable) {
@@ -861,10 +891,22 @@ function applyPropsFinal(
const elDisplayable = el.isGroup ? null : el as Displayable;
if (elDisplayable && styleOpt) {
+
+ const decalPattern = (styleOpt as CustomZRPathOption['style']).__decalPattern;
+ let originalDecalObj;
+ if (decalPattern) {
+ originalDecalObj = (styleOpt as CustomZRPathOption['style']).decal;
+ (styleOpt as any).decal = decalPattern;
+ }
+
// PENDING: here the input style object is used directly.
// Good for performance but bad for compatibility control.
elDisplayable.useStyle(styleOpt);
+ if (decalPattern) {
+ (styleOpt as CustomZRPathOption['style']).decal = originalDecalObj;
+ }
+
// When style object changed, how to trade the existing animation?
// It is probably conplicated and not needed to cover all the cases.
// But still need consider the case:
@@ -1794,22 +1836,25 @@ function makeRenderItem(
* @public
* @param dataIndexInside by default `currDataIndexInside`.
*/
- function visual(
- visualType: keyof DefaultDataVisual,
+ function visual<VT extends NonStyleVisualProps | StyleVisualProps>(
+ visualType: VT,
dataIndexInside?: number
- ): ReturnType<List['getItemVisual']> {
+ ): VT extends NonStyleVisualProps ? DefaultDataVisual[VT]
+ : VT extends StyleVisualProps ? PathStyleProps[typeof STYLE_VISUAL_TYPE[VT]]
+ : never {
+
dataIndexInside == null && (dataIndexInside = currDataIndexInside);
if (hasOwn(STYLE_VISUAL_TYPE, visualType)) {
const style = data.getItemVisual(dataIndexInside, 'style');
return style
- ? style[STYLE_VISUAL_TYPE[visualType as keyof typeof STYLE_VISUAL_TYPE]] as any
+ ? style[STYLE_VISUAL_TYPE[visualType as StyleVisualProps]] as any
: null;
}
// Only support these visuals. Other visual might be inner tricky
// for performance (like `style`), do not expose to users.
- if (hasOwn(VISUAL_PROPS, visualType)) {
- return data.getItemVisual(dataIndexInside, visualType);
+ if (hasOwn(NON_STYLE_VISUAL_PROPS, visualType)) {
+ return data.getItemVisual(dataIndexInside, visualType as NonStyleVisualProps) as any;
}
}
@@ -1858,6 +1903,7 @@ function wrapEncodeDef(data: List<CustomSeriesModel>): Dictionary<number[]> {
}
function createOrUpdateItem(
+ api: ExtensionAPI,
el: Element,
dataIndex: number,
elOption: CustomElementOption,
@@ -1878,7 +1924,7 @@ function createOrUpdateItem(
removeElementDirectly(el, group);
return;
}
- el = doCreateOrUpdateEl(el, dataIndex, elOption, seriesModel, group, true, morphPreparation);
+ el = doCreateOrUpdateEl(api, el, dataIndex, elOption, seriesModel, group, true, morphPreparation);
el && data.setItemGraphicEl(dataIndex, el);
enableHoverEmphasis(el, elOption.focus, elOption.blurScope);
@@ -1887,6 +1933,7 @@ function createOrUpdateItem(
}
function doCreateOrUpdateEl(
+ api: ExtensionAPI,
el: Element,
dataIndex: number,
elOption: CustomElementOption,
@@ -1951,6 +1998,7 @@ function doCreateOrUpdateEl(
);
const pendingAllPropsFinal = updateElNormal(
+ api,
el,
thisElIsMorphTo,
dataIndex,
@@ -1979,7 +2027,7 @@ function doCreateOrUpdateEl(
if (elOption.type === 'group') {
mergeChildren(
- el as graphicUtil.Group, dataIndex, elOption as CustomGroupOption, seriesModel, morphPreparation
+ api, el as graphicUtil.Group, dataIndex, elOption as CustomGroupOption, seriesModel, morphPreparation
);
}
@@ -2052,7 +2100,7 @@ function doCreateOrUpdateClipPath(
el.setClipPath(clipPath);
}
updateElNormal(
- clipPath, null, dataIndex, clipPathOpt, null, null, seriesModel, isInit, false
+ null, clipPath, null, dataIndex, clipPathOpt, null, null, seriesModel, isInit, false
);
}
// If not define `clipPath` in option, do nothing unnecessary.
@@ -2105,7 +2153,7 @@ function doCreateOrUpdateAttachedTx(
const txConStlOptNormal = txConOptNormal && txConOptNormal.style;
updateElNormal(
- textContent, null, dataIndex, txConOptNormal, txConStlOptNormal, null, seriesModel, isInit, true
+ null, textContent, null, dataIndex, txConOptNormal, txConStlOptNormal, null, seriesModel, isInit, true
);
for (let i = 0; i < STATES.length; i++) {
const stateName = STATES[i];
@@ -2212,6 +2260,7 @@ function retrieveStyleOptionOnState(
// child (otherwise the total indicies of the children array have to be modified).
// User can remove a single child by set its `ignore` as `true`.
function mergeChildren(
+ api: ExtensionAPI,
el: graphicUtil.Group,
dataIndex: number,
elOption: CustomGroupOption,
@@ -2233,6 +2282,7 @@ function mergeChildren(
if (byName) {
diffGroupChildren({
+ api: api,
oldChildren: el.children() || [],
newChildren: newChildren || [],
dataIndex: dataIndex,
@@ -2250,6 +2300,7 @@ function mergeChildren(
let index = 0;
for (; index < newLen; index++) {
newChildren[index] && doCreateOrUpdateEl(
+ api,
el.childAt(index),
dataIndex,
newChildren[index],
@@ -2268,12 +2319,13 @@ function mergeChildren(
}
type DiffGroupContext = {
- oldChildren: Element[],
- newChildren: CustomElementOption[],
- dataIndex: number,
- seriesModel: CustomSeriesModel,
- group: graphicUtil.Group,
- morphPreparation: MorphPreparation
+ api: ExtensionAPI;
+ oldChildren: Element[];
+ newChildren: CustomElementOption[];
+ dataIndex: number;
+ seriesModel: CustomSeriesModel;
+ group: graphicUtil.Group;
+ morphPreparation: MorphPreparation;
};
function diffGroupChildren(context: DiffGroupContext) {
(new DataDiffer(
@@ -2304,6 +2356,7 @@ function processAddUpdate(
const child = oldIndex != null ? context.oldChildren[oldIndex] : null;
doCreateOrUpdateEl(
+ context.api,
child,
context.dataIndex,
childOption,
diff --git a/test/ut/jest.config.js b/test/ut/jest.config.js
index ef57ca0..bc09902 100644
--- a/test/ut/jest.config.js
+++ b/test/ut/jest.config.js
@@ -35,10 +35,10 @@ module.exports = {
testMatch: [
'**/spec/api/*.test.ts',
'**/spec/component/**/*.test.ts',
+ '**/spec/series/**/*.test.ts',
'**/spec/data/*.test.ts',
'**/spec/model/*.test.ts',
'**/spec/scale/*.test.ts',
- '**/spec/util/*.test.ts',
- '**/spec/component/graphic/setOption.test.ts'
+ '**/spec/util/*.test.ts'
]
};
diff --git a/test/ut/spec/component/graphic/setOption.test.ts b/test/ut/spec/component/graphic/setOption.test.ts
index b56f8b8..6dddca2 100755
--- a/test/ut/spec/component/graphic/setOption.test.ts
+++ b/test/ut/spec/component/graphic/setOption.test.ts
@@ -1,4 +1,3 @@
-
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
diff --git a/test/ut/spec/series/custom.test.ts b/test/ut/spec/series/custom.test.ts
new file mode 100644
index 0000000..ec4565e
--- /dev/null
+++ b/test/ut/spec/series/custom.test.ts
@@ -0,0 +1,72 @@
+/*
+* 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.
+*/
+
+import { EChartsType } from '../../../../src/echarts';
+import { createChart } from '../../core/utHelper';
+import { ZRColor } from '../../../../src/util/types';
+import { CustomSeriesRenderItemAPI, CustomSeriesRenderItemParams } from '../../../../src/chart/custom';
+
+
+describe('custom_series', function () {
+
+ let chart: EChartsType;
+ beforeEach(function () {
+ chart = createChart();
+ });
+
+ afterEach(function () {
+ chart.dispose();
+ });
+
+
+ it('visual_palette', function () {
+ const colors = ['#111111', '#222222', '#333333'];
+ const resultPaletteColors: ZRColor[] = [];
+
+ function renderItem(params: CustomSeriesRenderItemParams, api: CustomSeriesRenderItemAPI) {
+ const color = api.visual('color');
+ resultPaletteColors.push(color);
+ return {
+ type: 'circle'
+ };
+ }
+
+ chart.setOption({
+ color: colors,
+ xAxis: { data: ['a'] },
+ yAxis: {},
+ series: [{
+ type: 'custom',
+ renderItem: renderItem,
+ data: [11]
+ }, {
+ type: 'custom',
+ renderItem: renderItem,
+ data: [22]
+ }, {
+ type: 'custom',
+ renderItem: renderItem,
+ data: [33]
+ }]
+ }, true);
+
+ expect(resultPaletteColors).toEqual(colors);
+ });
+
+});
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org