First, the prelude to the story

As time goes by, small programs have been with us for more than three years. Today, we are going to tell a story about small programs and animation: once upon a time…

2. The beginning of the story

When you think of applets and animations, what comes to mind first? There are three kinds of animation schemes: createAnimation, this.animate and CSS3 animation.

1. createAnimationwithAnimation

Create an animation instance animation. Call the instance’s methods to describe the animation. Finally, the animation data is exported to the animation property of the component through the animation instance’s export method.

var animation = wx.createAnimation({
  transformOrigin: "50% 50%".duration: 1000.timingFunction: "ease".delay: 0
})

// step() represents the completion of a set of animations. You can call any number of animation methods in a set of animations
// All animations in one group will start at the same time, and the next group of animations will not start until one group is complete
animation.translate(150.0).rotate(180).step()
animation.opacity(0).scale(0).step()
this.setData({
  animationData: animation.export()
})
Copy the code

2. Keyframe animationthis.animateinterface

Widget base 2.9.0 supports a friendlier way to create animations instead of the old wx.CreateAnimation. It has better performance and a more controlled interface. In pages or custom components, you can use the this.animate interface when you need to animate keyframes.

this.animate(selector, keyframes, duration, callback)
Copy the code

Official examples:

  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("Clear opacity and rotate properties on #container")
      })
  }.bind(this))
Copy the code

3. The range of the animation

This is a common way to animate interfaces, and CSS animations work well even on low performance systems. The rendering engine uses frame hopping and other techniques to make sure the animation is as smooth as possible.

Use style to achieve small program animation, usage and CSS usage similar, define the specified animation class name after the element can be added.

Here’s an animation that mimics a heartbeat:

@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.3 s;
  animation-timing-function: ease-in-out;
}
Copy the code

Iii. Story development

The story is set up like this: multiple preset animation configurations need to be supported, and the entry animation, emphasis animation, and exit animation run in sequence.

As follows, the “3 pieces 50% off /2 pieces 30% off /1 piece 10% off” text sets the entry animation – from small to large and the emphasis animation – pulse animation effect:

The resulting applet effect:

Taro is a good friend of Small program, and based on the setting of the story, H5 also needs to eat.

If you want to get to the climax of the story quickly, you have to use some clever tricks. You decided to use the popular Animate. CSS library to support a variety of preset animation effects!

1. Support multiple animation configurations

Animate. CSS is a ready-to-use cross-browser animation library that you can use in your Web projects, Preset shake (shake), flash (flash), bounce (flip), rotation (rotateIn/rotateOut), fadeIn/fadeOut and other 97 animation effects. All animations can be viewed on the official website home page.

To support multiple animation configurations, consider incorporating the animate. CSS library, a great CSS library, into your applet. From github.com/animate-css… Download the source code, rename the.css file to.wxss or.scss, and introduce style files into the page or component.

  import './animate.scss'
Copy the code

Animate. CSS is very easy to use because it binds different types of animation into different classes, so you just need to add the corresponding class to the element to have any animation you want.

Because applets limit the size of the code package, you can remove all @-webkit- and other prefixes in animate. CSS to reduce the size in half, or even use the code for @Keyframes without the class name.

2. Execute one animation followed by another?

As can be seen from the above, the animation scheme of CSS3 is adopted, which basically determines the next development stage of the story.

If you want an entry animation, an emphasis animation, and an exit animation to run sequentially, you need to listen for the end of the previous animation, followed by the next animation. Bindtransitionend, BindAnimationStart, BindAnimationIteration and BindAnimationEnd can be used to listen for animation events during animation.

Events for built-in components in Taro will still start with on, namely onTransitionEnd, onAnimationStart, onAnimationIteration, and onAnimationEnd.

Note: Listening for animation events are not bubbling events and need to be bound to the node where the animation actually occurred.

To make it invisible before entry and invisible after exit, set animation-fill-mode: both and do not remove the style, because the effect of the exit animation will fail and the element will be displayed again.

You might also have to deal with other behaviors, such as missing elements that might actually hold space, and interaction clicks that are best unbundled.

<View
  onAnimationEnd={this.onAnimationEnd}
>
  {this.props.children}
</View>
Copy the code

Fourth, the climax of the story

The story is all set and finally comes to a climax.

Sharp-eyed people also found that the GIF above “generated small program effect” also realized the effect of scrolling to the visible area before the animation began.

This is an old topic, so how to implement in the small program side?

Scheme 1: page rolling mode

  1. Applet utilizationonPageScrollThe API listens to the user slide page event, can be obtainedscrollTop: The vertical scrolling distance of the page (unit: px).
  2. Taro.createSelectorQueryGets the vertical scroll position of the element in the display area.
  3. Determine whether to start the animation by calculating whether it is in the viewable area on the base.

Scheme two: observer mode

  1. Does not supportonPageScrollIn the case of, you need to useTaro.createIntersectionObserverGets the proportion of the intersection of the target node and the reference region to trigger the relevant callback function, namely observer mode.

The code in

(1) The way to obtain the current page

The first element of the array is the home page, and the last element is the current page:

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

(2) Initialize page scrolling

Decide whether to use scroll mode or observer mode:

InitPageScroll () {const env = taro.getenv () const currentPage = this.getCurrentPage() // get onPageScroll method const OnPageScroll = currentPage.onPagescroll H5 or "small program page onPageScroll hooks" using unified code const isPageScroll env = = = = Taro. ENV_TYPE. WEB | | (env! == Taro.ENV_TYPE.WEB && onPageScroll ! = = undefined) / / observer pattern: a small program page without onPageScroll hooks, use Taro. CreateIntersectionObserver listening const isObserver = env! == Taro.ENV_TYPE.WEB && Taro.createIntersectionObserver if (isPageScroll) { this.listenPageScroll(currentPage) } else if  (isObserver) { this.observePageScroll() } }Copy the code

(3) Page scrolling mode

First define a multi-environment pageScroll hook outside the class, supporting applets and H5:

const createPageScroll = function(page) {
  const env = Taro.getEnv()
  let onPageScroll = () = > {}

  if(env ! == Taro.ENV_TYPE.WEB) {/ / small programs
    const prevOnPageScroll = page.onPageScroll.bind(page)
    page.onPageScroll = e= > {
      prevOnPageScroll(e)
      onPageScroll(e)
    }
  } else if (env === Taro.ENV_TYPE.WEB) {
    // H5
    window.addEventListener("scroll".() = > {
      onPageScroll({ scrollTop: window.scrollY })
    })
  }

  return nextOnPageScroll= > {
    onPageScroll = nextOnPageScroll
  }
}

Copy the code

Start listening for scrolling using the createPageScroll method defined above:

listenPageScroll (currentPage) {
  const pageScroll = createPageScroll(currentPage)
  pageScroll(this.onScroll)
}
Copy the code

Gets the height from the top of the page to determine whether to start the animation:

Knowledge:

  • In Taro’s pages and component classes,thisPoints to an instance of an Taro page or component while passingthis.$scopeGet examples of native applet pages and components corresponding to Taro’s pages and components.
  • Taro.createSelectorQueryReturn an instance of SelectorQuery. In custom components or pages containing custom components, use this.createsElectorQuery () instead.
  • The SelectorQuery object instance can further query node information providedselect ,inexecMethods.
  • The NodesRefboundingClientRectUsed to query the layout position of a node, in pixels, relative to the display area, similar in function to the DOM’s getBoundingClientRect.
onScroll = () = > {
  const query = Taro.createSelectorQuery().in(this.$scope)
  query
    .select(`.animation-The ${this.uniq}`)
    .boundingClientRect(res= > {
      if(! res)return

      let resTop = res.top
      const distance = res.height / 2
      const isStartAnimation = resTop + distance < this.windowHeight
      if (isStartAnimation && !this.isAnimated) {
        this.startAnimation()
        // The animation only appears once
        this.isAnimated = true
      }
    })
    .exec()
}
Copy the code

(4) Observer mode:

Knowledge:

  • Taro.createIntersectionObserverCreate and return an instance of IntersectionObserver object. In custom components or contain custom components in the page, you should use this. CreateIntersectionObserver ([options]) instead.
  • IntersectionObserver object, which is used to infer whether certain nodes can be seen by users and what proportion can be seen by users.
  • The IntersectionObserverrelativeToViewportMethod specifies the page display area as one of the reference areas.
  • The IntersectionObserverobserveSpecify the target node and start listening for intersection state changes, whereres.intersectionRatioRefers to the proportion of the intersection area to the layout area of the target node.
observePageScroll () {
  const navObserver = Taro.createIntersectionObserver(this.$scope, {
    initialRatio: 0.5.thresholds: [0.5]
  })
  navObserver.relativeToViewport()
  navObserver.observe(`.animation-The ${this.uniq}`.res= > {
  const isStartAnimation = !this.isAnimated && res.intersectionRatio > 0.5
    if (isStartAnimation) {
      this.startAnimation()
      // The animation only appears once
      this.isAnimated = true}})}Copy the code

5. The end of the story

The story of applets and animations is far from over, and even if the story begins, what you see is only one of a million possible stories.

The story is coming to an end, the story of small program is still running, thanks to wechat small program and taro’s document.