螞蟻金服 AntV G2 交互語法之框選高亮

G2 從 4.0 開始,將全部的交互行爲使用全新的交互語法實現,而且再也不默認內置,須要用戶顯式調用
chart.interaction() 接口。爲了幫助你們更好地理解、使用交互語法,咱們將會推出交互語法專題。javascript

本文介紹的是最多見的交互:框選,包括框選的形狀、框選過程當中的圖形變化以及框選後的各類操做。java

框選中,高亮圖形
b5.gif
框選後,拖拽 mask
b1.gif
框選後,過濾數據
b2.gif
框選後,過濾圖形
b3.gif
框選的形狀選擇
b6-2.gif
多視圖的框選聯動
b4.gif

交互語法概覽

G2 的交互語法,是將交互拆解成多個環節,每一個環節由觸發和反饋組成。只要你能將交互用天然語言的方式描述出來,就可使用 G2 的交互語法進行組合搭建出交互行爲。在這裏咱們再一塊兒溫習下 G2 交互語法中對於交互環節的定義,更詳細的內容能夠閱讀可視化交互語法。git

G2 將每個交互環節拆解成如下步驟:github

  • showEnable 示能:表示交互能夠進行;
  • start 開始:交互開始;
  • processing 持續:交互持續;
  • end 結束:交互結束;
  • rollback 回滾:取消交互,恢復到原始狀態;

下面咱們就開始框選高亮圖形的交互語法組裝吧,爲了幫助你們理解,每一個交互行爲咱們都會以天然語言 + 交互語法的形式向你們闡述。咱們以柱狀圖的高亮爲例,實現交互的過程當中咱們會使用 G2 內置的 Action,Action 的定義和列表參考 G2 配置交互。markdown

框選中,高亮圖形

b6.gif
b5.gif

交互的語言描述

  1. 鼠標進入繪圖區域時變成十字,鼠標離開繪圖區域時恢復正常;
  2. 按下鼠標,拖動鼠標開始框選,出現框選的遮罩層(mask),框選過程當中被框選的圖形高亮;
  3. 鬆開鼠標按鍵,框選結束;
  4. 雙擊畫布,隱藏遮罩層,圖形的高亮效果。

G2 交互語法

registerInteraction('element-range-highlight', {
  showEnable: [
    { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
    { trigger: 'plot:mouseleave', action: 'cursor:default' },
  ],
  start: [
    {
      trigger: 'plot:mousedown',
      action: ['rect-mask:start', 'rect-mask:show'],
    }
  ],
  processing: [
    {
      trigger: 'plot:mousemove',
      action: ['rect-mask:resize', 'element-range-highlight:highlight'],
    },
  ],
  end: [
    { trigger: 'plot:mouseup',
      action: ['rect-mask:end']
    },
  ],
  rollback: [
    { 
      trigger: 'dblclick', 
      action: ['element-range-highlight:clear', 'rect-mask:hide'] 
   	}
  ],
});
複製代碼

咱們使用了三個 Action 來實現這個區域高亮功能:ssh

  • cursor控制鼠標樣式的 Action,這個 Action 的方法支持全部的鼠標樣式,例如 'pointer'、'crosshair'、'move' 等。
  • rect-mask 矩形的遮罩層,這個 Action 支持的方法有:
    • start:開始,表示遮罩層開始改變;
    • show:顯示;
    • resize: 改變形狀;
    • hide: 隱藏;
    • end: 結束。
  • element-range-highlight圖表元素的區域高亮,支持的方法有:
    • highlight:高亮;
    • clear: 清除高亮。

**
交互語法解釋:ide

  • 咱們使用了多個環節來組裝框選高亮的交互: showEnable 意味着交互是否能夠進行; start 表示交互開始進行; processing 表示交互持續進行; end 表示交互結束; rollback 表示交互回滾。這些過程不徹底是順序的,例如:框選結束後,不須要回滾就能夠繼續開始新的框選; showEnable 在各個環節中都生效。
  • 你能夠更改任何環節中的實現,例如你能夠在 end 環節中的 action 增長 'rect-mask:hide' 這時候框選結束後遮罩層消失,可是框選的高亮效果還存在。

框選後,拖拽 mask

框選高亮後咱們能夠開始新的框選,可是若是可以拖拽遮罩層 (mask)使得被遮罩的圖形高亮,體驗更好。
oop

b1.gif

交互的語言描述

這個交互的步驟以下:spa

  1. 當鼠標進入繪圖區域時鼠標變成十字,當鼠標離開繪圖區域時鼠標恢復正常;
  2. 當鼠標進入遮罩層時鼠標變成移動形狀,當鼠標離開遮罩層時鼠標變成十字;
  3. 在不是遮罩層的地方按下鼠標並移動鼠標(拖拽),開始顯示遮罩層,拖拽過程當中遮罩層跟隨鼠標變化,同時被遮擋的圖形高亮;
  4. 鬆開鼠標框選結束;
  5. 當拖拽遮罩層時,遮罩層跟隨鼠標移動,被遮擋的圖形高亮;
  6. 鬆開鼠標拖動遮罩層結束;
  7. 雙擊畫布,遮罩層隱藏,圖形高亮效果取消。

注意這個交互同上面交互的差別,新增了 二、5 和 6 三個步驟,遮罩層的變化 3 須要增長觸發條件。code

G2 交互語法

registerInteraction('element-range-highlight', {
  showEnable: [
    { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
    { trigger: 'mask:mouseenter', action: 'cursor:move' },
    { trigger: 'plot:mouseleave', action: 'cursor:default' },
    { trigger: 'mask:mouseleave', action: 'cursor:crosshair' },
  ],
  start: [
    {
      trigger: 'plot:mousedown',
      isEnable(context) { // 不要點擊在 mask 上從新開始
        return !context.isInShape('mask');
      },
      action: ['rect-mask:start', 'rect-mask:show'],
    },
    {
      trigger: 'mask:dragstart',
      action: ['rect-mask:moveStart']
    }
  ],
  processing: [
    {
      trigger: 'plot:mousemove',
      action: ['rect-mask:resize'],
    },
    {
      trigger: 'mask:drag',action: ['rect-mask:move']
    },
    {
      trigger: 'mask:change', action: ['element-range-highlight:highlight']
    }
  ],
  end: [
    { trigger: 'plot:mouseup',
      action: ['rect-mask:end']
    },
    { trigger: 'mask:dragend', action: ['rect-mask:moveEnd']},
  ],
  rollback: [{ trigger: 'dblclick', action: ['element-range-highlight:clear', 'rect-mask:hide'] }],
});
複製代碼

咱們根據這個交互同前一個交互的差異,來逐條增長新的步驟(觸發和反饋):

  • **2 **在 showEnable 上增長鼠標移入 mask 和移出 mask 的效果。
  • 3start 環節中觸發遮罩層變化的步驟中增長是否在遮罩層上觸發的斷定。
  • 5start 環節中增長拖拽遮罩層,在 processing 中增長遮罩層移動、圖形根據遮罩層變化而高亮的步驟。
  • 6 在 end 環節中結束遮罩層的移動。

經過這個示例,咱們能夠看到如何來擴展一個交互。框選自己不是交互的目的,框選後的操做纔是框選交互的目的,框選後能夠進行數據過濾、詳情展現、顯示隱藏等,下面咱們經過幾個交互來進行說明。

框選後,過濾數據

框選後對數據進行過濾時常見的操做,爲了讓用戶意識到過濾已經發生,而且顯示的告訴用戶如何恢復,咱們在框選發生過濾夠顯示了一個 reset 按鈕。

b2.gif

交互的語言描述

  1. 鼠標進入繪圖區域變成十字,鼠標離開繪圖區域恢復正常;
  2. 鼠標移入 reset 按鈕變成 pointer,鼠標離開變成十字;
  3. 按下鼠標拖拽出現遮罩層,拖拽過程當中遮罩層變化;
  4. 鬆開鼠標,對圖表的數據進行過濾,並顯示 reset 按鈕;
  5. 點擊 reset 按鈕,數據過濾取消。

G2 交互語法

registerInteraction('brush', {
  showEnable: [
    { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
    { trigger: 'plot:mouseleave', action: 'cursor:default' },
    { trigger: 'reset-button:mouseenter', action: 'cursor:pointer' },
    { trigger: 'reset-button:mouseleave', action: 'cursor:crosshair' },
  ],
  start: [
    {
      trigger: 'plot:mousedown',
      action: ['brush:start', 'rect-mask:start', 'rect-mask:show'],
    },
  ],
  processing: [
    {
      trigger: 'plot:mousemove',
      action: ['rect-mask:resize'],
    },
  ],
  end: [
    {
      trigger: 'plot:mouseup',
      action: ['brush:filter', 'brush:end', 'rect-mask:end', 
               'rect-mask:hide', 'reset-button:show'],
    },
  ],
  rollback: [
    { 
      trigger: 'reset-button:click', 
     	action: ['brush:reset', 'reset-button:hide', 'cursor:crosshair'] 
    }],
});
複製代碼

咱們使用了四個 Action 來實現這個區域高亮功能, cursor 和 rect-mask 已經介紹過,這裏介紹其餘兩個:

  • brush 經過指定範圍來過濾數據,有下面幾個方法:
    • start 開始過濾的位置
    • end 結束過濾的位置
    • filter 過濾
    • reset 恢復過濾
  • reset-button恢復按鈕,僅有顯示和隱藏兩個方法:
    • show 顯示
    • hide 隱藏

**
幾點說明:

  • G2 內部提供了基礎的 button Action,全部個性化的 button 均可以從其中擴展出來,這個示例中的 Action(reset-button) 就是修改文本和位置而生成的。
  • Action brush 經過開始位置和結束位置肯定過濾的區域,另外還有兩個相似的 Action:
    • brush-x 僅僅過濾 x 軸範圍內的數據;
    • brush-y 僅僅過濾 y 軸範圍內的數據。

對應 rect-mask 也有兩個 Action: x-rect-mask,y-rect-mask。

b6-1.gif

框選後,過濾圖形

框選後不進行數據過濾,而僅僅控制圖形的顯示隱藏也是常見的,交互,咱們來看一下這個交互的實現:

b3.gif

交互的語言描述

  1. 鼠標進入繪圖區域變成十字,鼠標離開繪圖區域恢復正常;
  2. 鼠標按下,拖拽過程當中遮罩層變化大小,被遮擋的圖形高亮;
  3. 鬆開鼠標,被遮擋的圖形保留,其餘圖形隱藏;
  4. 雙擊畫布,取消圖形過濾。

G2 交互語法

registerInteraction('brush-visible', {
  showEnable: [
    { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
    { trigger: 'plot:mouseleave', action: 'cursor:default' },
  ],
  start: [
    {
      trigger: 'plot:mousedown',
      action: ['rect-mask:start', 'rect-mask:show', 'element-range-highlight:start'],
    },
  ],
  processing: [
    {
      trigger: 'plot:mousemove',
      action: ['rect-mask:resize','element-range-highlight:highlight'],
    },
    {trigger: 'mask:end',action: ['element-filter:filter']}
  ],
  end: [
    {
      trigger: 'plot:mouseup',
      action: ['rect-mask:end', 'rect-mask:hide', 
               'element-range-highlight:end', 'element-range-highlight:clear'],
    },
  ],
  rollback: [
    {
      trigger: 'dblclick',
      action: ['element-filter:clear']
    }
  ]
});
複製代碼

這個交互中咱們使用到了,其中 cursorrect-maskelement-range-highlight 三個 Action 前面已經介紹到,這裏對新的 Action element-filter 進行說明:

  • element-filter[8]:過濾圖表元素,有兩個方法:
    • filter:過濾
    • clear:清理過濾

從上面的幾個交互咱們能夠看到,多個交互之間會共享大量的 Action,這就解決了交互代碼複用的問題,爲提高開發交互的效率和提高質量提供了保障。

擴展

框選的形狀選擇

b6-2.gif
b6-3.gif

交互的語言描述

這個交互的步驟同前面的幾個交互相似,最大的差異在於:鼠標在畫布上拖拽時,根據鼠標移動的軌跡改變遮罩層的形狀

G2 交互語法

registerInteraction('element-range-highlight', {
  showEnable: [
    { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
    { trigger: 'mask:mouseenter', action: 'cursor:move' },
    { trigger: 'plot:mouseleave', action: 'cursor:default' },
    { trigger: 'mask:mouseleave', action: 'cursor:crosshair' },
  ],
  start: [
    {
      trigger: 'plot:mousedown',
      isEnable(context) { // 不要點擊在 mask 上從新開始
        return !context.isInShape('mask');
      },
      action: ['path-mask:start', 'path-mask:show'],
    },
    {
      trigger: 'mask:dragstart',
      action: ['path-mask:moveStart']
    }
  ],
  processing: [
    {
      trigger: 'plot:mousemove',
      action: ['path-mask:resize'],
    },
    {
      trigger: 'mask:drag',action: ['path-mask:move']
    },
    {
      trigger: 'mask:change', action: ['element-range-highlight:highlight']
    },
    {trigger: 'mask:end',action: ['element-filter:filter']}
  ],
  end: [
    { trigger: 'plot:mouseup',
      action: ['path-mask:end']
    },
    { trigger: 'mask:dragend', action: ['path-mask:moveEnd']},
  ],
  rollback: [
    { 
      trigger: 'dblclick', 
     	action: ['element-range-highlight:clear', 
               'path-mask:hide', 'element-filter:clear'] 
    }],
});
複製代碼

這個交互同上面 「框選後的操做-拖拽」 徹底一致,除了顯示遮罩層的 Action 從 rect-mask 替換成 path-mask 以外,這個 Action 同 rect-mask 的方法徹底一致:

  • path-mask 矩形的遮罩層,這個 Action 支持的方法有:
    • start:開始,表示遮罩層開始改變
    • show:顯示
    • resize: 改變形狀
    • hide: 隱藏
    • end: 結束

除了 rect-maskpath-mask 以外 G2 還內置了其餘兩種 circle-masksmooth-path-mask,除了形狀不一樣外提供的方法徹底相同。

多視圖的框選聯動

b4.gif

交互的語言描述

  1. 鼠標進入一個 View 的繪圖區域時變成十字,離開 View 的繪圖區域時恢復正常;
  2. 鼠標按下進行框選,在當前 View 上顯示遮罩層,同時被遮擋的圖形高亮;其餘 View 上同當前高亮圖形的數據對應的圖形同時高亮;
  3. 鬆開鼠標,遮罩層結束改變;
  4. 在遮罩層上拖拽,被遮擋的圖形高亮;其餘 View 上同當前高亮圖形的數據對應的圖形同時高亮;
  5. 鬆開鼠標,遮罩層位置改變結束;
  6. 雙擊畫布,高亮效果取消。

G2 交互語法

registerInteraction('highlight-view', {
  showEnable: [
    { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
    { trigger: 'mask:mouseenter', action: 'cursor:move' },
    { trigger: 'plot:mouseleave', action: 'cursor:default' },
    { trigger: 'mask:mouseleave', action: 'cursor:crosshair' },
  ],
  start: [
    { trigger: 'plot:mousedown',isEnable(context) {
      return !context.isInShape('mask');
    }, action: ['rect-mask:start', 'rect-mask:show'] },
    {trigger: 'mask:dragstart', action: 'rect-mask:moveStart'}
  ],
  processing: [
    { trigger: 'plot:mousemove', action: 'rect-mask:resize' },
    { trigger: 'mask:drag', isEnable(context) {
      return context.isInPlot();
    }, action: 'rect-mask:move'},
    { trigger: 'mask:change', 
     action: ['element-sibling-highlight:highlight', 
              'element-range-highlight:highlight'] }
  ],
  end: [
    { trigger: 'plot:mouseup', action: 'rect-mask:end' },
    { trigger: 'mask:dragend', action: 'rect-mask:moveEnd' },
    {
      trigger: 'document:mousedown',
      isEnable(context) {
        return !context.isInPlot();
      },
      action: ['element-sibling-highlight:clear',
               'element-range-highlight:clear', 
               'rect-mask:end', 'rect-mask:hide'],
      once: true,
    },
    {
      trigger: 'document:mouseup',
      isEnable(context) {
        return !context.isInPlot();
      },
      action: ['rect-mask:end'],
      once: true,
    }
  ],
  rollback: [
    { 
     trigger: 'dblclick', 
     action: ['rect-mask:hide', 'element-sibling-highlight:clear', 
              'element-range-highlight:clear']}
  ]
});
複製代碼

這個交互同前面提到 「框選後的操做-拖拽」 幾乎如出一轍,除了在調用 Action element-range-highlight 的同時也調用了 element-sibling-highlight 的方法,其含義是「高亮圖形」 的同時 「高亮全部同級 views 的圖形」。

  • element-sibling-highlight[10]:高亮當前 view 同一級的 views 的對應圖形,這個 Action 的方法有:
    • highlight:高亮
    • clear: 清除高亮

**
更多的解釋:
細心的讀者可能關注到這個交互中出現了幾個前面交互中沒有出現的步驟(觸發和反饋),這些都是一些異常處理的步驟,這關係到一個交互的質量:

  • document:mousedown 繪圖區域以外按下起鼠標(面上其餘位置 ),結束高亮而且隱藏遮罩層。
  • document:mouseup 繪圖區域以外擡起鼠標(面上其餘位置 ),則結束 rect-mask 的變化。
  • mask:drag 時添加約束條件,若是再也不當前 View 的繪圖區域,則再也不移動。

b7.gif

除了多個 View 之間聯動高亮,還能夠進行聯動過濾、圖形隱藏、tooltip 聯動等操做,咱們會在後面的章節中給你們介紹。

總結

框選高亮做爲圖表中常用的交互,在不一樣的場景下框選的過程和結果都有可能變化,傳統的固定死的交互方式並不能知足用戶的需求。當咱們有了交互語法,只要你能列出交互的步驟,就能夠將這些步驟天然地轉換成交互語法,高效並且高質,快去嘗試一下吧!

網址

G2 官網: g2.antv.vision/zh/

github: github.com/antvis/G2

參考資料

  1. 可視化交互語法
  2. G2 內置交互反饋
  3. cursor
  4. rect-mask
  5. element-highlight
  6. brush
  7. reset-button
  8. element-filter
  9. path-mask
  10. element-sibling-highlight
相關文章
相關標籤/搜索