搜尋此網誌

2023年11月27日 星期一

JavaScript Proxy 客製 Amchart 5 的 ForceDirected (一)

緣起:


    前陣子有好長的一段時間都在弄 amchart 5 的 ForceDirected 圖表,為了就是要產生快速查詢的圖表


    弄了好幾個禮拜才完成,中間碰上不少問題,但大部份的需求都是多看看它們的 API、然後設定某個屬性就能達成了。唯有一個需求,它很麻煩,不是設個屬性就能解決的,而且 API 裡面根本也沒有相對應的設定可以控制,最後,我是用 JavaScript 的 Proxy 來達成這個需求。

    這次的季會,我需要上台報告,原本是想要分享之前前弄的 prismjs 美化 Blogger 裡面的 code,但後來協理跟我說,這個東西好像太簡單,而且好像在專案上的實用性也不高,請我換個主題,所以我才會開始寫這篇文章,想說,在這邊紀錄完後,再做 PPT 也會比較方便。

    這篇先簡單介紹 ForceDirected 的使用,下篇再介紹碰到的問題跟解決方法。


ForceDirected 的基本:


    我是直接抄它們網站的範例,先用盡量短的程式碼來生個圖表出來看看,我是開個 asp webform 的專案來寫程式,主要是為了在專案啟動 iis express,之後 amchart 在存取資料夾裡的圖片才不會碰上問題。

    aspx 的頁面長這樣,要使用 amchart 的功能,需要引用它們的 js 函式庫,可以從它們提供的 CDN 來引用,或是下載下來在 local 端引用。index.js 是核心,必引用,再來是 Animated.js,圖表的動化效果,最後是 hierarchy.js,因為 ForceDirected 是屬於階層圖表。

    再來是在表單裡加個 id=myAmchartDiv 的 div,到時 amchart 圖表要掛載在它上面,設定它的寬為 100%,長為 800px。我把產生圖表的 js 碼寫在專案 Scripts 資料夾裡的 js 檔中

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="birdshiu.aspx.cs" Inherits="ForceDirected.birdshiu" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <script src="//cdn.amcharts.com/lib/5/index.js"></script>
    <script src="//cdn.amcharts.com/lib/5/themes/Animated.js"></script>
    <script src="//cdn.amcharts.com/lib/5/hierarchy.js"></script>
    
    <style type="text/css">
        #myAmchartDiv{
            width:100%;
            height:800px;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <div id="myAmchartDiv"></div>
        </div>
    </form>

    <script type="text/javascript" src="Scripts/index.js"></script>
</body>
</html>

    以下為 index.js

document.addEventListener("DOMContentLoaded", function () {
    let myAm5Root = am5.Root.new("myAmchartDiv"); //傳入 div 的 id 

    myAm5Root.setThemes([
        am5themes_Animated.new(myAm5Root) //設定 root 的動畫效果,預設即可
    ]);

    let myAm5Container = myAm5Root.container.children.push(//root 裡放入一個 container
        am5.Container.new(myAm5Root, {
            width: am5.percent(100),
            height: am5.percent(100),
            layout: myAm5Root.verticalLayout
        })
    );

    let myAm5ForceDirected = myAm5Container.children.push( //container 裡面放入 ForceDirected 圖表
        am5hierarchy.ForceDirected.new(myAm5Root, {
            downDepth: 1,
            initialDepth: 3, //初始展開層數
            topDepth: 1,
            valueField: "value",
            categoryField: "name",
            childDataField: "children"
        })
    );

    //圖表資料 (物件陣列)
    let myObjData = [{
        name: "Root",
        value: 0,
        children: [{
            name: "A0",
            value: 100,
            children: [{
                name: "A0A1",
                value: 80,
                children: [{
                    name: "A0A0A2",
                    value: 71
                }, {
                    name: "A0A0C2",
                    value: 48
                }]
            }, {
                name: "A0B1",
                value: 27
            }, {
                name: "A0C1",
                value: 70,
                children: [{
                    name: "A0C2A2",
                    value: 40
                }, {
                    name: "A0C2B2",
                    value: 40,
                    children: [{
                        name: "A0C2B1A3",
                        value: 54
                    }]
                }]
            }, {
                name: "A0D1",
                value: 89
            }]
        }]
    }];

    myAm5ForceDirected.data.setAll(myObjData); //設定 ForceDirected 的資料
    myAm5ForceDirected.set("selectedDataItem", myAm5ForceDirected.dataItems[0]);//採用第一個 Object
});

第 1 行 : amchart 一定要有個 root,new 裡面傳的是要被掛載的 div 的 id。

第 4 行 : 設定 root 的動畫效果,不用特別多做其它設定。

第 16 行 : 把 ForceDirected 圖表放進 Container 裡,ForceDirected 的屬性設定可以查看它的 API 文件,比較重要的是那三個 Field 有關的設定,它是用來指定 value、category、childData 是要參考 Object 資料的哪個 key。

第 27 行 : ForceDirected 的資料,它好像一定要是一個物件陣列,我們的範例只會用到一個物件而已。Root 必需要有,但真正會呈現為圖表的會是它的 children。整個物件資料的架構會跟樹一樣。

第 81 行 : 用 ForceDirected 的 data 的 setAll 可以設定圖表的資料來源。

第 82 行 : 用 set 方法來設定 ForceDirected 的 selectedDataItem,就是圖表目前要呈現的資料集。可以用 dataItems 來取得圖表的來源資料,因為是陣列資料,又只有一筆資料,所以取[0]。

    最後呈現出來的圖表會長這樣

See the Pen Untitled by 鳥旭 (@lbdjszpl-the-lessful) on CodePen.


    以上是一個 Amchart 5 ForceDirected 圖表的基本。


補充:


    我想補充一些我在研究 Amchart5 時,最常使用到的功能

    在新增一個元件時,通常都是寫這樣的語法,可以看上面 "ForceDirected 的基本" 程式的 8、16 行,如果加完新的元件後,想要再對它做操作的話,可以用個變數紀錄,它 push 成功後會回傳那個新加入的元件。

    可以用 set 或 setAll 來設定元件的屬性,set 是設定單一屬性,傳入的是屬性名稱跟值,可以是字串、數字...,就看那個屬性接受的是什麼類型的資料。setAll 是可以一次設定多個屬性質,傳入的是 Object。在 API 文件中,只要是條列在 Settings 部份的屬性都能透過 set 與 setAll 來設定。


    有些 class 會有 template 這個屬性可以存取,這邊舉 ForceDirected 的 nodes 來說,它是一個 ListTemplate,有點像 C# 的 List,是 Generic Type,這邊放的 class 是 LinkedHierarchyNode。如果我們想要設定那些 node 的屬性,雖然可以用 ListTemplate 的 each 來逐一設定每個 node,但其實直接用 template 去設定會更簡潔。
    

    以下是 ForceDirected 的 nodes 使用 template 的例子,一次設定好每個 LinkedHierarchyNode 的 draggable 屬性,讓所有的球球都不能被拖拉

myAm5ForceDirected.nodes.template.set("draggable", false); //設定球球不能拖拉

    設定完後,球球就不能被拖拉了

See the Pen ForceDirected-1 by 鳥旭 (@lbdjszpl-the-lessful) on CodePen.


    再舉兩個例子,如果把 toggleKey 設定成 "none" 的話,點擊球球就不會打開跟收合。如果不想看到外層的圈圈的話,可以把 outerCircles 的 forceHidden 設成 true。

myAm5ForceDirected.nodes.template.set("toggleKey", "none");//設定球球不能點擊展開或收合
myAm5ForceDirected.outerCircles.template.set("forceHidden", true); //把外層的圓圈給藏起來

    設定完後的結果

See the Pen ForceDirected-2 by 鳥旭 (@lbdjszpl-the-lessful) on CodePen.


    以上。下一篇會接著講我碰到的困難以及如何用 Proxy 來解決

沒有留言:

張貼留言