Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce new keyframe based animation to graphic component and custom series #16225

Merged
merged 37 commits into from
Dec 23, 2021

Conversation

pissang
Copy link
Contributor

@pissang pissang commented Dec 15, 2021

This pull request is in the type of:

  • bug fixing
  • new feature
  • others

What does this PR do?

This PR includes two major features:

  1. Introduces transition to graphic component. Which is similar to custom series.
  2. New keyframe based animation in both graphic component and custom series.

Transition in graphic component.

We introduced property transition in custom series in #12775.

It allow us applying complex transform/shape/style transition to the elements when data is updated. Even more, we can use enterFrom and leaveTo to configure the enter and leave animation.

In this PR we also brings this flexible transition option to the graphic component. Here is an simple example:

// Display an orange circle at [50, 100]
chart.setOption({
  graphic: {
    elements: [{
      type: 'circle',
      left: 'left',
      y: 100,
      x: 50,
      // Enter from outside
      enterFrom: {
        x: -100
      },
      // Configure x and y property can be transition.
      transition: ['x', 'y'],
      shape: {
        cx: 0,
        cy: 0,
        r: 50
      },
      style: {
        fill: 'orange'
      }
    }]
  }
});
...
// Move to [200, 100] smoothly
chart.setOption({
  graphic: {
    elements: [{
      x: 200
    }]
  }
})
...
// Layout options like left/top/right/bottom is also supported.
// Move to [center, center] smoothly
chart.setOption({
  graphic: {
    elements: [{
      left: 'center',
      top: 'center'
    }]
  }
});
...
// Enlarge the circle
chart.setOption({
  graphic: {
    elements: [{
      transition: ['shape'],
      shape: {
        r: 100
      }
    }]
  }
});
// Transition the color to red.
chart.setOption({
  graphic: {
    elements: [{
      transition: ['style'],
      style: {
        color: 'red'
      }
    }]
  }
});

As we can see, the option keeps the same as the custom series. To make the transition more usable. We add enhancement:

  1. Support transition: 'all' to configure all properties can have transition animation.

Enumerate the properties in the transition option can be tedious. We can now use the keyword 'all' on the transition configuration if we don't wan't to care about which property can have the animation.

  1. Can configure the nested properties include style, shape, extra in the top level enterFrom and leaveTo

For example:

enterFrom: {
  x: -100,
  style: {
    opacity: 0
  }
}
  1. Animation configuration per element.

We provide enterAnimation, updateAnimation, leaveAnimation to configure the animation duration, easing, delay per element.

For example:

graphic: {
  elements: [{
    enterAnimation: {
      duration: 1000
    },
    updateAnimation: {
      duration: 500
    },
    leaveAnimation: {
      duration: 200
    }
  }]
}

All these three enhancements will be available in both graphic component and custom series.

New keyframe animation API

Transition animation only applies when data is created, changed or removed. It only has start and end state and will only run once.

In order to providing more complex multiple keyframe looped animations. We introducing a brand new keyframe animation API to the custom series and graphic components in this PR.

We can use it to do things like:

  1. Displaying an awesome loading animation.
  2. Having background with some dynamic effect.
  3. Creating your own animation effect in the chart. Not only the builtin ripple effect or trail effect.
  4. Load a lottie animation file and added it in the echarts.
  5. Or just have fun playing with the animation.

Interface

interface KeyframeAnimationKeyframe {
  // Easing of each keyframe
  easing?: AnimationEasing
  // Percent of this keyframe
  percent?: number

  ...// Element properties.
};

interface KeyframeAnimation {
  duration?: string
  easing?: AnimationEasing
  delay?: number
  loop?: boolean
  keyframes?: KeyframeAnimationKeyframe[]
}

interface GraphicElement {
  ... // Other properties
  keyframeAnimation?: KeyframeAnimation | KeyframeAnimation[]
}

Examples

Text animation

Code:

chart.setOption({
  graphic: {
    elements: [{
      type: 'text',
      x: 100,
      y: 100,
      style: {
        text: 'Apache ECharts',
        fontSize: 50,
        fontWeight: 'bold',
        lineDash: [0, 200],
        lineDashOffset: 0,
        fill: 'transparent',
        stroke: '#000',
        lineWidth: 1
      },
      keyframeAnimation: {
        duration: 3000,
        loop: true,
        keyframes: [{
          percent: 0.7,
          style: {
            fill: 'transparent',
            lineDashOffset: 200,
            lineDash: [200, 0]
          }
        }, {
          // Hold a bit.
          percent: 0.8,
          style: {
            fill: 'transparent'
          }
        }, {
          percent: 1,
          style: {
            fill: 'black'
          }
        }]
      },
    }]
  }
})

Result:

logo.mov

Procedural wave animation

Code:

const elements = [];
for (let x = 20; x < width; x += 40) {
  for (let y = 20; y < height; y += 40) {
    elements.push({
      type: 'circle',
      x, y,
      style: {
        fill: '#800080'
      },
      shape: {
        r: 30
      },
      keyframeAnimation: {
        duration: 4000,
        loop: true,
        delay: (noise.perlin2(x / 500 + 0, y / 500 + 100) - 1) * 4000,
        keyframes: [{
          percent: 0.5,
          easing: 'sinusoidalInOut',
          style: {
              fill: '#ffa500'
          },
          scaleX: 0.1,
          scaleY: 0.1
        }, {
          percent: 1,
          easing: 'sinusoidalInOut',
          style: {
              fill: '#800080'
          },
          scaleX: 1,
          scaleY: 1
        }]
      }
    });
  }
}
chart.setOption({
  graphic: {
    elements
  }
})

Result:

wave.mp4

Marker jump effect

Use keyframe based animation in the custom series to have more complex marker effect.

Code:

renderItem(params, api) {
  const coord = api.coord([api.value(0, params.dataIndex), api.value(1, params.dataIndex)]);

  const circles = [];
  for (let i = 0; i < 5; i++) {
    circles.push({
      type: 'circle',
      shape: {
        cx: 0,
        cy: 0,
        r: 30
      },
      style: {
        stroke: 'red',
        fill: 'none',
        lineWidth: 2
      },
      // Ripple animation
      keyframeAnimation: {
        duration: 4000,
        loop: true,
        delay: -i / 4 * 4000,
        keyframes: [{
          percent: 0,
          scaleX: 0,
          scaleY: 0,
          style: {
            opacity: 1
          }
        }, {
          percent: 1,
          scaleX: 1,
          scaleY: 0.4,
          style: {
            opacity: 0
          }
        }]
      }
    });
  }
  return {
    type: 'group',
    x: coord[0],
    y: coord[1],
    children: [
      ...circles,
      {
        type: 'path',
        shape: {
          d: 'M16 0c-5.523 0-10 4.477-10 10 0 10 10 22 10 22s10-12 10-22c0-5.523-4.477-10-10-10zM16 16c-3.314 0-6-2.686-6-6s2.686-6 6-6 6 2.686 6 6-2.686 6-6 6z',
          x: -10,
          y: -35,
          width: 20,
          height: 40
        },
        style: {
          fill: 'red'
        },
        // Jump animation.
        keyframeAnimation: {
          duration: 1000,
          loop: true,
          delay: Math.random() * 1000,
          keyframes: [{
            y: -10,
            percent: 0.5,
            easing: 'cubicOut'
          }, {
            y: 0,
            percent: 1,
            easing: 'bounceOut'
          }]
        }
      }
    ]
  }

Result:

Screen.Recording.2021-12-17.at.2.45.54.PM.mov

Lottie animation

TBD

Misc

support multiple keyframe animation
@echarts-bot
Copy link

echarts-bot bot commented Dec 15, 2021

Thanks for your contribution!
The community will review it ASAP. In the meanwhile, please checkout the coding standard and Wiki about How to make a pull request.

The pull request is marked to be PR: author is committer because you are a committer of this project.

Document changes are required in this PR. Please also make a PR to apache/echarts-doc for document changes and update the issue id in the PR description. When the doc PR is merged, the maintainers will remove the PR: awaiting doc label.

This PR depends on ZRender changes. Please update the ZRender dependency to the latest nightly version including this change, which takes place everyday at 8:00 UTC (16:00 Beijing Time).
You can use npm i zrender@npm:zrender-nightly@dev to update package.json.
If you have any question about this, please leave a comment and we will give you extra help on this.

@pissang pissang marked this pull request as ready for review December 17, 2021 08:43
@plainheart
Copy link
Member

Seems the unit test didn't pass for some type errors.

@pissang
Copy link
Contributor Author

pissang commented Dec 21, 2021

UT is fixed

@plainheart
Copy link
Member

I noticed the strange behavior when hovering on the animated marker. Is there anything wrong?

@pissang
Copy link
Contributor Author

pissang commented Dec 21, 2021

@plainheart Seems it's because keyframe animation is conflicted to the state animation.

@pissang
Copy link
Contributor Author

pissang commented Dec 22, 2021

@plainheart Improved it a bit in the PR ecomfe/zrender#859. Now the behavior is:

hover.mov

Copy link
Member

@plainheart plainheart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@pissang pissang merged commit 7a7bd40 into next Dec 23, 2021
@echarts-bot
Copy link

echarts-bot bot commented Dec 23, 2021

Congratulations! Your PR has been merged. Thanks for your contribution! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: author is committer PR: awaiting doc Document changes is required for this PR. size/XXL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants