一、接入场景

在 react-native 或者是 weex 容器环境下发现动画交互的效果有些差,尤其是在某些复杂的动画场景下。

究其原因,bindingx 的官网已经说的很明白了,引用如下:

由于 weex底层使用的 JS-Native Bridge 具有天然的异步特性,这使得 JS 和 Native 之间的通信会有固定的性能损耗,因此在一些复杂的交互场景中,JS 代码很难以高帧率运行。举个例子,如果我们要实现 "视图随手势移动" 的效果,那么按照传统的方式,需要在这个视图上绑定 touch 或者 pan 事件,当手势发生时, Native 会将手势事件通过 Bridge 传递给 JS , 这产生了一次 Native 到 JS 的通信。而 JS 在接收到事件后,需要根据手指移动的偏移量驱动界面变化,这又会产生一次 JS 到 Native 的通信。与此同时,手势回调事件触发的频率是非常高的,频繁的的通信带来的时间成本很可能导致界面无法在16ms中完成绘制,进而产生卡顿。

事实上,不仅仅是在 weex 上存在这种问题, React Native 等框架同样存在类似的问题。拿 React Native Animated 组件为例,为了实现流畅的动画效果,这个组件采用了声明式的API,在 JS 端仅仅定义了输入与输出以及具体的 transform 行为,而真正的动画是通过 Native Driver 在 Native 层执行,这样就避免了频繁的通信。然而,这个方案只能解决一部分问题,如果是有复杂交互操作的场景就不够用了。另外,声明式的方式能够定义的行为非常有限,无法满足更复杂的交互场景。

就如同我之前写的文章:rax 创新体验组件 GestureView 手势,其实每次在触发 touch 事件的时候,js 和 native 之间都会进行通信,因此这个效果其实是比较差的,经常会出现卡顿现象。

如果在 RN 或者是 weex 容器场景下有富交互的场景,那非常推荐使用 bindingx,可以看看 bindingx 官网的动画示例,尤其是下面这个,非常实用:

二、核心概念

官方文档中以一句话总结了 bindingx 的核心概念:

BindingX 的核心思想就是将"交互行为"以表达式的方式描述,并提前预置到Native,避免在行为触发时JS与native的频繁通信。

因此只要是频繁通信的交互场景,其实都可以使用 bindingx,而 bindingx 本身也已经支持了下面四种场景:

  • 监听 pan 手势,更新UI。
  • 监听滚动容器(如List)的 onscroll 事件,更新UI。
  • 监听设备传感器方向变化,更新UI。
  • 动画。(即监听设备的每一帧的屏幕刷新回调事件,更新UI)。

三、基础概念

其实基础概念就是 表达式(Expression)事件类型(Event Type)属性变换(Transformation Properties)

表达式其实就是给你几个预置的变量,比如 pan 事件的 xy 表示移动的左右的距离,而 timing 事件的 t 则表示变化的时间,时间是从 0 开始的等等。

通过预置变量在加上各种计算,就可以得出一个值,而这个值就是将来应用到css属性上的值,现在支持的语法或者是方法名可以在 https://alibaba.github.io/bindingx/guide/cn_api_expressionSyntax 看到。

比如我要在 timing 事件类型中根据时间去计算旋转的角度可以这样写表达式:

{
  element: findDOMNode(this.refs.header),
  expression: 't/4',
  property: 'transform.rotateZ'
}

比如我要根据手指移动的 x 距离来控制一个方框的 transform.translateX 属性:

{
  element: findDOMNode(this.refs.header),
  expression: 'x/1',
  property: 'transform.translateX '
}

可以发现,我即使最终使用的是 x ,也需要一个表达式,因为表达式所以表达式

而事件类型就是上面提到的四种类型,不同的事件类型提供的预置变量也是不同的,目前支持的四种事件类型如下:

  • pan: 监听手势的pan事件
  • timing: 监听时间变化,用来实现动画
  • scroll: 监听滚动容器的onScroll事件
  • orientation: 监听设备方向变化,与web DeviceOrientation一致

而不同的事件类型支持的预置变量可以在 https://alibaba.github.io/bindingx/guide/cn_api_eventType 看到

属性变换就是当动画发生的时候,需要应用到的 css 属性上,现在支持的属性可以在 https://alibaba.github.io/bindingx/guide/cn_api_attributes 中看到。

四、体验

我的场景是在 rax 环境中做一个 loading 动画效果,这个肯定是没办法通过 rax 的 universal-transition 实现的,毕竟后者仅是为了实现高性能(相对)的过渡效果而已。

基本的 rax render 方法内容是

render() { 
  return (
    <View style={styles.app} ref='app'>
      <View style={styles.appHeader} ref="header">
      </View>
    </View>
  );
}

1、pan 事件类型

官网给的最简单的示例都是 pan 事件的,我先写一个 pan 事件的,使用场景就是通过滑动控制 div 的 transform.translateX.

代码写在了 playground 上,这里只贴 bindingx 的代码:

   const props = [
      {
        element: getEl(this.refs.header),
        expression: 'x/1',
        property: 'transform.translateX'
      }
    ];
    BindingX .bind({ 
      eventType: 'pan',
      anchor:getEl(this.refs.app),
      props
    });

在线体验链接:

最终效果(gif可能有卡顿):

GIF.gif

2、timing 事件类型

这个应用场景是一个 div 根据时间进行旋转。

    const props = [
    {
      element: findDOMNode(this.refs.header),
      expression: 't/4',
      property: 'transform.rotateZ'
    }
    ];
    BindingX .bind({ 
      eventType: 'timing',
      props
    });

playground 链接:

最终效果(gif 可能卡顿)

GI2F.gif