Mastering Mini‑Program Animations: From createAnimation to CSS3 and Taro Integration

This article walks through the three animation approaches supported by WeChat mini‑programs—createAnimation, this.animate, and CSS3—provides detailed code examples, shows how to integrate Animate.css, and explains using Taro’s page‑scroll and IntersectionObserver techniques to sequence entrance, emphasis, and exit animations.

Aotu Lab
Aotu Lab
Aotu Lab
Mastering Mini‑Program Animations: From createAnimation to CSS3 and Taro Integration

Story Prologue

WeChat mini‑programs have been around for over three years, and this guide explores how to create animations within them.

Animation Options in Mini‑Programs

There are three officially supported ways to animate elements:

createAnimation
this.animate

CSS3 animations

1. Using createAnimation

Create an animation instance, call its methods to describe the animation steps, and export the data to bind to a component’s animation property.
var animation = wx.createAnimation({
  transformOrigin: "50% 50%",
  duration: 1000,
  timingFunction: "ease",
  delay: 0
});
// step() marks a group of animation actions; all actions in a group start together.
animation.translate(150, 0).rotate(180).step();
animation.opacity(0).scale(0).step();
this.setData({
  animationData: animation.export()
});

2. Keyframe Animation with this.animate

Available from base library 2.9.0, this method offers better performance and a more controllable API for keyframe animations.
this.animate(selector, keyframes, duration, callback)

Example:

this.animate('#container', [
  {opacity: 1.0, rotate: 0, backgroundColor: '#FF0000'},
  {opacity: 0.5, rotate: 45, backgroundColor: '#00FF00'},
  {opacity: 0.0, rotate: 90, backgroundColor: '#FF0000'}
], 5000, function () {
  this.clearAnimation('#container', {opacity: true, rotate: true}, function () {
    console.log("Cleared opacity and rotate on #container");
  });
}.bind(this));

3. CSS3 Animations

CSS animations work well even on low‑performance devices; the rendering engine may drop frames to keep the animation smooth.

Define a keyframe and apply it via a class, similar to standard web CSS.

@keyframes heartBeat {
  0%   {transform: scale(1);}
 14%   {transform: scale(1.3);}
 28%   {transform: scale(1);}
 42%   {transform: scale(1.3);}
 70%   {transform: scale(1);}
}
.heartBeat {
  animation-name: heartBeat;
  animation-duration: 1.3s;
  animation-timing-function: ease-in-out;
}

Extending Animations with Animate.css

Animate.css provides 97 ready‑to‑use animations (shake, flash, bounce, flip, rotateIn/Out, fadeIn/Out, etc.). To use it in a mini‑program, download the source from the GitHub releases page, rename the .css file to .wxss or .scss, and import it: import './animate.scss' Because mini‑program packages have size limits, you can remove vendor prefixes (e.g., @-webkit-) or keep only the @keyframes definitions.

Sequencing Multiple Animations

To run entrance, emphasis, and exit animations in order, you need to listen for the end of one animation before starting the next. Mini‑programs expose several event listeners:

bindtransitionend
bindanimationstart
bindanimationiteration
bindanimationend

In Taro, the corresponding component events are prefixed with on (e.g., onTransitionEnd, onAnimationStart, etc.). Note that these events do not bubble; they must be bound to the actual animated node.

To keep an element invisible before the entrance animation and after the exit animation, set animation-fill-mode: both and avoid removing the class after the animation finishes.

Triggering Animations on Scroll

Method 1: Page‑Scroll Mode

Use the mini‑program onPageScroll API to obtain scrollTop (vertical scroll distance in pixels).

Call Taro.createSelectorQuery to get the element’s position relative to the viewport.

Calculate whether the element is within the visible area and start the animation accordingly.

Method 2: IntersectionObserver Mode

If onPageScroll is unavailable, use Taro.createIntersectionObserver to observe the intersection ratio between the target node and the viewport.

Implementation Details

Below is a concise implementation that works for both modes.

getCurrentPage() {
  const pages = Taro.getCurrentPages ? Taro.getCurrentPages() : [{}];
  const currentPage = pages[pages.length - 1];
  return currentPage;
}

initPageScroll() {
  const env = Taro.getEnv();
  const currentPage = this.getCurrentPage();
  const onPageScroll = currentPage.onPageScroll;
  const isPageScroll = env === Taro.ENV_TYPE.WEB || (env !== Taro.ENV_TYPE.WEB && onPageScroll !== undefined);
  const isObserver = env !== Taro.ENV_TYPE.WEB && Taro.createIntersectionObserver;
  if (isPageScroll) {
    this.listenPageScroll(currentPage);
  } else if (isObserver) {
    this.observePageScroll();
  }
}

const createPageScroll = function (page) {
  const env = Taro.getEnv();
  let onPageScroll = () => {};
  if (env !== Taro.ENV_TYPE.WEB) {
    const prevOnPageScroll = page.onPageScroll.bind(page);
    page.onPageScroll = e => {
      prevOnPageScroll(e);
      onPageScroll(e);
    };
  } else {
    window.addEventListener("scroll", () => {
      onPageScroll({ scrollTop: window.scrollY });
    });
  }
  return nextOnPageScroll => {
    onPageScroll = nextOnPageScroll;
  };
};

listenPageScroll(currentPage) {
  const pageScroll = createPageScroll(currentPage);
  pageScroll(this.onScroll);
}

onScroll = () => {
  const query = Taro.createSelectorQuery().in(this.$scope);
  query.select(`.animation-${this.uniq}`)
    .boundingClientRect(res => {
      if (!res) return;
      const resTop = res.top;
      const distance = res.height / 2;
      const isStartAnimation = resTop + distance < this.windowHeight && !this.isAnimated;
      if (isStartAnimation) {
        this.startAnimation();
        this.isAnimated = true;
      }
    })
    .exec();
};

observePageScroll() {
  const navObserver = Taro.createIntersectionObserver(this.$scope, {
    initialRatio: 0.5,
    thresholds: [0.5]
  });
  navObserver.relativeToViewport();
  navObserver.observe(`.animation-${this.uniq}`, res => {
    const isStartAnimation = !this.isAnimated && res.intersectionRatio > 0.5;
    if (isStartAnimation) {
      this.startAnimation();
      this.isAnimated = true;
    }
  });
}

Key points: this refers to the Taro component instance; this.$scope gives access to the underlying mini‑program page/component. Taro.createSelectorQuery (or this.createSelectorQuery() inside a component) provides select, in, exec, and boundingClientRect methods.

The boundingClientRect result mimics the DOM getBoundingClientRect API.

Conclusion

The story of mini‑program animations is far from over; the techniques above enable entrance, emphasis, and exit effects, support multiple preset animations via Animate.css, and allow scroll‑triggered playback using either page‑scroll or IntersectionObserver approaches.

References

[1] Animate.css – https://animate.style/

[2] WeChat Mini‑Program Documentation – https://developers.weixin.qq.com/miniprogram/dev/framework/

[3] Taro Documentation – https://taro-docs.jd.com/taro/docs/README

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

animationWeChat mini-programTarocreateAnimationthis.animate
Aotu Lab
Written by

Aotu Lab

Aotu Lab, founded in October 2015, is a front-end engineering team serving multi-platform products. The articles in this public account are intended to share and discuss technology, reflecting only the personal views of Aotu Lab members and not the official stance of JD.com Technology.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.