基於 HTML5 WebGL 的 3D “彈力”布局


分子力(molecular force),又稱分子間作用力、范得瓦耳斯力,是指分子間的相互作用。當二分子相距較遠時,主要表現為吸引力,這種力主要來源於一個分子被另一個分子隨時間迅速變化的電偶極矩所極化而引起的相互作用;當二分子非常接近時,則排斥力成為主要的,這是由於各分子的外層電子雲開始重疊而產生的排斥作用。

HT for Web 提供了彈力布局(也稱為力導向布局)的功能,即根據節點之間存在互斥力,相互連接的節點間存在引力, 彈力布局運行一段時間后,整體拓撲網絡結構會逐漸達到收斂穩定的平衡狀態。這個功能很有趣,今天我們就將它的魅力展現出來。

本例地址:http://www.hightopo.com/demo/pipeline/index.html

使用彈力布局功能需要在引入 ht.js 核心庫之后,再引入一個 ht-forcelayout.js 的彈力布局插件庫,因為還用到了 form 表單,所以要引入 ht-form.js 的表單插件庫:

<script src="../../guide/lib/core/ht.js"></script>
<script src="../../guide/lib/plugin/ht-forcelayout.js"></script>
<script src="../../guide/lib/plugin/ht-form.js"></script>

ht.layout.Force3dLayout 類提供 3D 彈力布局,構造函數可傳入 DataModel Graph3dView 兩種參數。 默認僅對未選中圖元進行布局,如果構造函數參數為 Graph3dView 時,則視圖組件的 isMovable isVisible 函數將影響圖元是否可布局, 圖元 style 上的 layoutable 屬性也可設為 false 阻止圖元參與布局。

介紹完 HT 封裝的彈力布局的背景之后,接下來就是幫助你們也能輕松地實現這個效果。

首先我們定義一個顏色數組變量,存儲各個彈力球的顏色,還定義了一個隨機函數,用來生成數隨機的數組中的顏色:

var colorList = ['#FFAFA4', '#B887C5', '#B9EA9C', '#CFD9E7', '#4590B8', '#FF9C30'], 
    colorLen = colorList.length;
var randomColor = function() {
    var ran = Math.random() * colorLen;
    return colorList[Math.floor(ran)];//隨機6種顏色
};

接着創建彈力球,簡單生成一個 3D 節點,通過設置這個節點的 style 樣式屬性來控制節點的顯示方式,其中將“shape3d”設置為“sphere”即可將 ht.Node 六面體變成 3D 球體模型,再設置“shape3d”屬性為前面定義的隨機顏色,s3 是 HT 封裝的設置 3D 節點大小的 setSize3d 函數的簡寫,最后將這個節點添加進數據模型 dataModel 中:

var createNode = function(dm) {//創建node節點 圓
    var node = new ht.Node();
    node.s({//設置樣式為 setStyle 的簡寫
        'shape3d': 'sphere',
        'shape3d.color': randomColor()//設置隨機顏色
    });
    node.s3(40, 40, 40);
    dm.add(node);
    return node;
};

現在效果圖上出現的還有各個彈力球之間的連線,這個連線我們一看就覺得很不一般,也是通過構造一個一個節點,這個節點是通過 HT for Web 建模手冊 setShape3dModel函數自定義的 ht.Default.createRingModel 根據 xy 平面的曲線,環繞一周形成的 3D 環形模型,將其命名為‘custom’:

ht.Default.setShape3dModel(//創建模型 根據xy平面的曲線,環繞一周形成3D模型。
    'custom', ht.Default.createRingModel( [0.5, 0.5, -0.2, 0, 0.5, -0.5], [1, 3] )
);

HT 將用戶自定義的屬性和 HT 默認的屬性調用方法分為 node.a 和 node.s 這樣就能將兩者有效地區分開來(具體參照 HT for Web 入門手冊 style 章節),我們在創建管線的時候就用了這種方法:

var updatePipeline = function(edge) {//重新設置edge的樣式
    var pipeline = edge.a('pipeline');
    pipeline.s3(1, 1, 1);//設置大小
    pipeline.p3(0, 0, 0);//設置坐標

    var node1 = edge.getSourceAgent(),//獲取圖形上連接的起始節點
    node2 = edge.getTargetAgent();//獲取圖形上連接的目標節點
    pipeline.s('mat', createMatrix(node1.p3(), node2.p3(), 20));//3d整體圖形矩陣變化
};

最神秘的是如何能做出讓兩個節點“若即若離”的效果?

我們知道,矩陣能描述任意線性變換。線性變換保留了直線和平行線,線性變換保留直線的同時,其他的幾何性質如長度、角度、面積和體積可能被變換改變了。簡單的說,線性變換可能“拉伸”坐標系,但不會“彎曲”或“卷折”坐標系。這個函數主要是將我們的連接線在拖動彈力球后被拖拉的連接線的進行一個“變化矩陣”的操作,變化矩陣也是 HT 封裝的 ht.Default.createMatrix 函數,通過將節點的 style 屬性 mat 設置為一個自定義的函數,就是將這個節點的坐標乘上在“mat”屬性對應的值,也就是說如果當前這個管線的旋轉角為 [Math.PI/6, 0, 0],假設我們在 createMatrix 函數中設置 r3 為 [Math.PI/3, 0, 0],那么這個節點會旋轉 90 度。非常輕松地創建出變化矩陣:

var createMatrix = function(p1, p2, width) {//createMatrix(array, matrix)將一組JSON描述的縮放、移動和旋轉等操作轉換成對應的變化矩陣
    var vec = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]],
        dist = ht.Default.getDistance(p1, p2);//獲取兩點之間距離,或向量長度
    return ht.Default.createMatrix({
        s3: [width, dist, width],
    r3: [Math.PI/2 - Math.asin(vec[1]/dist), Math.atan2(vec[0], vec[2]), 0],
    rotationMode: 'xyz',
    t3: [(p1[0]+p2[0])/2, (p1[1]+p2[1])/2, (p1[2]+p2[2])/2]
    });
};

基礎配件全部定義完畢,接着就是將“shape3d”屬性設置為自定義的 3D 模型“custom” ,並將“layoutable”屬性設置為“false”阻止圖元參與布局,並將點之間的連線通過edge.a('pipeline', node)重新刷新,並添加進數據模型 dataModel 中:

var createEdge = function(dm, node1, node2) {//創建‘custom’模型的edge
    var node = new ht.Node();
    node.s({
        'shape3d': 'custom',
        'shape3d.color': '#ECE0D4',
        'layoutable': false
    });
    dm.add(node);

    var edge = new ht.Edge(node1, node2);
    edge.a('pipeline', node);
    edge.s('edge.color', 'rgba(0, 0, 0, 0)');
    dm.add(edge);
    return edge;
};

插:我們還可以在工業上用 HeatMap 熱圖上做文章,效果依舊很炫,具體地址 http://hightopo.com/guide/guide/plugin/forcelayout/examples/example_heatmap3d.html

界面上的圖形全部繪制完畢,剩下的就只有 form 表單,首先將 form 表單添加進 HTML 頁面,用的是 HT 封裝的 ht.widget.FormPane 函數:

var formPane = new ht.widget.FormPane();
formPane.setWidth(230);
formPane.setHeight(125);
formPane.addToDOM();

記住,form 表單要設置寬高,不然不顯示。

form 表單添加行是通過 addRow 函數,我們重點來說一下下面的幾行,Color、Range 和 Intensity,這三個名字主要是用來控制“頭燈”的。在 HT 中直接通過 setHeadlightColor/setHeadlightRange/setHeadlightIntensity 三個函數來控制“頭燈”的顏色、范圍以及燈的強度,onValueChanged 屬性,顧名思義屬性值改變之后觸發的事件:

['Color', 'Range', 'Intensity'].forEach(function(name) {
    var obj = { id: name },
    func = function(oV, nV) {
        g3d['setHeadlight' + name](nV);// === g3d.setHeadlightColor(nV)/g3d.setHeadlightRange(nV)/g3d.setHeadlightIntensity(nV)
    };
    if (name === 'Color')
        obj.colorPicker = {//ht.widget.ColorPicker為顏色選擇框 
        instant: true,
        value: g3d['getHeadlight' + name](),// === g3d.getHeadlightColor()
        onValueChanged: func
    };
    else 
        obj.slider = {//滑動條
            min: 0,
        max: name === 'Range' ? 20000 : 3,
        step: 0.1,
        value: g3d['getHeadlight' + name](),
        onValueChanged: func
        };
    formPane.addRow([ name, obj ], [ 70, 0.1 ]);
});

slider 和 colorPicker 都是 HT 自定義的滑動條和顏色選擇器,詳情請參考 HT for Web 表單手冊

如果還有不懂的請咨詢我,或者可以直接上 HT for Web 官網查閱手冊。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM