This is an automated email from the ASF dual-hosted git repository. sushuang pushed a commit to branch custom-series-enhance in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit 1dffebc9bf846f8ea0d75fe97407e8d12a261eab Author: 100pah <[email protected]> AuthorDate: Thu May 28 17:26:56 2020 +0800 feature: support clipPath and clipPath animation in custom series. --- src/chart/custom.ts | 124 +++++++++++++++-------- test/custom-transition.html | 241 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 323 insertions(+), 42 deletions(-) diff --git a/src/chart/custom.ts b/src/chart/custom.ts index 7af3ec0..2bb85c9 100644 --- a/src/chart/custom.ts +++ b/src/chart/custom.ts @@ -105,6 +105,8 @@ interface CustomBaseElementOption extends Partial<Pick< info?: CustomExtraElementInfo; // `false` means remove the textContent. textContent?: CustomTextOption | false; + // `false` means remove the clipPath + clipPath?: CustomZRPathOption | false; // updateDuringAnimation during?(elProps: CustomDuringElProps): void; }; @@ -1180,7 +1182,7 @@ function createOrUpdate( group: ViewRootGroup, data: List<CustomSeriesModel> ): Element { - el = doCreateOrUpdate(el, dataIndex, elOption, seriesModel, group, data, true); + el = doCreateOrUpdate(el, dataIndex, elOption, seriesModel, group, true); el && data.setItemGraphicEl(dataIndex, el); return el; @@ -1192,7 +1194,6 @@ function doCreateOrUpdate( elOption: CustomElementOption, seriesModel: CustomSeriesModel, group: ViewRootGroup, - data: List<CustomSeriesModel>, isRoot: boolean ): Element { @@ -1213,37 +1214,12 @@ function doCreateOrUpdate( } elOption = elOption || {} as CustomElementOption; - const elOptionType = elOption.type; - const elOptionShape = (elOption as CustomZRPathOption).shape; - const elOptionStyle = elOption.style; let toBeReplacedIdx = -1; - if (el) { - const elInner = inner(el); - if ( - // || elOption.$merge === false - // If `elOptionType` is `null`, follow the merge principle. - (elOptionType != null - && elOptionType !== elInner.customGraphicType - ) - || (elOptionType === 'path' - && hasOwnPathData(elOptionShape) - && getPathData(elOptionShape) !== elInner.customPathData - ) - || (elOptionType === 'image' - && zrUtil.hasOwn(elOptionStyle, 'image') - && (elOptionStyle as CustomImageOption['style']).image !== elInner.customImagePath - ) - // // FIXME test and remove this restriction? - // || (elOptionType === 'text' - // && zrUtil.hasOwn(elOptionStyle, 'text') - // && (elOptionStyle as TextStyleProps).text !== elInner.customText - // ) - ) { - // Should keep at the original index, otherwise "merge by index" will be incorrect. - toBeReplacedIdx = group.childrenRef().indexOf(el); - el = null; - } + if (el && doesElNeedRecreate(el, elOption)) { + // Should keep at the original index, otherwise "merge by index" will be incorrect. + toBeReplacedIdx = group.childrenRef().indexOf(el); + el = null; } const isInit = !el; @@ -1252,6 +1228,7 @@ function doCreateOrUpdate( el = createEl(elOption); } else { + // FIMXE:NEXT unified clearState? // If in some case the performance issue arised, consider // do not clearState but update cached normal state directly. el.clearStates(); @@ -1265,6 +1242,10 @@ function doCreateOrUpdate( el, dataIndex, elOption, seriesModel, isInit, attachedTxInfoTmp ); + doCreateOrUpdateClipPath( + el, dataIndex, elOption, seriesModel, isInit + ); + const stateOptEmphasis = retrieveStateOption(elOption, EMPHASIS); const styleOptEmphasis = retrieveStyleOptionOnState(elOption, stateOptEmphasis, EMPHASIS); @@ -1273,9 +1254,9 @@ function doCreateOrUpdate( updateZ(el, elOption, seriesModel, attachedTxInfoTmp); - if (elOptionType === 'group') { + if (elOption.type === 'group') { mergeChildren( - el as graphicUtil.Group, dataIndex, elOption as CustomGroupOption, seriesModel, data + el as graphicUtil.Group, dataIndex, elOption as CustomGroupOption, seriesModel ); } @@ -1289,6 +1270,72 @@ function doCreateOrUpdate( return el; } +// `el` must not be null/undefined. +function doesElNeedRecreate(el: Element, elOption: CustomElementOption): boolean { + const elInner = inner(el); + const elOptionType = elOption.type; + const elOptionShape = (elOption as CustomZRPathOption).shape; + const elOptionStyle = elOption.style; + return ( + // || elOption.$merge === false + // If `elOptionType` is `null`, follow the merge principle. + (elOptionType != null + && elOptionType !== elInner.customGraphicType + ) + || (elOptionType === 'path' + && hasOwnPathData(elOptionShape) + && getPathData(elOptionShape) !== elInner.customPathData + ) + || (elOptionType === 'image' + && zrUtil.hasOwn(elOptionStyle, 'image') + && (elOptionStyle as CustomImageOption['style']).image !== elInner.customImagePath + ) + // // FIXME test and remove this restriction? + // || (elOptionType === 'text' + // && zrUtil.hasOwn(elOptionStyle, 'text') + // && (elOptionStyle as TextStyleProps).text !== elInner.customText + // ) + ); +} + +function doCreateOrUpdateClipPath( + el: Element, + dataIndex: number, + elOption: CustomElementOption, + seriesModel: CustomSeriesModel, + isInit: boolean +): void { + // Based on the "merge" principle, if no clipPath provided, + // do nothing. The exists clip will be totally removed only if + // `el.clipPath` is `false`. Otherwise it will be merged/replaced. + const clipPathOpt = elOption.clipPath; + if (clipPathOpt === false) { + if (el && el.getClipPath()) { + el.removeClipPath(); + } + } + else if (clipPathOpt) { + let clipPath = el.getClipPath(); + if (clipPath && doesElNeedRecreate(clipPath, clipPathOpt)) { + clipPath = null; + } + if (!clipPath) { + clipPath = createEl(clipPathOpt) as graphicUtil.Path; + if (__DEV__) { + zrUtil.assert( + clipPath instanceof graphicUtil.Path, + 'Only any type of `path` can be used in `clipPath`, rather than ' + clipPath.type + '.' + ); + } + el.setClipPath(clipPath); + } + updateElNormal( + clipPath, dataIndex, clipPathOpt, null, null, seriesModel, isInit, false + ); + } + // If not define `clipPath` in option, do nothing unnecessary. +} + function doCreateOrUpdateAttachedTx( el: Element, dataIndex: number, @@ -1440,8 +1487,7 @@ function mergeChildren( el: graphicUtil.Group, dataIndex: number, elOption: CustomGroupOption, - seriesModel: CustomSeriesModel, - data: List<CustomSeriesModel> + seriesModel: CustomSeriesModel ): void { const newChildren = elOption.children; @@ -1462,8 +1508,7 @@ function mergeChildren( newChildren: newChildren || [], dataIndex: dataIndex, seriesModel: seriesModel, - group: el, - data: data + group: el }); return; } @@ -1480,7 +1525,6 @@ function mergeChildren( newChildren[index], seriesModel, el, - data, false ); } @@ -1497,8 +1541,7 @@ type DiffGroupContext = { newChildren: CustomElementOption[], dataIndex: number, seriesModel: CustomSeriesModel, - group: graphicUtil.Group, - data: List<CustomSeriesModel> + group: graphicUtil.Group }; function diffGroupChildren(context: DiffGroupContext) { (new DataDiffer( @@ -1534,7 +1577,6 @@ function processAddUpdate( childOption, context.seriesModel, context.group, - context.data, false ); } diff --git a/test/custom-transition.html b/test/custom-transition.html index 77a6aab..2d8c718 100644 --- a/test/custom-transition.html +++ b/test/custom-transition.html @@ -28,6 +28,7 @@ under the License. <script src="lib/jquery.min.js"></script> <script src="lib/facePrint.js"></script> <script src="lib/testHelper.js"></script> + <script src="./custom-transition-texture.js"></script> <link rel="stylesheet" href="lib/reset.css" /> </head> <body> @@ -38,7 +39,8 @@ under the License. <div id="init-animation-additive"></div> <div id="spiral-fixed-extent"></div> <div id="spiral-dynamic-extent"></div> - <div id="texture-bar"></div> + <div id="texture-bar-by-clipPath"></div> + <!-- <div id="texture-bar-texture-maker"></div> --> @@ -623,5 +625,242 @@ under the License. + + + + + + <script> + require(['echarts'], function (echarts) { + var _animationDuration = 1000; + var _animationDurationUpdate = 1000; + var _animationEasingUpdate = 'elasticOut'; + var _datasourceList = [ + [[1, 156]], + [[1, 54]], + [[1, 131]], + [[1, 32]], + [[1, 103]], + [[1, 66]], + ]; + var _valOnRadianMax = 200; + var _outerRadius = 100; + var _innerRadius = 85; + var _pointerInnerRadius = 40; + var _insidePanelRadius = 65; + var _currentDataIndex = 0; + + function renderItem(params, api) { + var children = []; + var dataIdx = params.dataIndex; + var valOnRadian = api.value(1); + var coords = api.coord([api.value(0), valOnRadian]); + var polarEndRadian = coords[3]; + var imageStyle = { + image: window.BAR_ROUND_GRADIENT_TEXTURE, + x: params.coordSys.cx - _outerRadius, + y: params.coordSys.cy - _outerRadius, + width: _outerRadius * 2, + height: _outerRadius * 2 + }; + + return { + type: 'group', + children: [{ + type: 'image', + style: imageStyle, + clipPath: { + type: 'sector', + shape: { + cx: params.coordSys.cx, + cy: params.coordSys.cy, + r: _outerRadius, + r0: _innerRadius, + startAngle: 0, + // polor: anticlockwise-positive radian + // sector: clockwise-positive radian + endAngle: -polarEndRadian + }, + } + }, { + type: 'image', + style: imageStyle, + clipPath: { + type: 'polygon', + shape: { + points: makePionterPoints(params, polarEndRadian), + polarEndRadian: polarEndRadian + }, + during: function (elProps) { + elProps.shape.points = makePionterPoints(params, elProps.shape.polarEndRadian); + } + }, + }, { + type: 'circle', + shape: { + cx: params.coordSys.cx, + cy: params.coordSys.cy, + r: _insidePanelRadius + }, + style: { + fill: '#fff', + shadowBlur: 25, + shadowOffsetX: 0, + shadowOffsetY: 0, + shadowColor: 'rgb(0,0,50)' + } + }, { + type: 'text', + shape: { + valOnRadian: valOnRadian + }, + style: { + text: makeText(valOnRadian), + fontSize: 40, + x: params.coordSys.cx, + y: params.coordSys.cy, + fill: 'rgb(0,50,190)', + align: 'center', + verticalAlign: 'middle', + }, + during: function (elProps) { + elProps.style.text = makeText(elProps.shape.valOnRadian); + } + }] + }; + } + + function convertToPolarPoint(renderItemParams, radius, radian) { + return [ + Math.cos(radian) * radius + renderItemParams.coordSys.cx, + -Math.sin(radian) * radius + renderItemParams.coordSys.cy + ]; + } + + function makePionterPoints(renderItemParams, polarEndRadian) { + return [ + convertToPolarPoint(renderItemParams, _outerRadius, polarEndRadian), + convertToPolarPoint(renderItemParams, _outerRadius, polarEndRadian + Math.PI * 0.03), + convertToPolarPoint(renderItemParams, _pointerInnerRadius, polarEndRadian) + ]; + } + + function makeText(valOnRadian) { + return (valOnRadian / _valOnRadianMax * 100).toFixed(0) + '%' + } + + var option = { + animationDuration: _animationDuration, + animationDurationUpdate: _animationDurationUpdate, + animationEasingUpdate: _animationEasingUpdate, + dataset: { + source: _datasourceList[_currentDataIndex] + }, + tooltip: {}, + angleAxis: { + type: 'value', + startAngle: 0, + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { show: false }, + splitLine: { show: false }, + min: 0, + max: _valOnRadianMax + }, + radiusAxis: { + type: 'value', + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { show: false }, + splitLine: { show: false } + }, + polar: {}, + series: [{ + type: 'custom', + coordinateSystem: 'polar', + renderItem: renderItem + }] + }; + + var chart = testHelper.create(echarts, 'texture-bar-by-clipPath', { + title: [ + 'Angle gradient | clipPath animation', + 'click **next** to check the transition animation in both bar and text.' + ], + option: option, + height: 300, + buttons: [{ + text: 'next', + onclick: function () { + _currentDataIndex++; + _currentDataIndex >= _datasourceList.length && (_currentDataIndex = 0); + chart.setOption({ + dataset: { + source: _datasourceList[_currentDataIndex] + } + }); + } + }] + }); + }); + </script> + + + <script> + require(['echarts'], function (echarts) { + var chart = testHelper.create(echarts, 'texture-bar-texture-maker', { + title: [], + width: 200, + height: 200, + option: {}, + buttons: [{ + text: 'dataURL', + onclick: function () { + console.log(chart.getDataURL({ + type: 'png', + backgroundColor: 'rgba(0,0,0,0)' + })); + } + }] + }); + if (!chart) { + return; + } + + var zr = chart.getZr(); + var eles = []; + var extent = [0.0, 0.95]; + var count = 200; + var unit = (extent[1] - extent[0]) / count; + var baseColor = 'rgb(0,0,255)'; + for (var i = 0; i < count; i++) { + var oo = extent[0] + (count - i) * unit; + var color = echarts.color.modifyHSL(baseColor, null, null, oo); + var startAngle = 2 * Math.PI / count * i; + var endAngle = Math.min((2 * Math.PI / count * (i + 1) + 0.05), Math.PI * 2); + zr.add(new echarts.graphic.Sector({ + type: 'sector', + shape: { + cx: 100, + cy: 100, + r: 100, + r0: 60, + startAngle: startAngle, + endAngle: endAngle + }, + style: { + fill: color + } + })); + } + }); + </script> + + + + + + + </body> </html> \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
