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.
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.animateCSS3 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 bindanimationendIn 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
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
