基於 HTML5 WebGL 的 3D 「彈力」佈局

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

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

本例地址:www.hightopo.com/demo/pipeli…數組

圖片描述

使用彈力佈局功能須要在引入 ht.js 核心庫以後,再引入一個 ht-forcelayout.js 的彈力佈局插件庫,由於還用到了 form 表單,因此要引入 ht-form.js 的表單插件庫:bash

<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 封裝的彈力佈局的背景以後,接下來就是幫助大家也能輕鬆地實現這個效果。dom

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

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 章節),咱們在建立管線的時候就用了這種方法:ui

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 熱圖上作文章,效果依舊很炫,具體地址 Heatmap - 3D

圖片描述

界面上的圖形所有繪製完畢,剩下的就只有 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

相關文章
相關標籤/搜索