基於HTML5 Canvas的工控SCADA模擬飛機飛行


昨天看到一篇文章說是學習如何開飛機的,然后我就想,如果我也可以開飛機那就好玩了,每個人小時候都想做飛行員!中國飛行員太難當了,再說也不輕易讓你開飛機!后來我就想如果能用 HT 開飛機那就是真的有趣了,哈哈,這個實現的效果還是很不錯的,可以讓你滿足一下開飛機的虛榮心偷笑

Demo 地址: http://hightopo.com/guide/guide/plugin/obj/examples/example_path.html

來看下具體實現的效果:

這個例子基本上完全模擬了飛機的飛行模式,包括起飛跑道,包括飛機的移動路徑,螺旋槳的旋轉,機尾的指示燈等部分。

首先,最重要的是我們的飛機模型,前面有文章寫到過,HT 內部封裝了一個方法 ht.Default.loadObj 來加載 OBJ 文件:

 1 ht.Default.loadObj('obj/plane.obj', 'obj/plane.mtl', {                    
 2     center: true,
 3     r3: [0, -Math.PI/2, 0], // make plane face right
 4     s3: [0.15, 0.15, 0.15], // make plane smaller
 5     finishFunc: function(modelMap, array, rawS3){
 6         if(modelMap){                            
 7             modelMap.propeller.r3 = {//propeller 螺旋槳
 8             func: function(data){
 9                 return [data.a('angle'), 0, 0]; 
10             }
11         };                             
12         // make propeller a litter bigger
13         modelMap.propeller.s3 = [1, 1.2, 1.2]; 
14         modelMap.propeller.color = 'yellow';
15     } 
16 });

這里面的 modelMap.propeller 是 OBJ 文件中定義好的 modelMap 對象中的 propeller 對象,你可以試着打印 modelMap 看看輸出結果。

這個方法里的 finishFunc(modelMap, array, rawS3) 用於加載后的回調處理,具體查閱 HT for Web OBJ 手冊,我們還添加了一個在 OBJ 模型中沒有的飛機尾部的“紅色閃爍指示燈”,這里用到的是組合模型 array(所有材質組成的數組,里面有至少一個模型),我們在 array中加入一個新的球模型:

 1 // add a sphere model as an indicator light 指示燈
 2 array.push({
 3     shape3d: ht.Default.createSmoothSphereModel(),
 4     t3: [-40, 10, 0],
 5     s3: [6, 6, 6],
 6     color: {
 7         func: function(data){
 8             return data.a('light') ? 'red': 'black';
 9         }
10     }
11 });

這里的 shape3d 是 HT 封裝的一個屬性名,通過 setShape3dModel(name, model) 函數注冊的或者是通過 getShape3dModel(name) 函數返回的注冊過的 3D 模型,如何注冊 3D 模型可查閱 HT for Web 建模手冊

color 屬性名對應了一個對象,這邊的定義是這樣的,color 直接通過 data.getAttr('a') 獲取 data.setAttr(‘a’, value) 中的值,這樣做有兩個好處,一是可以不污染 HT 的常用屬性操作,所以 HT 專門定義了這個 attr 屬性類型,是 HT 預留給用戶存儲業務數據的;二是這樣也很方便數據綁定,我們可以通過在需要更改屬性的地方調用 setAttr 方法,非常方便。

接着我們通過 ht.Default.setShape3dModel(name, model) 來將我們剛剛組合好的模型 array 注冊成我們要的“plane”模型:

1 ht.Default.setShape3dModel('plane', array);

注冊好模型后肯定是要調用這個模型,我們可以通過 shape3d 屬性來調用這個模型,並且在這個模型中自定義上面代碼中出現過的 light 屬性和 angle 屬性:

 1 plane = new ht.Node();
 2 plane.s3(200, 200, 200);
 3 plane.s3(rawS3);
 4 plane.s({
 5     'shape3d': 'plane',
 6     'shape3d.scaleable': false,
 7     'wf.visible': true,//線框是否可見
 8     'wf.color': 'white',
 9     'wf.short': true //是否顯示封閉的線框,true為不封閉的短線框
10 });
11 plane.a({
12     'angle': 0,
13     'light': false
14 });

因為飛機還有螺旋槳、指示燈兩個功能,我們還得對這兩個模型做動畫效果,可查閱 HT for Web 動畫手冊,通過用戶在 form 表單上選擇的結果來決定飛機飛行持續時間、看飛機的視角、飛機沿着“航線”飛行所要旋轉的角度、機尾指示燈的“閃爍”功能等等,最后別忘了飛機停止飛行時,如果要讓飛機繼續飛行,就得回調這個動畫,並且設置燈不再閃爍,別忘了要啟動動畫:

 1 params = {
 2     delay: 1500,
 3     duration: 20000,
 4     easing: function(t){ 
 5         return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2));
 6     },
 7     action: function(v, t){
 8         var length = g3d.getLineLength(polyline),
 9         offset = g3d.getLineOffset(polyline, length*v),
10         point = offset.point,
11         px = point.x,
12         py = point.y,
13         pz = point.z,
14         tangent = offset.tangent,
15         tx = tangent.x,
16         ty = tangent.y,
17         tz = tangent.z;
18         plane.p3(px, py, pz);
19         plane.lookAt([px + tx, py + ty, pz + tz], 'right');
20                         
21         var camera = formPane.v('Camera');
22         if(camera === 'Look At'){
23             g3d.setCenter(px, py, pz);
24         }
25         else if(camera === 'First Person'){                            
26             g3d.setEye(px - tx * 400, py - ty * 400 + 30, pz - tz * 400);
27             g3d.setCenter(px, py, pz);                            
28         }
29                         
30         plane.a('angle', v*Math.PI*120);                        
31         if(this.duration * t % 1000 > 500){
32             plane.a('light', false);
33         }else{
34             plane.a('light', true);
35         }                        
36     },
37     finishFunc: function(){
38         animation = ht.Default.startAnim(params);
39         plane.a('light', false);
40     }                  
41 };                               
42                 
43 animation = ht.Default.startAnim(params);

其實最讓我們好奇的是描繪的路徑跟飛機本身的飛行並沒有關系,還有那么多左拐右拐的,要如何做才能做到呢?

接下來我們來描繪路徑,首先這個路徑是由 ht.Polyline 作為基礎來描繪的:

 1 polyline = new ht.Polyline();   
 2 polyline.setThickness(2);
 3 polyline.s({
 4     'shape.border.pattern': [16, 16],
 5     'shape.border.color': 'red',
 6     'shape.border.gradient.color': 'yellow',
 7     'shape3d.resolution': 300,
 8     '3d.selectable': false
 9 });
10 dataModel.add(polyline);

上面的代碼只是向 datamodel 數據模型中添加了一個 polyline 管線而已,不會顯示任何東西,要顯示“航道”首先就要設置航道所在的點,我們先設置航道的初始點:

1 points = [{ x: 0, y: 0, e: 0 }];
2 segments = [1];

這個 points 和 segments 是 HT for Web Shape 手冊中定義的,points 是 ht.List 類型數組的定點信息,頂點為 { x: 100, y: 200 } 格式的對象;segments 是 ht.List 類型的線段數組信息,代表 points 數組中的頂點按數組順序的連接方式。

圖中“航道”左側的多個圓形軌道也是通過設置 points 和 segments 來設置的:

1 for(var k=0; k<count+1; k++){
2     var angle = k * Math.PI * 2 * round / count;
3     points.push({
4         x: cx + radius * Math.cos(angle),
5         y: cy + radius * Math.sin(angle),
6         e: k * height / count
7     }); 
8     segments.push(2);
9 }

接下來幾個拐點也是這種方法來實現的,這里就不贅述了,如果你還沒看手冊的話,這里標明一點,segments 只能取值 1~5,1 代表一個新路徑的起點;2 代表從上次最后點連接到該點;3 占用兩個點信息,第一個點作為曲線控制點,第二個點作為曲線結束點;4 占用3個點信息,第一和第二個點作為曲線控制點,第三個點作為曲線結束點;5 不占用點信息,代表本次繪制路徑結束,並閉合到路徑的起始點:

 1 points.push({ x: cx+radius, y: 0, e: height/2 });
 2 points.push({ x: 0, y: 0, e: height/2 });
 3 segments.push(3);
 4      
 5 points.push({ x: radius, y: -radius, e: height/2*0.7 });
 6 points.push({ x: radius*2, y: radius, e: height/2*0.3 });
 7 points.push({ x: radius*3, y: 0, e: 0 });
 8 segments.push(4);   
 9 
10 points.push({ x: 0, y: 0, e: 0 });
11 segments.push(2);  

我們已經把路徑上的點都添加進“航道”中了,接下來要把點都設置到管道上去才會顯示在界面上:

1 polyline.setPoints(points);
2 polyline.setSegments(segments);  

“跑道”就比較簡單了,只是一個 Node 節點然后設置基礎效果而已,沒什么特別的:

 1 runway = new ht.Node();
 2 runway.s3(-cx+radius*3, 1, 200);
 3 runway.p3(cx+runway.getWidth()/2, -22, 0);
 4 runway.s({
 5     'all.color': '#FAFAFA',
 6     'all.transparent': true,
 7     'all.reverse.cull': true,
 8     'all.opacity': 0.8,
 9     '3d.selectable': false
10 });
11 dataModel.add(runway);

最后,在界面上添加一個 formPane 表單面板,定義好之后可以直接添加到 body 上,這樣就不會跟 graph3dView 有顯示的聯系了。

formPane 可以用 formPane.addRow 方法動態添加行,這個方法中可以直接對動態變化的數據進行交互,例如本例中的是否有動畫 Animation,我們利用 checkBox 來記錄選中或者非選中的狀態:

 1 {
 2     checkBox: {
 3         label: 'Animation',
 4         selected: true,
 5         onValueChanged: function(){
 6             if(this.isSelected()){
 7                 animation.resume();
 8             }else{
 9                 animation.pause();
10             }                               
11         }
12     }
13 }

也可以通過設置“id”來記錄動態改變的值,然后 formPane就會通過調用 formPane.v(id) 來獲取當前值。

至此,整個 Demo 的解釋到此為止,如果還有不懂的可以先查閱我們官網 HT for Web,之后還有不懂的可以私信我,但還是希望你們能仔細閱讀,不然很浪費我們雙方的時間,謝謝~


免責聲明!

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



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