G6 圖可視化引擎——入門教程——圖的交互 Behavior

在這裏插入圖片描述

G6 封裝了一系列交互方法,方便用戶直接使用。本文將爲 Tutorial 案例 增加簡單的交互:hover 節點、點擊節點、點擊邊、放縮畫布、拖拽畫布。本節目標效果如下:

在這裏插入圖片描述

基本概念

交互行爲 Behavior

G6 中的交互行爲。G6 內置了一系列交互行爲,用戶可以直接使用。簡單地理解,就是可以一鍵開啓這些交互行爲:

  • drag-canvas:拖拽畫布;
  • zoom-canvas:縮放畫布。

交互管理 Mode

Mode 是 G6 交互行爲的管理機制,一個 mode 是多種行爲 Behavior 的組合,允許用戶通過切換不同的模式進行交互行爲的管理。

交互狀態 State

狀態 State 是 G6 中的狀態機制。用戶可以爲圖中的元素(節點/邊)設置不同的狀態及不同狀態下的樣式。在狀態發生變化時,G6 自動更新元素的樣式。例如,可以爲節點設置狀態 ‘click’ 爲 true 或 false,併爲節點設置 ‘click’ 的樣式爲加粗節點邊框。當 ‘click’ 狀態被切換爲 true 時,節點的邊框將會被加粗,‘click’ 狀態被切換爲 false 時,節點的樣式恢復到默認。

使用方法

拖拽、縮放——內置的交互行爲

在 G6 中使用內置 Behavior 的方式非常簡單,只需要在圖實例化時配置 modes。

拖拽和縮放屬於 G6 內置交互行爲,修改代碼如下:

const graph = new G6.Graph({
  // ...                                          // 其他配置項
  modes: {
    default: ['drag-canvas', 'zoom-canvas', 'drag-node'], // 允許拖拽畫布、放縮畫布、拖拽節點
  },
});

除了直接使用內置交互名稱外,也可以爲 Behavior 配置參數,例如放縮畫布的敏感度、最大/最小放縮程度等。

上面代碼中的 modes 定義了 G6 的模式,default 是默認的模式,還可以允許有其他的模式,比如:編輯模式 edit 等。不同的模式,用戶能進行的行爲可以不同,比如默認模式能拖拽畫布,編輯模式不允許拖拽畫布:

// 舉例解釋不同模式
modes: {
  default: ['drag-canvas'],
  edit: []
}

Hover、Click 改變樣式——狀態式交互

有時我們希望通過交互可以將元素樣式變成特定樣式,如我們看到的圖 1 中,鼠標 hover 節點、點擊節點、點擊邊時,樣式發生了變化。這裏涉及到了 G6 中 狀態 State 的概念。簡單地說,是否 hover 、click 、任何操作(可以是自己起的狀態名),都可以稱爲一種狀態(state)。用戶可以自由設置不同狀態下的元素樣式。要達到交互更改元素樣式,需要兩步:

  1. 設置各狀態下的元素樣式;
  2. 監聽事件並切換元素狀態。

設置各狀態下的元素樣式

在實例化圖時,通過 nodeStateStyles 和 edgeStateStyles 兩個配置項可以配置元素在不同狀態下的樣式。

爲達到 Tutorial 案例 中的效果:

  • 鼠標 hover 節點時,該節點顏色變淺;
  • 點擊節點時,該節點邊框加粗變黑;
  • 點擊邊時,該邊變成藍色。

下面代碼設置了節點分別在 hover 和 click 狀態爲 true 時的樣式,邊在 click 狀態爲 true 時的樣式:

const graph = new G6.Graph({
  // ...                           // 其他配置項
  // 節點不同狀態下的樣式集合
  nodeStateStyles: {
    // 鼠標 hover 上節點,即 hover 狀態爲 true 時的樣式
    hover: {
      fill: 'lightsteelblue',
    },
    // 鼠標點擊節點,即 click 狀態爲 true 時的樣式
    click: {
      stroke: '#000',
      lineWidth: 3,
    },
  },
  // 邊不同狀態下的樣式集合
  edgeStateStyles: {
    // 鼠標點擊邊,即 click 狀態爲 true 時的樣式
    click: {
      stroke: 'steelblue',
    },
  },
});

監聽事件並切換元素狀態

G6 中所有元素監聽都掛載在圖實例上,如下代碼中的 graph 對象是 G6.Graph 的實例,graph.on() 函數監聽了某元素類型(node / edge)的某種事件。

// 在圖實例 graph 上監聽
graph.on('元素類型:事件名', e => {
  // do something
});

現在,我們通過下面代碼,爲 Tutorial 案例 增加點和邊上的監聽事件,並在監聽函數裏使用 graph.setItemState() 改變元素的狀態:

// 鼠標進入節點
graph.on('node:mouseenter', e => {
  const nodeItem = e.item; // 獲取鼠標進入的節點元素對象
  graph.setItemState(nodeItem, 'hover', true); // 設置當前節點的 hover 狀態爲 true
});

// 鼠標離開節點
graph.on('node:mouseleave', e => {
  const nodeItem = e.item; // 獲取鼠標離開的節點元素對象
  graph.setItemState(nodeItem, 'hover', false); // 設置當前節點的 hover 狀態爲 false
});

// 點擊節點
graph.on('node:click', e => {
  // 先將所有當前是 click 狀態的節點置爲非 click 狀態
  const clickNodes = graph.findAllByState('node', 'click');
  clickNodes.forEach(cn => {
    graph.setItemState(cn, 'click', false);
  });
  const nodeItem = e.item; // 獲取被點擊的節點元素對象
  graph.setItemState(nodeItem, 'click', true); // 設置當前節點的 click 狀態爲 true
});

// 點擊邊
graph.on('edge:click', e => {
  // 先將所有當前是 click 狀態的邊置爲非 click 狀態
  const clickEdges = graph.findAllByState('edge', 'click');
  clickEdges.forEach(ce => {
    graph.setItemState(ce, 'click', false);
  });
  const edgeItem = e.item; // 獲取被點擊的邊元素對象
  graph.setItemState(edgeItem, 'click', true); // 設置當前邊的 click 狀態爲 true
});

完整代碼

至此,完整代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Tutorial Demo</title>
</head>
<body>
<div id="mountNode"></div>
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.1.0/build/g6.js"></script>
<script>
    const graph = new G6.Graph({
        container: 'mountNode',
        width: 800,
        height: 600,
        // 節點默認配置
        defaultNode: {
            labelCfg: {
                style: {
                    fill: '#fff',
                },
            },
        },
        // 邊默認配置
        defaultEdge: {
            labelCfg: {
                autoRotate: true,
            },
        },
        // 節點在各狀態下的樣式
        nodeStateStyles: {
            // hover 狀態爲 true 時的樣式
            hover: {
                fill: 'lightsteelblue',
            },
            // click 狀態爲 true 時的樣式
            click: {
                stroke: '#000',
                lineWidth: 3,
            },
        },
        // 邊在各狀態下的樣式
        edgeStateStyles: {
            // click 狀態爲 true 時的樣式
            click: {
                stroke: 'steelblue',
            },
        },
        // 佈局
        layout: {
            type: 'force',
            linkDistance: 200,
            preventOverlap: true,
            nodeStrength: -30,
            edgeStrength: 0.1,
        },
        // 內置交互
        modes: {
            default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
        },
    });

    const main = async () => {
        const response = await fetch(
            'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
        );
        const remoteData = await response.json();

        const nodes = remoteData.nodes;
        const edges = remoteData.edges;
        nodes.forEach(node => {
            if (!node.style) {
                node.style = {};
            }
            node.style.lineWidth = 1;
            node.style.stroke = '#666';
            node.style.fill = 'steelblue';
            switch (node.class) {
                case 'c0': {
                    node.shape = 'circle';
                    node.size = 30;
                    break;
                }
                case 'c1': {
                    node.shape = 'rect';
                    node.size = [35, 20];
                    break;
                }
                case 'c2': {
                    node.shape = 'ellipse';
                    node.size = [35, 20];
                    break;
                }
            }
        });
        edges.forEach(edge => {
            if (!edge.style) {
                edge.style = {};
            }
            edge.style.lineWidth = edge.weight;
            edge.style.opacity = 0.6;
            edge.style.stroke = 'grey';
        });

        graph.data(remoteData);
        graph.render();

        // 監聽鼠標進入節點
        graph.on('node:mouseenter', e => {
            const nodeItem = e.item;
            // 設置目標節點的 hover 狀態 爲 true
            graph.setItemState(nodeItem, 'hover', true);
        });
        // 監聽鼠標離開節點
        graph.on('node:mouseleave', e => {
            const nodeItem = e.item;
            // 設置目標節點的 hover 狀態 false
            graph.setItemState(nodeItem, 'hover', false);
        });
        // 監聽鼠標點擊節點
        graph.on('node:click', e => {
            // 先將所有當前有 click 狀態的節點的 click 狀態置爲 false
            const clickNodes = graph.findAllByState('node', 'click');
            clickNodes.forEach(cn => {
                graph.setItemState(cn, 'click', false);
            });
            const nodeItem = e.item;
            // 設置目標節點的 click 狀態 爲 true
            graph.setItemState(nodeItem, 'click', true);
        });
        // 監聽鼠標點擊節點
        graph.on('edge:click', e => {
            // 先將所有當前有 click 狀態的邊的 click 狀態置爲 false
            const clickEdges = graph.findAllByState('edge', 'click');
            clickEdges.forEach(ce => {
                graph.setItemState(ce, 'click', false);
            });
            const edgeItem = e.item;
            // 設置目標邊的 click 狀態 爲 true
            graph.setItemState(edgeItem, 'click', true);
        });
    };
    main();
</script>
</body>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章