乐趣区

关于javascript:小程序与动画的故事

一、故事尾声

工夫一分一秒地流逝,小程序已随同咱们三载无余,明天要讲的是对于小程序与动画的故事:从前 …

二、故事结尾

一提小程序与动画,首先想到的是什么?嗯,微信小程序独创了一套动画玩法,官网反对 3 种动画计划,别离是 createAnimationthis.animateCSS3 动画

1. createAnimationAnimation

创立一个动画实例 animation。调用实例的办法来形容动画。最初通过动画实例的 export 办法导出动画数据传递给组件的 animation 属性。

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

// step() 示意一组动画的实现,能够在一组动画中调用任意多个动画办法
// 一组动画中的所有动画会同时开始,一组动画实现后才会进行下一组动画
animation.translate(150, 0).rotate(180).step()
animation.opacity(0).scale(0).step()
this.setData({animationData: animation.export()
})

2. 关键帧动画 this.animate 接口

从小程序根底库 2.9.0 开始反对一种更敌对的动画创立形式,用于代替旧的 wx.createAnimation。它具备更好的性能和更可控的接口。在页面或自定义组件中,当须要进行关键帧动画时,能够应用 this.animate 接口。

this.animate(selector, keyframes, duration, callback)

官网给出的例子:

  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("革除了 #container 上的 opacity 和 rotate 属性")
      })
  }.bind(this))

3. css3 动画

这是界面动画的常见形式,CSS 动画运行成果良好,甚至在低性能的零碎上。渲染引擎会应用跳帧或者其余技术以保障动画体现尽可能的晦涩。

利用款式实现小程序动画,用法和 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;
}

三、故事倒退

故事的设定是这样子的:须要反对多种预设的动画成果配置,且实现进场动画、强调动画、登场动画按程序运行。

如下,“3 件 5 折 / 2 件 7 折 / 1 件 9 折”的文本 设置了 进场动画 - 从小到大 以及 强调动画 - 脉冲 的动画成果:

生成的小程序成果:

Taro 是小程序的好搭档,而且基于故事的设定,H5 还是要点饭吃的。

要想疾速进入故事低潮,不得不采纳一些取巧的伎俩了,决定采纳市面上常见的 Animate.css 动画库来反对多种预设的动画成果!

1. 反对多种动画配置

Animate.css 是一个可在您的 Web 我的项目中应用的即用型跨浏览器动画库,预设了抖动(shake)、闪动(flash)、弹跳(bounce)、翻转(flip)、旋转(rotateIn/rotateOut)、淡入淡出(fadeIn/fadeOut)等 97 种动画成果。官网首页即可查看所有动画成果。

要反对多种动画配置,思考将 animate.css 这个十分棒的 css 库引入到小程序内应用。
从 https://github.com/animate-cs… 下载源码,将 .css 文件 改名为 .wxss 或者.scss 文件,在页面或组件中引入款式文件即可。

  import './animate.scss'

Animate.css 的应用非常简单,因为它是把不同的动画类型绑定到了不同的类里,所以想用哪种动画,只须要把相应的类增加到元素上就能够纵情享受了。

因为小程序对代码包的大小限度,因而可删除 animate.css 中所有 @-webkit- 等前缀的款式缩小一半体积,甚至间接应用 @keyframes 的代码,即去掉类名的形式调用。

2. 执行完一个动画后接着执行另一个动画 ?

从上文可知,采纳的是 CSS3 的动画计划,根本决定了故事的下一个倒退阶段。

如果要实现进场动画、强调动画、登场动画按程序运行,那么须要监听上一个动画完结,紧接着运行下一个动画。
动画过程中,微信小程序能够应用 bindtransitionendbindanimationstartbindanimationiterationbindanimationend 来监听动画事件。

在 Taro 中内置组件的事件仍然是以 on 结尾的,即 onTransitionEndonAnimationStartonAnimationIterationonAnimationEnd

留神:监听动画事件都不是冒泡事件,须要绑定在真正产生了动画的节点上才会失效。

要实现进场之前不可见,登场后不可见,设置 animation-fill-mode: both 即可,且不可移除款式,因为登场动画的成果成果 会生效,元素又显示进去了。

可能还得解决其余行为,比方 隐没的元素 理论可能还占位,交互点击的行为最好解绑。

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

四、故事低潮

故事都铺垫好了,终于来到了低潮。

眼尖的人儿也发现了,上文 GIF 图“生成的小程序成果”还实现了滚动到可视区域才开始执行动画的成果。

这是陈词滥调的话题了,那怎么在小程序侧实现呢?

计划一:页面滚动模式

  1. 小程序利用 onPageScroll 的 API 监听用户滑动页面事件,可获取 scrollTop:页面在垂直方向已滚动的间隔(单位 px)。
  2. Taro.createSelectorQuery 获取元素在显示区域的竖直滚动地位。
  3. 基上计算是否在可视区域来判断是否要开始动画。

计划二:观察者模式

  1. 不反对 onPageScroll的状况下,则须要应用 Taro.createIntersectionObserver 获取指标节点与参照区域的相交比例触发相干的回调函数,即观察者模式。

代码奉上

(1) Taro 获取以后页面的形式

首先咱们要晓得如何获取以后页面栈,数组中第一个元素为首页,最初一个元素为以后页面:

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

(2) 初始化页面滚动

判断应用页面滚动模式还是观察者模式:

initPageScroll () {const env = Taro.getEnv()
  const currentPage = this.getCurrentPage()

  // 获取 onPageScroll 办法
  const onPageScroll = currentPage.onPageScroll

  // 页面滚动模式:h5 或「小程序页面有 onPageScroll 钩子」应用对立的代码
  const isPageScroll =
    env === Taro.ENV_TYPE.WEB ||
    (env !== Taro.ENV_TYPE.WEB && onPageScroll !== undefined)

  // 观察者模式:小程序页面没有 onPageScroll 钩子,应用 Taro.createIntersectionObserver 监听
  const isObserver = env !== Taro.ENV_TYPE.WEB && Taro.createIntersectionObserver

  if (isPageScroll) {this.listenPageScroll(currentPage)
  } else if (isObserver) {this.observePageScroll()
  }
}

(3) 页面滚动模式

首先在类里头定义一个多环境的 pageScroll 钩子,反对小程序和 H5:

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 if (env === Taro.ENV_TYPE.WEB) {
    // H5
    window.addEventListener("scroll", () => {onPageScroll({ scrollTop: window.scrollY})
    })
  }

  return nextOnPageScroll => {onPageScroll = nextOnPageScroll}
}

应用上述定义的 createPageScroll 办法,开始监听滚动:

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

获取间隔页面顶部高度来判断是否要开始动画:

知识点:

  • 在 Taro 的页面和组件类中,this 指向的是 Taro 页面或组件的实例,而通过 this.$scope 获取 Taro 的页面和组件所对应的小程序原生页面和组件的实例。
  • Taro.createSelectorQuery 返回一个 SelectorQuery 对象实例。在自定义组件或蕴含自定义组件的页面中,应应用 this.createSelectorQuery() 来代替。
  • SelectorQuery 对象实例可进一步查问节点信息,提供 select inexec 等办法。
  • NodesRef 的 boundingClientRect 用于查问节点的布局地位,绝对于显示区域,以像素为单位,其性能相似于 DOM 的 getBoundingClientRect。
onScroll = () => {const query = Taro.createSelectorQuery().in(this.$scope)
  query
    .select(`.animation-${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()
        // 动画只呈现一次
        this.isAnimated = true
      }
    })
    .exec()}

(4) 观察者模式:

知识点:

  • Taro.createIntersectionObserver 创立并返回一个 IntersectionObserver 对象实例。在自定义组件或蕴含自定义组件的页面中,应应用 this.createIntersectionObserver([options]) 来代替。
  • IntersectionObserver 对象,用于推断某些节点是否能够被用户看见、有多大比例能够被用户看见。
  • IntersectionObserver 的 relativeToViewport 办法 指定页面显示区域作为参照区域之一。
  • IntersectionObserver 的observe 指定指标节点并开始监听相交状态变动状况,其中 res.intersectionRatio 指相交区域占指标节点的布局区域的比例。
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
    }
  })
}

五、故事结尾

小程序与动画的故事远远没有完结,纵使故事有了结尾,你看到的只是故事的万种可能的其中一种。

故事就要告一段落了,小程序的故事还在继续奔跑,感激 微信小程序 和 taro 的文档。


欢送关注凹凸实验室博客:aotu.io

或者关注凹凸实验室公众号(AOTULabs),不定时推送文章。

退出移动版