首頁 >web前端 >H5教程 >基於HTML5 Canvas 實現向量工控風機葉輪旋轉

基於HTML5 Canvas 實現向量工控風機葉輪旋轉

高洛峰
高洛峰原創
2016-11-23 10:02:491424瀏覽

之前在拓樸上的應用都是些靜態的圖元,今天我們將在拓樸上設計一個會動的圖元-葉輪旋轉。

先看看最後我們實現的效果:http://www.hightopo.com/demo/fan/index.html

基於HTML5 Canvas 實現向量工控風機葉輪旋轉

我們先來看看這個葉輪模型長什麼樣子

基於HTML5 Canvas 實現向量工控風機葉輪旋轉

我們先來看看這個葉輪模型長什麼樣子

我們先來看看這個葉輪模型長什麼樣子

模型上看,這個葉輪模型有三個葉片,每一個葉片都是不規則圖形,顯然無法用上我們HT for Web的基礎圖形來拼接,那我們該怎麼做呢?很簡單,在HT for Web中提供了自訂圖形的方案,我們可以透過自訂圖形來繪製像葉片這種不規則圖形。

在繪製葉片之前,我們得先來了解下HT for Web的自訂圖形繪製的基本知識:

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

1: moveTo,佔用1個點信息,代表一個新路徑的起點

2: lineTo,佔用1個點信息,代表從上次最後點連接到該點基於HTML5 Canvas 實現向量工控風機葉輪旋轉

3: quadraticCurveTo,佔用2個點信息,第一個點作為曲線控制點,第二個點作為曲線結束點

4: bezierCurveTo,佔用3個點信息,第一和第二個點作為曲線控制點,第三個點作為曲線結束點

5: closePath,不佔用點信息,代表本次路徑繪製結束,並閉合到路徑的起始點基於HTML5 Canvas 實現向量工控風機葉輪旋轉

對比閉合多邊形除了設定segments參數外,還可以設定closePath屬性: * closePath取得和設定多邊形是否閉合,預設為false,對閉合直線採取這種方式,無需設定segments參數。

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

基於HTML5 Canvas 實現向量工控風機葉輪旋轉

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
]
基於HTML5 Canvas 實現向量工控風機葉輪旋轉基於HTML5 Canvas 實現向量工控風機葉輪旋轉

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

基於HTML5 Canvas 實現向量工控風機葉輪旋轉

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
        }
    ]
});

基於HTML5 Canvas 實現向量工控風機葉輪旋轉

在程式碼中,我們定義了三個葉片,並且對第二和第三個葉片做了旋轉和定位的處理,讓這三個葉片排布組合成一個葉輪來,但是怎麼能讓葉輪中間空出一個三角形呢,這個問題解決起來不難,我們只需要在葉片的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');

看下效果:

基於HTML5 Canvas 實現向量工控風機葉輪旋轉

果然生效了,这下好了,我们就可以让叶轮旋转变得更加完美了,来看看具体该这么做。

首先,我们先在节点上定义一个自定义属性,名字为: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就可以让节点中的叶轮旋转起来,并且不会影响到节点自身的属性,这就是我们想要的效果。

基於HTML5 Canvas 實現向量工控風機葉輪旋轉

在2D上可以实现,在3D上一样可以实现,下一章我们就来讲讲叶轮旋转在3D上的应用,今天就先到这里,下面附上今天Demo的源码,有什么问题欢迎大家咨询。

http://www.hightopo.com/demo/fan/index.html

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);
}


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn