在2017年1月12日 Weex Conf 2017上,来自阿里的寒泉结合Weex的背景、动画和手势的技术分析,从Weex存在的实际问题入手,提出了新的构想和新方案expression binding。Weex总体来说性能较好,能够解决很多问题,具有协同多个业务在同一个大型APP上的能力,本文主要介绍怎么样通过Weex提升用户体验。
背景
基于某个业务,产品经理和设计师想实现一个页面,该页面是卡片型的List,既能像List一样水平方向滑动,又能在垂直方向获取类似详情页的功能,这种需求在web时代比较难实现。但是有了Weex之后,这个设计变得相对容易了。
技术分析
我们首先需要把效果拆成我们所能够认识的特性。实际上,效果是非常复杂的,是同时几个动画在一起变。我们可以看到,在横滑过程中,最显眼的是卡片的变化,卡片随着手指的拖动产生了大小的变化,并且产生了位移,映射到CSS属性上即利用到了transform里面的scale和translate两个属性。背景是一张做了高斯模糊的图片,当切换卡片的时候有一个图片的变化,随着手指的拖动实际上是两张图片透明度的变化,一个增加一个减小,总和为100%,用到了CSS的opacity属性。界面上有一个细节,标题文字也发生了变化,位置和透明度都发生了变化,即用到了CSS中的opacity和translate属性。同理,向上推的动画是通过scale和translate实现的。
经过上述分析,我们可以得出一个结论:看似复杂的效果,我们都可以通过手势和动画两个特性来解决。
Weex动画
动画的代码比较简单,Weex提供了一个API,有weex-module/animation这一特性,其调用与CSS的transition类似,API的名字也叫animation.transition。具体的写法是,首先需要style样式,从性能考虑,一次可以指定多个属性,上例中指定了color、transform、transformOrigin三个属性,duration为0ms,timingFunction为ease,delay为0ms,最后还有一个回调。
Weex手势
Weex支持4种手势:长按press,单机tap,拖拽pan,轻划swipe。比如拖拽效果,我们只要把两个Weex里的data绑定在元素的top和left上,监听它的onPanStart和onPanMove事件,根据屏幕上的位移改变对应的x和y的值。实际上只是把其计算的差量累加到原始值上面,这样就形成了拖拽的效果。
问题
在实际的应用过程中,发现拖拽效果做demo的时候比较顺利,但实际业务上需要拖拽的东西相当复杂而且几个动画同时播放,上述的几种手势在ios上的性能可以接受,在某些安卓机型上会出现卡顿。下面进行分析,事件触发的原理首先从native侧接收到gesture事件,通过JS和native的bridge将其调给JS,JS找到相应的gesture handler执行JS代码,执行完之后更新到了虚拟DOM上,通过VDOM的compare算法到达real element上。上述过程会在每一次的手指移动的触发过程中执行一遍,频率非常高。
构想
基于上述问题,提出了新的构想:在JS侧创建一个expression,把属性更新的规则(到底要更新哪个属性?每个属性怎么更新?)建立好,然后到native侧去监听gesture,并且更新到real element上,等到这个过程结束(手松开之后)再将其更新回VDOM Sync上。这与之前的做法是完全相反的过程。为了在一些环节上追求高性能、灵活性,有上述两种处理方法。为此,我们设计了新的方案。
新方案——expression binding
上图是最初设计的一个API,在onPanGestureStart的时候,create一个binding,将元素、属性以及最终的表达式传递进去。具体实践的时候也遇到了瓶颈,比如表达式的解析。最终选定的技术方案平衡了开发成本和最终性能。
比如有一个表达式x+y*2,经过JS的Parser解析成标准表达式树,根据四则运算的优先级,y*2先算然后与x相加,从叶子节点往根节点一直解析执行就能算出最后的结果。在gesture start后,JS会将这棵树构造好变成Json传递给native,经过native侧的interpreter按照上述流程执行树,native通过gesture把x和y变量取出,传递给interpreter,表达式就会获得最终结果。通过上面的方式,极大地增强了在手势的情况下操作DOM的效率。