トポロジーにおける以前のアプリケーションはすべて静的プリミティブでした。今日は、トポロジーで移動するプリミティブ、つまりインペラの回転を設計します。
達成した最終的な効果を見てみましょう: http://www.hightopo.com/demo/fan/index.html
まず、このインペラ モデルがどのようなものかを見てみましょう
モデルを見ると、このインペラー モデルには 3 つのブレードがあり、明らかに、Web 用の HT の基本的なグラフィックスを使用して接続することはできません。非常にシンプルです。HT for Web はカスタム グラフィックス ソリューションを提供し、カスタム グラフィックスを通じて葉のような不規則なグラフィックスを描画できます。
描画を始める前に、まず HT for Web でのカスタム グラフィック描画の基本知識を理解する必要があります:
カスタム グラフィックを描画するには、ベクトル タイプを形状として指定し、点の Array 配列を通じて各点情報を指定する必要があります。 . Points は、点の座標を [x1, y1, x2, y2, x3, y3, ...] の形式で保存します。曲線の多角形は、セグメントの Array 配列によって記述できます。Segment は、各線分を [1, 2, 1, 3...] の形式で記述します。
1: moveTo は 1 つの点情報を占め、新しいパス 開始点
2: lineTo、最後の点からこの点までの接続を表す 1 つの点情報を占有します
3:quadraticCurveTo、2 つの点情報を占有します、最初の点は曲線の制御点として使用され、2 番目の点は曲線の制御点として使用されます点は曲線の終点として使用されます
4: bezierCurveTo、3つの点情報を占め、1番目と2番目の点は曲線制御点として使用され、3番目の点は曲線の終点として使用されます
5: closePath 、ポイント情報を占有せず、これを表します。二次パスの描画が終了し、パスの開始点に閉じられます
閉じたポリゴンと比較して、セグメント パラメーターの設定に加えて、closePath 属性も設定できます。 * closePath は取得します。ポリゴンが閉じているかどうかを設定します。デフォルトは false です。このメソッドは、セグメント パラメータを設定する必要はありません。
それでは、ブレードのデザインを始めましょう
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つの頂点を使用してブレードの大まかな形状を直線で輪郭を描きます。少し抽象的ですが、次に、コントロール ポイントを追加し、セグメント パラメーターを変更して、ブレードを変形させます。
まず、bezierCurveTo メソッドを使用して、1 番目と 2 番目の頂点の間の線分に 2 つの制御点を追加して、曲線を描画します。点とセグメントの属性は次のとおりです:
points: [ 92, 67, 93, 35, 78, 0, 62, 7, 0, 70, 60, 98 ], segments: [ 1, 4, 2, 2 ]
今回は前と同じです。写真 比較すると、1 つのエッジは少し曲がっているので、2 番目と 3 番目のエッジを処理しましょう
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 ]
ほら、これで形状が決まり、ブレードがそこにあります。次に行うことは、このような 3 つのブレードをインペラに接続します。
既存のリソースを結合するには、ベクトル内の画像タイプ クラスを使用して新しいベクトルを定義する必要があります。具体的な使用方法は次のとおりです。 2 番目と 3 番目のブレードを回転させて配置し、3 つのブレードを組み合わせてインペラを構成します。しかし、この問題を解決する必要があるのは、それほど難しいことではありません。ブレードの Points 属性にもう 1 つの頂点を追加して、三角形を埋めます。コードは次のとおりです。
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 属性に頂点を追加した後、セグメント配列の最後に説明を追加することを忘れないでください。見てみましょう。最終的な効果:
このインペラのリソースが準備できました。次に、インペラを回転させます。まず、インペラを回転させるための原理を説明します。これを実現するには、回転属性を設定するだけで十分です。ただし、この回転属性は、インペラが常に変化している場合にのみ回転させるため、この時点ではタイマーを使用して、継続的に回転を設定する必要があります。回転属性。羽根車を動かします。
まあ、こんな感じなので実装してみましょう:
首先是创建一个节点,并设置其引用的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的源码,有什么问题欢迎大家咨询。
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); }