基于 BindingX 的富交互解决方案
BindingX 官网: alibaba.github.io/bindingx/
BindingX 项目地址: github.com/alibaba/bin…
一. 背景
在 Weex 环境下实现一些复杂的手势交互效果可能会产生卡顿,这是因为每次手势交互都会产生两次 JS-native 通信。第一次是 native call JS,将手势事件传递到 JS 层交给前端处理,当 JS 层接收到回调后,会产生第二次通信,JS call native,用来驱动界面变化。与此同时,手势回调事件触发的频率是非常高的,频繁通信带来的时间成本很可能导致界面无法在 16ms 中完成绘制,因而产生卡顿。
我们提出了 Expression Binding
方案用来解决这个问题。方案是在手势开始的时候,将具体的手势控制函数以表达式
的形式传递给 Native 层,当手势发生时,Native 根据预置的表达式解析器去解释执行表达式,并根据结果驱动视图变化。这样带来的好处是大大的减少了 native-JS 的通信次数,下面两幅图描述了传统方案与 Expression Binding
方案的差别:
图 1:传统方案
图 2:Expression Binding 方案
事实上,Expression Binding
不仅仅可以解决手势交互问题,任何 JS-native 频繁通信 + UI 更新的场景理论上都可以复用这套方案。比如:
- 监听容器的滚动,并基于滚动距离等变量更新UI如最常见的视差动画等;
- 监听陀螺仪方向变化数据,并更新 UI;
- 监听时间变化,更新 UI;
- ……
因此,我们将原方案进行了横向的扩展,实现了这些新的特性,并将它命名为 BindingX。2018 年 3 月,BindingX 正式开源,并同时支持了 React Native。
二. 特性一览
1. 手势能力
BindingX 能够监听元素的 pan 事件,基于此可以实现拖拽、卡片横滑等跟手的交互效果。更令人惊喜的是,类似 Weex Slider 这样的组件现在也可以使用 BindingX 来实现!
2. 动画
在 Weex 上实现动画通常的做法是使用 animation module
,现在有了新的选择。使用 BindingX 可以实现所有 animation module 能实现的效果,另外,BindingX 内置了 30 多组常见的插值器,可以自由选择,当然也可以使用 cubicBezier 贝塞尔曲线定制插值器。
3. 陀螺仪
BindingX 内置了陀螺仪监听器,可以监听设备方向变化。这在很多富交互场景中非常实用,比如在手机淘宝里,你可以看到很多基于陀螺仪的视差效果:
4. 列表滚动监听
BindingX 能够监听列表等滚动容器的 onScroll
事件,通过它可以实现酷炫的视差动画:
三. 使用方式
BiningX 同时支持 ReactNative 和 Weex,对于 Weex 来说不管你是使用 Rax 还是 Vue DSL,都没有关系。下面以 Weex 举例来说明如何使用 BindingX。
第一步:安装依赖
- 安装 npm 依赖
|
|
- 在 JS 代码中引入 BindingX 模块
|
|
第二步:编写表达式
- 根据业务场景,选择您需要的 eventType。 比如,要监听手势,eventType 值为 pan,监听滚动容器 scrollOffset 变化,eventType 值为 scroll。
- 根据交互行为,选择要改变的属性,并编写相应的表达式。比如,交互行为是“用户横滑 100 单位,透明度从 1 变化到 0”。则属性为 “opacity”,表达式为 “1 - x / 100”。
第三步:绑定表达式
根据第二步得到的 eventType、Expression 以及 Property,调用 BindingX 模块的 bind 方法,完成绑定。
|
|
当调用 bind 方法之后,native 会启动监听,当目标事件(比如手指滑动、设备方向变化等)发生的时候,便会执行您先前绑定的一组或者多组表达式。 bind 方法会返回一个 JS 对象,其中包含了一个 token 属性,可以使用这个 token 取消绑定。
更多细节,请参考我们的文档。
第四步:取消绑定
在合适的时机调用 BindingX 的 unbind 方法取消绑定。比如,页面不可见或者即将销毁的时候。
|
|
四. 内部细节
下面以 Android 为例从 native 的视角介绍下 BindingX 的具体实现,首先我们来梳理整个流程:
前端通过声明的方式定义具体的视图变化,每个视图变化过程都用一个三元组描述:
- element:目标元素。
- property:要改变的属性。
- expression:表达式。通过工具生成抽象语法树。
native 根据 eventType 注册对应的事件监听器,并将映射关系保存起来;
- 当指定的事件发生的时候,native 自行消费先前绑定的所有表达式,计算结果,并根据结果对视图进行更新。
这个过程可以用下面这张图描述:
在这个模型里,输入可以是手势事件、滚动事件、陀螺仪方向变化事件,而输出则是经过视图变换的view,视图变换的过程在 native 完成。而视图变换的规则是通过表达式
来描述的,一个表达式在前端声明之后,会先通过 parser 转成 Abstract syntax tree
,native 会通过预置的解析器来解析表达式树,并计算出结果,根据结果去驱动视图变化。
五. 更多想象力
事实上,BindingX
比我们想象的更加强大,在上面那张架构图中,输出部分画的是 transformed view
,但是事实上除了 view,我们还在探索更多有趣的玩法,比如:
- BindingX 和 Lottie 结合。用 BindingX 驱动
lottie
实现动画; - BindingX 和
Weex SVG
结合,实现好玩的轨迹动画、路径跟随动画,甚至是 morph 变形动画; - BindingX 和
Shader
结合,用 BindingX 来控制着色器! - ……
六. 下一步?
BindingX 在内部经过很长时间的孵化,在上层衍生出了很多通用的业务组件,它们涵盖了大部分的交互场景,诸如下拉刷新、转场、联动、视差动画,tab-panel、parallax 就是很好的例子。一个基于 BindingX
的前端交互体系正在成型,下一步我们会将它们逐渐开源到社区,敬请期待!