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/06/02 17:54:35 UTC
[incubator-echarts] 05/10: feature: add duration animation for
custom series.
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 c56e2dcd1e553780b91c6a004c1a06b770f082bf
Author: 100pah <su...@gmail.com>
AuthorDate: Wed May 13 04:08:33 2020 +0800
feature: add duration animation for custom series.
---
src/chart/custom.ts | 54 +++++-
test/custom-feature.html | 442 ++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 494 insertions(+), 2 deletions(-)
diff --git a/src/chart/custom.ts b/src/chart/custom.ts
index 3ed15b9..a58633d 100644
--- a/src/chart/custom.ts
+++ b/src/chart/custom.ts
@@ -84,6 +84,8 @@ const inner = makeInner<{
customImagePath: CustomImageOption['style']['image'];
customText: string;
txConZ2Set: number;
+ orginalDuring: Element['updateDuringAnimation'];
+ customDuring: CustomZRPathOption['during'];
}, Element>();
type CustomExtraElementInfo = Dictionary<unknown>;
@@ -103,6 +105,8 @@ interface CustomBaseElementOption extends Partial<Pick<
info?: CustomExtraElementInfo;
// `false` means remove the textContent.
textContent?: CustomTextOption | false;
+ // updateDuringAnimation
+ during?(elProps: CustomDuringElProps): void;
};
interface CustomDisplayableOption extends CustomBaseElementOption, Partial<Pick<
Displayable, 'zlevel' | 'z' | 'z2' | 'invisible'
@@ -128,6 +132,9 @@ interface CustomGroupOption extends CustomBaseElementOption {
}
interface CustomZRPathOption extends CustomDisplayableOption, Pick<PathProps, 'shape'> {
}
+interface CustomDuringElProps extends Partial<Pick<Element, TransformProps>> {
+ shape?: PathProps['shape'];
+}
interface CustomSVGPathOption extends CustomDisplayableOption {
type: 'path';
shape?: {
@@ -277,7 +284,7 @@ const Z2_SPECIFIED_BIT = {
emphasis: 1
} as const;
-
+const tmpDuringElProps = {} as CustomDuringElProps;
export type PrepareCustomInfo = (coordSys: CoordinateSystem) => {
@@ -640,6 +647,19 @@ function updateElNormal(
zrUtil.hasOwn(elOption, 'silent') && (el.silent = elOption.silent);
zrUtil.hasOwn(elOption, 'ignore') && (el.ignore = elOption.ignore);
+ const customDuringMounted = el.updateDuringAnimation === elUpdateDuringAnimation;
+ if (elOption.during) {
+ const innerEl = inner(el);
+ if (!customDuringMounted) {
+ innerEl.orginalDuring = el.updateDuringAnimation;
+ el.updateDuringAnimation = elUpdateDuringAnimation;
+ }
+ innerEl.customDuring = elOption.during;
+ }
+ else if (customDuringMounted) {
+ el.updateDuringAnimation = inner(el).orginalDuring;
+ }
+
if (!isTextContent) {
// `elOption.info` enables user to mount some info on
// elements and use them in event handlers.
@@ -650,6 +670,38 @@ function updateElNormal(
el.markRedraw();
}
+function elUpdateDuringAnimation(this: graphicUtil.Path, key: string): void {
+ const innerEl = inner(this);
+ // FIXME `this.markRedraw();` directly ?
+ innerEl.orginalDuring.call(this, key);
+ const customDuring = innerEl.customDuring;
+
+ // Only provide these props. Usually other props do not need to be
+ // changed in animation during.
+ // Do not give `this` to user util really needed in future.
+ // Props in `shape` can be modified directly in the during callback.
+ tmpDuringElProps.shape = this.shape;
+ tmpDuringElProps.x = this.x;
+ tmpDuringElProps.y = this.y;
+ tmpDuringElProps.scaleX = this.scaleX;
+ tmpDuringElProps.scaleX = this.scaleY;
+ tmpDuringElProps.originX = this.originX;
+ tmpDuringElProps.originY = this.originY;
+ tmpDuringElProps.rotation = this.rotation;
+
+ customDuring(tmpDuringElProps);
+
+ tmpDuringElProps.shape !== this.shape && (this.shape = tmpDuringElProps.shape);
+ // Consider prop on prototype.
+ tmpDuringElProps.x !== this.x && (this.x = tmpDuringElProps.x);
+ tmpDuringElProps.y !== this.y && (this.y = tmpDuringElProps.y);
+ tmpDuringElProps.scaleX !== this.scaleX && (this.scaleX = tmpDuringElProps.scaleX);
+ tmpDuringElProps.scaleY !== this.scaleY && (this.scaleY = tmpDuringElProps.scaleY);
+ tmpDuringElProps.originX !== this.originX && (this.originX = tmpDuringElProps.originX);
+ tmpDuringElProps.originY !== this.originY && (this.originY = tmpDuringElProps.originY);
+ tmpDuringElProps.rotation !== this.rotation && (this.rotation = tmpDuringElProps.rotation);
+}
+
function updateElOnState(
state: DisplayStateNonNormal,
el: Element,
diff --git a/test/custom-feature.html b/test/custom-feature.html
index 03457ed..56e106e 100644
--- a/test/custom-feature.html
+++ b/test/custom-feature.html
@@ -38,7 +38,10 @@ under the License.
<div id="main0"></div>
<div id="main2"></div>
<div id="main3"></div>
- <!-- <div id="main1"></div> -->
+ <div id="init-animation-additive"></div>
+ <!-- <div id="spiral"></div> -->
+ <div id="spiral2"></div>
+ <div id="texture-bar"></div>
<script>
@@ -365,6 +368,443 @@ under the License.
+ <script>
+
+ require(['echarts'], function (echarts) {
+
+ var animationDuration = 5000;
+ var animationDurationUpdate = 4000;
+ var option = {
+ xAxis: {},
+ yAxis: {},
+ dataZoom: [
+ { type: 'slider' },
+ { type: 'inside' }
+ ],
+ animationDuration: animationDuration,
+ animationDurationUpdate: animationDurationUpdate,
+ series: [{
+ type: 'custom',
+ renderItem: function (params, api) {
+ return {
+ type: 'group',
+ position: api.coord([api.value(0), api.value(1)]),
+ children: [{
+ type: 'rect',
+ shape: {
+ x: -50,
+ y: 50,
+ width: 100,
+ height: 150,
+ r: 10
+ },
+ style: {
+ fill: 'rgba(102,241,98,0.9)'
+ }
+ }, {
+ type: 'circle',
+ shape: {
+ cx: -50,
+ cy: 50,
+ r: 30
+ },
+ style: {
+ fill: 'blue'
+ },
+ textContent: {
+ text: 'data',
+ style: {
+ fill: '#fff'
+ }
+ }
+ }]
+ };
+ },
+ data: [[121, 333], [29, 312]]
+ }]
+ };
+
+ var chart = testHelper.create(echarts, 'init-animation-additive', {
+ title: [
+ 'Style merge:',
+ '(1) dataZoom hide a data item, and then show it, ensure the fade in animation normal.',
+ '(2) click button to setOption merge.'
+ ],
+ option: option,
+ info: {
+ animationDuration: animationDuration,
+ animationDurationUpdate: animationDurationUpdate
+ },
+ buttons: [{
+ text: 'merge style: border become blue, but background not changed',
+ onclick: function () {
+ chart.setOption({
+ type: 'custom',
+ renderItem: function (params, api) {
+ return {
+ type: 'group',
+ children: [{
+ type: 'rect',
+ style: {
+ stroke: 'red',
+ lineWidth: 5
+ }
+ }]
+ };
+ }
+ });
+ }
+ }]
+ });
+ });
+
+ </script>
+
+
+
+
+<!--
+
+ <script>
+ require([
+ 'echarts'/*, 'map/js/china' */
+ ], function (echarts) {
+ var animationDuration = 5000;
+ var animationDurationUpdate = 4000;
+ var angleLabel = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo', 'Libra', 'Scorpius', 'Sagittarius', 'Capricornus', 'Aquarius', 'Pisces'];
+ var angleRoundValue = angleLabel.length;
+ var radiusOffset = 10;
+ var angleStep = angleRoundValue / 90;
+ var barWidthValue = 0.4;
+ var radiusStep = 4;
+ var colors = {
+ 'A': { stroke: 'green', fill: 'rgba(0,152,0,0.6)' },
+ 'B': { stroke: 'red', fill: 'rgba(152,0,0,0.6)' },
+ 'C': { stroke: 'blue', fill: 'rgba(0,0, 152,0.6)' },
+ };
+ var allData = [[
+ [[1, 3, 'A']],
+ [[2, 6, 'B']],
+ [[3, 9, 'C']],
+ ], [
+ [[1, 12, 'A']],
+ [[2, 16, 'B']],
+ [[3, 14, 'C']],
+ ], [
+ [[1, 17, 'A']],
+ [[2, 22, 'B']],
+ [[3, 19, 'C']],
+ ]];
+ var currentDataIndex = 0;
+
+ function getMaxRadius() {
+ var radius = 0;
+ for (var j = 0; j < allData.length; j++) {
+ var data = allData[j];
+ for (var i = 0; i < data.length; i++) {
+ radius = Math.max(radius, getSpiralValueRadius(data[i][0][0], data[i][0][1]));
+ }
+ }
+ return Math.ceil(radius * 1.2);
+ }
+
+ function getSpiralValueRadius(valRadius, valAngle) {
+ return valRadius + radiusStep * (valAngle / angleRoundValue);
+ }
+
+ function renderItem(params, api) {
+ var valueRadius = api.value(0);
+ var valueAngle = api.value(1);
+ var points = [];
+ for (var iAngleVal = 0, end = valueAngle + angleStep; iAngleVal < end; iAngleVal += angleStep) {
+ iAngleVal > valueAngle && (iAngleVal = valueAngle);
+ var iRadiusVal = getSpiralValueRadius(valueRadius - barWidthValue, iAngleVal);
+ var point = api.coord([iRadiusVal, iAngleVal]).slice(0, 2);
+ points.push(point);
+ }
+ for (var iAngleVal = valueAngle; iAngleVal > -angleStep; iAngleVal -= angleStep) {
+ iAngleVal < 0 && (iAngleVal = 0);
+ var iRadiusVal = getSpiralValueRadius(valueRadius + barWidthValue, iAngleVal);
+ var point = api.coord([iRadiusVal, iAngleVal]).slice(0, 2);
+ points.push(point);
+ }
+ var name = api.value(2);
+ return {
+ type: 'polygon',
+ shape: { points: points },
+ style: {
+ lineWidth: 1,
+ fill: colors[name].fill,
+ stroke: colors[name].stroke
+ }
+ };
+ }
+
+ var option = {
+ animationDuration: animationDuration,
+ animationDurationUpdate: animationDurationUpdate,
+ angleAxis: {
+ type: 'value',
+ // splitLine: { show: false },
+ splitArea: {show: true},
+ axisLabel: {
+ formatter: function(val) {
+ return angleLabel[val];
+ },
+ color: 'rgba(0,0,0,0.2)'
+ },
+ axisLine: { lineStyle: { color: 'rgba(0,0,0,0.2)' } },
+ min: 0,
+ max: angleRoundValue
+ },
+ radiusAxis: {
+ type: 'value',
+ splitLine: { show: false },
+ axisLabel: { color: 'rgba(0,0,0,0.2)' },
+ axisLine: { lineStyle: { color: 'rgba(0,0,0,0.2)' } },
+ min: 0,
+ max: getMaxRadius()
+ },
+ polar: {
+ },
+ tooltip: {},
+ series: [{
+ type: 'custom',
+ name: 'A',
+ coordinateSystem: 'polar',
+ renderItem: renderItem,
+ data: allData[currentDataIndex][0]
+ }, {
+ type: 'custom',
+ name: 'B',
+ coordinateSystem: 'polar',
+ renderItem: renderItem,
+ data: allData[currentDataIndex][1]
+ }, {
+ type: 'custom',
+ name: 'C',
+ coordinateSystem: 'polar',
+ renderItem: renderItem,
+ data: allData[currentDataIndex][2]
+ }]
+ };
+
+ var chart = testHelper.create(echarts, 'spiral', {
+ title: [
+ 'animation: ',
+ ],
+ option: option,
+ buttons: [{
+ text: 'next',
+ onclick: function () {
+ currentDataIndex++;
+ currentDataIndex >= allData.length && (currentDataIndex = 0);
+ chart.setOption({
+ series: [{
+ data: allData[currentDataIndex][0]
+ }, {
+ data: allData[currentDataIndex][1]
+ }, {
+ data: allData[currentDataIndex][2]
+ }]
+ })
+ }
+ }, {
+ text: 'enable animation',
+ onclick: function () {
+ chart.setOption({ animation: true });
+ }
+ }, {
+ text: 'disable animation',
+ onclick: function () {
+ chart.setOption({ animation: false });
+ }
+ }]
+ });
+ });
+ </script>
+ -->
+
+
+
+
+
+
+
+ <script>
+ require([
+ 'echarts'/*, 'map/js/china' */
+ ], function (echarts) {
+ var animationDuration = 5000;
+ var animationDurationUpdate = 4000;
+ var animationEasingUpdate = 'elasticOut';
+ var angleLabel = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo', 'Libra', 'Scorpius', 'Sagittarius', 'Capricornus', 'Aquarius', 'Pisces'];
+ var angleRoundValue = angleLabel.length;
+ var radiusOffset = 10;
+ var angleStep = angleRoundValue / 90;
+ var barWidthValue = 0.4;
+ var radiusStep = 4;
+ var colors = {
+ 'A': { stroke: 'green', fill: 'rgba(0,152,0,0.6)' },
+ 'B': { stroke: 'red', fill: 'rgba(152,0,0,0.6)' },
+ 'C': { stroke: 'blue', fill: 'rgba(0,0, 152,0.6)' },
+ };
+ var allData = [[
+ [[1, 3, 'A']],
+ [[2, 6, 'B']],
+ [[3, 9, 'C']],
+ ], [
+ [[1, 12, 'A']],
+ [[2, 16, 'B']],
+ [[3, 14, 'C']],
+ ], [
+ [[1, 17, 'A']],
+ [[2, 22, 'B']],
+ [[3, 19, 'C']],
+ ]];
+ var currentDataIndex = 0;
+
+ function getMaxRadius() {
+ var radius = 0;
+ for (var j = 0; j < allData.length; j++) {
+ var data = allData[j];
+ for (var i = 0; i < data.length; i++) {
+ radius = Math.max(radius, getSpiralValueRadius(data[i][0][0], data[i][0][1]));
+ }
+ }
+ return Math.ceil(radius * 1.2);
+ }
+
+ function getSpiralValueRadius(valRadius, valAngle) {
+ return valRadius + radiusStep * (valAngle / angleRoundValue);
+ }
+
+ function makeShapePoints(api, valueRadius, valueAngle) {
+ var points = [];
+ for (var iAngleVal = 0, end = valueAngle + angleStep; iAngleVal < end; iAngleVal += angleStep) {
+ iAngleVal > valueAngle && (iAngleVal = valueAngle);
+ var iRadiusVal = getSpiralValueRadius(valueRadius - barWidthValue, iAngleVal);
+ var point = api.coord([iRadiusVal, iAngleVal]).slice(0, 2);
+ points.push(point);
+ }
+ for (var iAngleVal = valueAngle; iAngleVal > -angleStep; iAngleVal -= angleStep) {
+ iAngleVal < 0 && (iAngleVal = 0);
+ var iRadiusVal = getSpiralValueRadius(valueRadius + barWidthValue, iAngleVal);
+ var point = api.coord([iRadiusVal, iAngleVal]).slice(0, 2);
+ points.push(point);
+ }
+ return points;
+ }
+
+ function renderItem(params, api) {
+ var valueRadius = api.value(0);
+ var valueAngle = api.value(1);
+ var name = api.value(2);
+ return {
+ type: 'polygon',
+ shape: {
+ points: makeShapePoints(api, valueRadius, valueAngle),
+ valueAngle: valueAngle
+ },
+ style: {
+ lineWidth: 1,
+ fill: colors[name].fill,
+ stroke: colors[name].stroke
+ },
+ during: function (elProps) {
+ elProps.shape.points = makeShapePoints(
+ api, valueRadius, elProps.shape.valueAngle
+ );
+ }
+ };
+ }
+
+ var option = {
+ animationDuration: animationDuration,
+ animationDurationUpdate: animationDurationUpdate,
+ animationEasingUpdate: animationEasingUpdate,
+ angleAxis: {
+ type: 'value',
+ // splitLine: { show: false },
+ splitArea: {show: true},
+ axisLabel: {
+ formatter: function(val) {
+ return angleLabel[val];
+ },
+ color: 'rgba(0,0,0,0.2)'
+ },
+ axisLine: { lineStyle: { color: 'rgba(0,0,0,0.2)' } },
+ min: 0,
+ max: angleRoundValue
+ },
+ radiusAxis: {
+ type: 'value',
+ splitLine: { show: false },
+ axisLabel: { color: 'rgba(0,0,0,0.2)' },
+ axisLine: { lineStyle: { color: 'rgba(0,0,0,0.2)' } },
+ min: 0,
+ max: getMaxRadius()
+ },
+ polar: {
+ },
+ tooltip: {},
+ series: [{
+ type: 'custom',
+ name: 'A',
+ coordinateSystem: 'polar',
+ renderItem: renderItem,
+ data: allData[currentDataIndex][0]
+ }, {
+ type: 'custom',
+ name: 'B',
+ coordinateSystem: 'polar',
+ renderItem: renderItem,
+ data: allData[currentDataIndex][1]
+ }, {
+ type: 'custom',
+ name: 'C',
+ coordinateSystem: 'polar',
+ renderItem: renderItem,
+ data: allData[currentDataIndex][2]
+ }]
+ };
+
+ var chart = testHelper.create(echarts, 'spiral2', {
+ title: [
+ 'animation: ',
+ ],
+ option: option,
+ buttons: [{
+ text: 'next',
+ onclick: function () {
+ currentDataIndex++;
+ currentDataIndex >= allData.length && (currentDataIndex = 0);
+ chart.setOption({
+ series: [{
+ data: allData[currentDataIndex][0]
+ }, {
+ data: allData[currentDataIndex][1]
+ }, {
+ data: allData[currentDataIndex][2]
+ }]
+ })
+ }
+ }, {
+ text: 'enable animation',
+ onclick: function () {
+ chart.setOption({ animation: true });
+ }
+ }, {
+ text: 'disable animation',
+ onclick: function () {
+ chart.setOption({ animation: false });
+ }
+ }]
+ });
+ });
+ </script>
+
+
</body>
</html>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org