基於HT for Web矢量實現2D葉輪旋轉

以前在拓撲上的應用都是些靜態的圖元,今天咱們將在拓撲上設計一個會動的圖元——葉輪旋轉。node

咱們先來看下這個葉輪模型長什麼樣數組

圖片描述

從模型上看,這個葉輪模型有三個葉片,每個葉片都是不規則圖形,顯然沒法用上咱們HT for Web的基礎圖形來拼接,那麼咱們該怎麼作呢?很簡單,在HT for Web中提供了自定義圖形的方案,咱們能夠經過自定義圖形來繪製像葉片這種不規則圖形。app

在繪製葉片以前,咱們得先來了解下HT for Web的自定義圖形繪製的基本知識:函數

繪製自定義圖形須要制定矢量類型爲shape,並經過points的Array數組指定每一個點信息, points以[x1, y1, x2, y2, x3, y3, ...]的方式存儲點座標。曲線的多邊形可經過segments的Array數組來描述, segment以[1, 2, 1, 3 ...]的方式描述每一個線段:spa

1: moveTo,佔用1個點信息,表明一個新路徑的起點
2: lineTo,佔用1個點信息,表明從上次最後點鏈接到該點
3: quadraticCurveTo,佔用2個點信息,第一個點做爲曲線控制點,第二個點做爲曲線結束點
4: bezierCurveTo,佔用3個點信息,第一和第二個點做爲曲線控制點,第三個點做爲曲線結束點設計

圖片描述

5: closePath,不佔用點信息,表明本次路徑繪製結束,並閉合到路徑的起始點code

對比閉合多邊形除了設置segments參數外,還能夠設置closePath屬性: * closePath獲取和設置多邊形是否閉合,默認爲false,對閉合直線採用這種方式,無需設置segments參數。對象

好了,那麼接下來咱們開始設計葉片了blog

圖片描述

ht.Default.setImage('vane', {
    width: 97,
    height: 106,
    comps: [
        {
            type: 'shape',
            points: [
                92, 67,
                62, 7,
                0, 70,
                60, 98
            ],
            segments: [
                1, 2, 2, 2
            ],
            background : 'red'
        }
    ]
});

咱們在矢量中定義了4個頂點,而且將這4個頂點經過直線勾勒出葉片的大體形狀,雖然有些抽象,可是,接下來將會經過增長控制點和改變segment參數來讓這個葉片發生蛻變。圖片

首先咱們經過bezierCurveTo方式向第一個和第二個頂點之間的線段添加兩個控制點,從而繪製出曲線,如下是points及segments屬性:

points: [
    92, 67,
    93, 35, 78, 0, 62, 7,
    0, 70,
    60, 98
],
segments: [
    1, 4, 2, 2
]

圖片描述

這時候與上一個圖相比較,有一條邊一件有些弧度了,那麼接下來就來處理第二條邊和第三條邊

圖片描述圖片描述

points: [
    92, 67,
    93, 35, 78, 0, 62, 7,
    29, 13, 4, 46, 0, 70,
    28, 53, 68, 60, 60, 98
],
segments: [
    1, 4, 4, 4
]

看吧,如今是否是有模有樣了,如今葉片已經有了,那麼接下來要作的就是使用三個這樣的葉片拼接成一個葉輪。

將已有的資源拼接在一塊兒須要用到矢量中的image類型類定義新的矢量,具體的使用方法以下:

ht.Default.setImage('impeller', {
    width: 166,
    height: 180.666,
    comps : [
        {
            type: 'image',
            name: 'vane',
            rect: [0, 0, 97, 106]
        },
        {
            type: 'image',
            name: 'vane',
            rect: [87.45, 26.95, 97, 106],
            rotation: 2 * Math.PI / 3
        },
        {
            type: 'image',
            name: 'vane',
            rect: [20.45, 89.2, 97, 106],
            rotation: 2 * Math.PI / 3 * 2
        }
    ]
});

圖片描述

在代碼中,咱們定義了三個葉片,而且對第二個和第三個葉片作了旋轉和定位的處理,讓這三個葉片排布組合成一個葉輪來,可是怎麼能讓葉輪中間空出一個三角形呢,這個問題解決起來不難,咱們只須要在葉片的points屬性上再多加一個頂點,就能夠填充這個三角形了,代碼以下:

points: [
    92, 67,
    93, 35, 78, 0, 62, 7,
    29, 13, 4, 46, 0, 70,
    28, 53, 68, 60, 60, 98,
    97, 106
],
segments: [
    1, 4, 4, 4, 2
]

在points屬性上添加了一個頂點後,別忘了在segments數組的最後面添加一個描述,再來看看最終的效果:

圖片描述

到這個葉輪的資源就作好了,那麼接下來就是要讓這個葉輪旋轉起來了,咱們先來分析下:

要讓葉輪旋轉起來,其實原理很簡單,咱們只須要設置rotation屬性就能夠實現了,可是這個rotation屬性只有在不斷的變化中,纔會讓葉輪旋轉起來,因此這個時候就須要用到定時器了,經過定時器來不斷地設置rotation屬性,讓葉輪動起來。

恩,好像就是這樣子的,那麼咱們來實現一下:

首先是建立一個節點,並設置其引用的image爲impeller,再將其添加到DataModel,令節點在拓撲中顯示出來:

var node = new ht.Node();
node.setSize(166, 181);
node.setPosition(400, 400);
node.setImage('impeller');
dataModel.add(node);

接下來就是添加一個定時器了:

window.setInterval(function() {
    var rotation = node.getRotation() + Math.PI / 10;
    if (rotation > Math.PI * 2) {
        rotation -= Math.PI * 2;
    }
    node.setRotation(rotation);
}, 40);

OK了,好像就是這個效果,可是當你選中這個節點的時候,你會發現這個節點的邊框在不停的閃動,看起來並非那麼的舒服,爲何會出現這種狀況呢?緣由很簡單,當設置了節點的rotation屬性後,節點的顯示區域就會發生變化,這個時候節點的寬高天然就發生的變化,其邊框也天然跟着改變。

還有,在不少狀況下,節點的rotation屬性及寬高屬性會被當成業務屬性來處理,不太適合被實時改變,那麼咱們該如何處理,才能在不不改變節點的rotation屬性的前提下令葉輪轉動起來呢?

矢量中,好像有數據綁定的功能,在手冊中是這麼介紹的:

綁定的格式很簡單,只需將之前的參數值用一個帶func屬性的對象替換便可,func的內容有如下幾種類型:

  1. function類型,直接調用該函數,並傳入相關Data和view對象,由函數返回值決定參數值,即func(data, view);調用。

  2. string類型:
    2.1 style@開頭,則返回data.getStyle()值,其中表明style的屬性名。
    2.2 attr@
    開頭,則返回data.getAttr()值,其中表明attr的屬性名。
    2.3 field@開頭,則返回data.值,其中表明data的屬性名。
    2.4 若是不匹配以上狀況,則直接將string類型做爲data對象的函數名調用data.
    (view),返回值做爲參數值。

除了func屬性外,還可設置value屬性做爲默認值,若是對應的func取得的值爲undefined或null時,則會採用value屬性定義的默認值。 例如如下代碼,若是對應的Data對象的attr屬性stateColor爲undefined或null時,則會採用yellow顏色:

color: {
    func: 'attr@stateColor',
    value: 'yellow'
}

數據綁定的用法已經介紹得很清楚了,咱們不妨先試試綁定葉片的背景色吧,看下好很差使。在矢量vane中的background屬性設置成數據綁定的形式,代碼以下:

background : {
    value : 'red',
    func : 'attr@vane_background'
}

在沒有設置vane_background屬性的時候,令其去red爲默認值,那麼接下來咱們來定義下vane_background屬性爲blue,看看葉輪會不會變成藍色:

node.setAttr('vane_background', ‘blue');

看下效果:

圖片描述

果真生效了,這下好了,咱們就可讓葉輪旋轉變得更加完美了,來看看具體該這麼作。

首先,咱們先在節點上定義一個自定義屬性,名字爲:impeller_rotation

node.setAttr('impeller_rotation', 0);

而後再定義一個名字爲rotate_impeller的矢量,並將rotation屬性綁定到節點的impeller_rotation上:

ht.Default.setImage('rotate_impeller', {
    width : 220,
    height : 220,
    comps : [
        {
            type : 'image',
            name : 'impeller',
            rect : [27, 20, 166, 180.666],
            rotation : {
                func : function(data) { 
                    return data.getAttr('impeller_rotation'); 
                }
            }
        }
    ]
});

這時候咱們在定時器中修改節點的rotation屬性改爲修改自定義屬性impeller_rotation就可讓節點中的葉輪旋轉起來,而且不會影響到節點自身的屬性,這就是咱們想要的效果。

圖片描述

2D上能夠實現,在3D上同樣能夠實現,下一章咱們就來說講葉輪旋轉在3D上的應用,今天就先到這裏,下面附上今天Demo的源碼,有什麼問題歡迎你們諮詢。

ht.Default.setImage('vane', {
    width : 97,
    height : 106,
    comps : [
        {
            type : 'shape',
            points : [
                92, 67,
                93, 35, 78, 0, 62, 7,
                29, 13, 4, 46, 0, 70,
                28, 53, 68, 60, 60, 98,
                97, 106
            ],
            segments : [
                1, 4, 4, 4, 2
            ],
            background : {
                value : 'red',
                func : 'attr@vane_background'
            }
        }
    ]
});

ht.Default.setImage('impeller', {
    width : 166,
    height : 180.666,
    comps : [
        {
            type : 'image',
            name : 'vane',
            rect : [0, 0, 97, 106]
        },
        {
            type : 'image',
            name : 'vane',
            rect : [87.45, 26.95, 97, 106],
            rotation : 2 * Math.PI / 3
        },
        {
            type : 'image',
            name : 'vane',
            rect : [20.45, 89.2, 97, 106],
            rotation : 2 * Math.PI / 3 * 2
        }
    ]
});

ht.Default.setImage('rotate_impeller', {
    width : 220,
    height : 220,
    comps : [
        {
            type : 'image',
            name : 'impeller',
            rect : [27, 20, 166, 180.666],
            rotation : {
                func : function(data) {
                    return data.getAttr('impeller_rotation');
                }
            }
        }
    ]
});

function init() {
    var dataModel = new ht.DataModel();

    var graphView = new ht.graph.GraphView(dataModel);
    var view = graphView.getView();
    view.className = "view";
    document.body.appendChild(view);

    var node = new ht.Node();
    node.setSize(220, 220);
    node.setPosition(200, 400);
    node.setImage('rotate_impeller');
    node.setAttr('impeller_rotation', 0);
    node.setAttr('vane_background', 'blue');
    dataModel.add(node);

    var node1 = new ht.Node();
    node1.setSize(166, 181);
    node1.setPosition(500, 400);
    node1.setImage('impeller');
    dataModel.add(node1);

    window.setInterval(function() {
        var rotation = node.a('impeller_rotation') + Math.PI / 10;
        if (rotation > Math.PI * 2) {
            rotation -= Math.PI * 2;
        }
        node.a('impeller_rotation', rotation);
        node1.setRotation(rotation);

    }, 40);
}
相關文章
相關標籤/搜索