基於 BindingX 的富交互解決方案 基於 BindingX 的富交互解決方案

基於 BindingX 的富交互解決方案

BindingX 官網: https://alibaba.github.io/bindingx/
BindingX 項目地址: https://github.com/alibaba/bindingx

一. 背景

在 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 更新的場景理論上都可以複用這套方案。比如:

  1. 監聽容器的滾動,並基於滾動距離等變量更新UI如最常見的視差動畫等;
  2. 監聽陀螺儀方向變化數據,並更新 UI;
  3. 監聽時間變化,更新 UI;
  4. ……

因此,我們將原方案進行了橫向的擴展,實現了這些新的特性,並將它命名爲 BindingX。2018 年 3 月,BindingX 正式開源,並同時支持了 React Native。

二. 特性一覽

1. 手勢能力

BindingX 能夠監聽元素的 pan 事件,基於此可以實現拖拽、卡片橫滑等跟手的交互效果。更令人驚喜的是,類似 Weex Slider 這樣的組件現在也可以使用 BindingX 來實現!

demo1
demo2
demo3

2. 動畫

在 Weex 上實現動畫通常的做法是使用 animation module ,現在有了新的選擇。使用 BindingX 可以實現所有 animation module 能實現的效果,另外,BindingX 內置了 30 多組常見的 插值器 ,可以自由選擇,當然也可以使用 cubicBezier 貝塞爾曲線定製插值器。

demo4
demo6

3. 陀螺儀

BindingX 內置了陀螺儀監聽器,可以監聽設備方向變化。這在很多富交互場景中非常實用,比如在手機淘寶裏,你可以看到很多基於陀螺儀的視差效果:

demo7
demo8

4. 列表滾動監聽

BindingX 能夠監聽列表等滾動容器的 onScroll 事件,通過它可以實現酷炫的視差動畫:

demo9
demo10

三. 使用方式

BiningX 同時支持 ReactNative 和 Weex,對於 Weex 來說不管你是使用 Rax 還是 Vue DSL,都沒有關係。下面以 Weex 舉例來說明如何使用 BindingX。

第一步:安裝依賴
  • 安裝 npm 依賴
$ npm install weex-bindingx --save
  • 在 JS 代碼中引入 BindingX 模塊
import BindingX from weex-bindingx;
第二步:編寫表達式
  • 根據業務場景,選擇您需要的 eventType。 比如,要監聽手勢,eventType 值爲 pan,監聽滾動容器 scrollOffset 變化,eventType 值爲 scroll。
  • 根據交互行爲,選擇要改變的屬性,並編寫相應的表達式。比如,交互行爲是“用戶橫滑 100 單位,透明度從 1 變化到 0”。則屬性爲 “opacity”,表達式爲 “1 - x / 100”。
第三步:綁定表達式

根據第二步得到的 eventType、Expression 以及 Property,調用 BindingX 模塊的 bind 方法,完成綁定。

let result = BindingX.bind({
eventType: 'pan', ==> 事件類型
anchor: 'foo', ==> anchor 指的是事件的觸發者,如果是 eventType 是 “orientation” 或 “timing”,則不用填
props: [
{
element: view.ref, ==> 要改變的視圖的引用或者 id
expression: "1 - x / 100", ==> 表達式
property: "opacity" ==> 要改變的屬性
}
]
})

當調用 bind 方法之後,native 會啓動監聽,當目標事件(比如手指滑動、設備方向變化等)發生的時候,便會執行您先前綁定的一組或者多組表達式。 bind 方法會返回一個 JS 對象,其中包含了一個 token 屬性,可以使用這個 token 取消綁定。

更多細節,請參考我們的 文檔

第四步:取消綁定

在合適的時機調用 BindingX 的 unbind 方法取消綁定。比如,頁面不可見或者即將銷燬的時候。

BindingX.unbind({
token: result.token,
eventType: 'pan'
})

四. 內部細節

下面以 Android 爲例從 native 的視角介紹下 BindingX 的具體實現,首先我們來梳理整個流程:

  1. 前端通過聲明的方式定義具體的視圖變化,每個視圖變化過程都用一個三元組描述:

    • element:目標元素。
    • property:要改變的屬性。
    • expression:表達式。通過工具生成抽象語法樹。
  2. native 根據 eventType 註冊對應的事件監聽器,並將映射關係保存起來;

  3. 當指定的事件發生的時候,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 的前端交互體系正在成型,下一步我們會將它們逐漸開源到社區,敬請期待!

基於 BindingX 的富交互解決方案

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章