WebGL でレンダリングされた 3D コンピューター ルームは、今では何も目新しいものではありません。この記事の主な目的は、たまたまプロジェクトで使用された 3D コンピューター ルームの目と中心の問題を説明することです。この例が私の要件を最もよく満たしていると感じたので、記録として使用します。
レンダリングこの 3D コンピューター ルームのデモは非常に優れており、美しく、基本的なインタラクションも満足のいくものです。実装方法を見てみましょう。
コード生成クラスの定義まず、index.html で呼び出される js パスから該当する js を 1 つずつ開きます。 Editor.Server クラスは、server.js でカスタマイズされ、HT によってカプセル化された ht.Default.def 関数によって作成されます (作成されたクラスに注意してください) name は Editor です。.Server の前の Editor は E) に置き換えることはできません。
ht.Default.def('Editor.Server', Object, {//最初のパラメータはクラス名です。文字列の場合は自動的に HT の classMap に登録されます。2 番目のパラメータは継承される親クラスです; 3 番目のパラメータはメソッドと変数の宣言です addToDataModel: function(dm) { // データ コンテナにノードを追加します。 dm.add(this._node); // ht に定義済みの関数を渡します。ノードから追加までデータ コンテナに追加されたメソッド}, setHost: function() { //吸着を設定します this._node.setHost.apply(this._node, argument) }, s3: function() {//ノードのサイズを設定しますthis._node .s3.apply(this._node, 引数) }, setElevation: function(); {//ノード プリミティブの中心位置が配置される 3D 座標系の y 軸位置を制御します。 this._node.setElevation.apply(this._node, argument) }});
Editor.Server クラスを作成する
このクラスは ht.Node ノードを作成し、ノードの色と前面テクスチャを設定できます。
var S = E.Server = function(obj) {//サーバーコンポーネント var color = obj.color,frontImg = obj.frontImg; var node = this._node = new ht.Node();//ノードnode.sを作成します。 (//ノードのスタイルをsetStyleの略称に設定します 'all.color': color,//ノードの6つの側面の色を設定します 'front.image':frontImg //ノードの前面に画像を設定します});};
このようにして、サーバー コンポーネントを作成する必要がある場所で新しいサーバー コンポーネント オブジェクトを直接作成でき、上で宣言した setHost やその他の関数 (後ほど使用します) を直接呼び出すことができます。
次に、Editor.Cabinet キャビネット クラスを作成します。このメソッドは、上記の Editor.Server クラスの定義メソッドと似ています。
ht.Default.def('Editor.Cabinet', Object, { addToDataModel: function(dm) { dm.add(this._door); dm.add(this._node); this._serverList.forEach(function(s) { s.addToDataModel(dm) }); }、p3: function() { this._node.p3.apply(this._node, argument);//ノードの 3D 座標を設定します}});
Editor.Cabinet クラスの作成
このクラスは、以前の Editor.Server サーバー コンポーネント クラスよりも比較的複雑です。このクラスは、キャビネット本体、キャビネット ドア、およびキャビネット内にサーバー コンポーネントを作成します。
var C = E.Cabinet = function(obj) { var color = obj.color, doorFrontImg = obj.doorFrontImg, doorBackImg = obj.doorBackImg, s3 = obj.s3 var node = this._node = new ht.Node(); ; // キャビネットnode.s3(s3) //ノードのサイズをsetSize3dに設定します。 node.a('cabinet', this);//キャビネットのプロパティをカスタマイズしますnode.s({//ノードのスタイルをsetStyleに設定します 'all.color': color,//ノードの6つの側面の色を設定します'front.visible ': false//ノードの前面が表示されるかどうかを設定します}); if (Math.random() > 0.5) { node.addStyleIcon('alarm', {//アイコン名をノードに追加します: ['icon Thermometer'], //複数の文字列を含む配列。各文字列は画像またはベクトル (ht.Default.setImage で登録) に対応します。 face: 'top', // デフォルト値は前面、アイコンの方向は 3D です。 、利用可能な値は left|right|top|bottom|front|back|center です。position: 17, //自動回転するアイコンの位置を指定します。 'y', //デフォルト値は false、アイコンが 3D で自動的に目の方向を向くかどうか t3: [0, 16, 0], //デフォルト値は未定義、3D でのアイコンのオフセット,形式は[x ,y,z]です width: 37,//各アイコンの幅を指定します、デフォルトは画像登録時の幅に基づきます height: 32,//各アイコンの高さを指定します、デフォルトは画像の登録時の高さに基づく textureScale: 4, //デフォルト値は 2 です。この値はメモリによって生成される実際のマップの倍数を表します。あまり大きく設定しないと、パフォーマンスに影響します。 { func: function() { return !! E.alarmVisible; }}//画像のグループを表示するかどうかを示します }); var door = this._door = new ht.DoorWindow();//キャビネットのドア door.setWidth(s3[0]);//3D トポロジの x 軸方向のグラフィック要素の長さを設定 door.setHeight(1);// 3D トポロジのグラフィック要素の Z 軸長さ door.setTall(s3[1]);//y 軸上のノード プリミティブの長さを制御 door.setElevation(0);//3D 座標系のプリミティブの中心の y 座標を設定します.setY(s3[2 ] * 0.5);//y 軸上のノードの位置を設定 door.setHost(node);//吸着を設定 door.s({//ノードのスタイルを設定 setStyle 'all.color': color,/ /ノードの 6 つの側面の色を設定します 'front.image': doorFrontImg, //ノードの正面画像を設定します 'front.transparent': true, //ノードの正面が透明かどうかを設定します 'back .image': doorBackImg, //ノードの背面に画像を設定します 'back.uv': [1,0, 1,1, 0,1, 0,0],//ノードの背後にある UV マップをカスタマイズします。空の場合は、デフォルト値 [0,0, 0,1, 1,1, 1]。 ,0] ' dw.axis': 'right'//ドアウィンドウ要素の展開および閉じる操作の回転軸を設定します。可能な値は left|right|top|bottom|v|h }); ._serverList = []; max = 6, list = E.randomList(max, Math.floor(Math.random() * (max - 2)) + 2) //global.js var サーバーで宣言された乱数を取得する関数、h = s3 [0] / 4; list.forEach(function(r) { var server = new E.Server({ //サーバーコンポーネントの色: 'rgb(51,49,49)',frontImg: 'サーバー コンポーネントは問題ありません' }); server.s3(s3[0] - 2, h, s3[2] - 4);//ノード サイズを設定します。server.setElevation((r - max * 0.5) * (h + 2 ));//Y軸上のノード中心点の座標を設定します。server.setHost(node);//ノードの吸着を設定します。serverList.push(server);//serverListにサーバーノードを });}; に追加します。
上記のコードで唯一言及されていないのは、Editor.randomList 関数です。この関数は global.js ファイルで宣言されており、次のように宣言されています。
var E = window.Editor = { leftWidth: 0、topHeight: 40、randomList: function(max, size) { var list = []、ran; while (list.length < size) { ran = Math.floor(Math.floor)ランダム() * 最大); if (list.indexOf(ran) >= 0) リストを返します。
さて、シーンの各部分のクラスが作成されたので、シーンを作成して、その中にこれらのプリミティブを積み上げます。
シーンの作成これに精通している学生は、HT を使用して 3D シーンを作成するには、新しい 3D コンポーネントが必要なだけで、次に addToDOM 関数を使用してそのシーンをボディに追加するだけであることを知っているはずです。
var g3d = E.main = new ht.graph3d.Graph3dView(); // 3D シーン
main.js ファイルは主に、壁、床、ドア、エアコン、すべてのキャビネットの生成と排出位置、および非常に重要なインタラクティブな部分など、3D シーンで必要ないくつかの要素を実行します。
壁、床、ドア、エアコン、キャビネットの作成コードは掲載しませんので、興味のある方はご自身でコードを確認してください。ここでは主にキャビネットとキャビネットに関連するオブジェクトのダブルクリックについて説明します。キャビネットの扉、サーバー機器など)を使って3D化するのですが、ダブルクリックしたキャビネットの前の一定の位置にカメラの視線の中心が移動するのですが、この動きがとてもスムーズで、今まで苦手だったので思いました。この部分については長々と説明してきましたが、最後に今回のDemoの実装方法について言及しました。
eye と center を繰り返し設定できるようにするために、これら 2 つのパラメータの設定に対応するコンテンツは setEye メソッドと setCenter メソッドにカプセル化されます。 setCenter メソッドは setEye メソッドに似ているため、ここでは繰り返しません。
//目の位置を設定します var setEye = function(eye,finish) { if (!eye) return; // 現在の目の値を取得します dx = eye[0] - e[0], dy = eye[1] - e[1], dz = eye[2] - e[2] // 500 ミリ秒のアニメーション遷移を開始 ht.Default.startAnim({ duration: 500, easing: easing,//アニメーションイージング関数finishFunc:finish || function() {}, //アニメーション終了後に呼び出される関数 action: function(v, t) {//イージングの通過を表すアニメーション v を設定します(t) 関数操作後の値、t は現在のアニメーションの進行状況を表します [0~1]、一般的な属性の変更は v パラメーターに基づきます g3d.setEye([ // 3D シーンに目を設定します目の値は、x、y、z 軸の値にそれぞれ対応する配列です。 e[0] + dx * v、e[1] + dy * v、e[2] + dz * v ]); } }) ;};
setCenter 関数を繰り返し宣言しなかったということは、この関数が重要ではないということを意味するわけではありません。むしろ、この関数は視線を移動するプロセスにおいて決定的な役割を果たします。ターゲット位置の前を歩くため (少なくとも私が定義した はこの目的に使用されます)、sCenter定義は、視線をターゲットの位置に移動することです (たとえば、現在の位置に立って右後ろの物体を見ることもできますし、右奥に行って物体の前に立つこともできます)。とても大切なことなので味わってください。
ダブルクリック イベントは単純で、HT によってカプセル化されたイベントをリッスンし、イベント タイプを判断し、対応するアクションを実行するだけです。
g3d.mi(function(e) {//addInteractorListener イベント リスニング関数 if (e.kind !== 'doubleClickData') //イベント タイプをダブルクリック ノードに決定 return; var data = e.data, p3 ; if (data. a('cabinet')) //本体 p3 = data.p3(); else { host = data.getHost(); //クリックしたノードの吸着オブジェクトを取得 if (host && host.a('cabinet')) {//吸着対象がキャビネットの場合 p3 = host.p3(); } } if (!p3) return; // 中心ターゲットの移動位置を設定します。キャビネットの位置 setEye([p3[0], 211, p3[2] + 247]) //目の移動する位置を設定します});上部のナビゲーションバー
この例を初めて見たとき、この人はすごいな、と思いました。私はずっと HT を使ってきましたが、HT の ht.widget.Toolbar を使ってまだこれほど美しいエフェクトを作成することができませんでした。それで、これは実際にHTを使用していることに気づきました、それはすごいです、私は本当に愚かです。
var form = E.top = new ht.widget.FormPane(); //トップフォームコンポーネント form.setRowHeight(E.topHeight);//行の高さを設定します form.setVGap(-E.topHeight);//維持するためにフォームコンポーネントの水平方向の間隔を行の高さの負の値に設定します同じ位置に複数の行 Line form.setVPadding(0);//フォームの上部と、上部とコンポーネントのコンテンツ間の間隔を設定します form.addRow([null, {//コンポーネントの行をフォームに追加します。最初のパラメータは要素の配列です。要素には、文字列、json 形式で記述されたコンポーネント パラメータ情報、または null イメージを指定できます。 { icon: './symbols/ inputBG.json ', ストレッチ: 'centerUniform' }}], [40, 260]);//2 番目のパラメータは、各要素の幅情報の配列です。1 より大きい幅の値は固定の絶対値を表し、1 以下の幅の値は相対値を表すこともできます。 80+0.3 の組み合わせ form.addRow([null, null , { id: 'searchInput', textField: {}}, { element: 'コンピュータ ルーム ビジュアル管理システム', color: 'white', font: '18px arial 、サンセリフ'}、null、 { button: { // ラベル: 'ViewChange'、アイコン: './symbols/viewChange.json'、背景: null、selectBackground: 'rgb(128,128,128)'、borderColor: 'rgba(0, 0, 0, 0 ) ', onClicked: function() { E.focusTo() } }}, null, { button: { // ラベル: 'アラート', アイコン: './symbols/alarm.json'、切り替え可能: true、選択済み: false、背景: null、selectBackground: 'rgb(128,128,128)'、borderColor: 'rgba(0, 0, 0, 0)'、onClicked: function( e) { E.setAlarmVisible(this.isSelected()) } }}, null], [40, 42、218、300、0.1、50、10、50、10]);
上記は可能なだけですが、実際には html タグには追加されません。つまり、インターフェースには何も存在しないことになります。ページの読み込み時に 3D シーンを本文に追加することを忘れないでください。また、ウィンドウ サイズ変更イベントを設定するときは、フォームもリアルタイムで更新する必要があります。
window.addEventListener('load', function() { g3d.addToDOM(); // 3D シーンを本文に追加します document.body.appendChild(E.top.getView()); // の基礎となる div を追加しますform コンポーネント 本体に追加 window.addEventListener('resize', function() {//ウィンドウ サイズ変更イベントをリッスン E.top.iv();// フォーム form の基礎となる div を更新します});});
HTの仕組みを理解する上で非常に重要なaddToDOM関数について説明します。 HT コンポーネントは通常、BorderPane、SplitView、TabView などのコンテナに埋め込まれます。最も外側の HT コンポーネントでは、getView() によって返される基礎となる div 要素をページの DOM 要素に手動で追加する必要があります。 , 親コンテナのサイズが変更されたとき、親コンテナが BorderPane や SplitView などの HT の定義済みコンテナ コンポーネントの場合、HT コンテナは自動的に子コンポーネントの無効化関数を再帰的に呼び出して更新を通知します。しかし、親コンテナがネイティブ HTML 要素である場合、HT コンポーネントはそれを更新する必要があることを認識できないため、通常、最も外側の HT コンポーネントはウィンドウのウィンドウ サイズ変更イベントをリッスンし、最も外側の無効化関数を呼び出す必要があります。更新するコンポーネント。
最も外側のコンポーネントをウィンドウ全体にロードしやすくするために、HT のすべてのコンポーネントには addToDOM 関数があり、その実装ロジックは次のとおりです。ここで、iv は validate の省略形です。
addToDOM = function(){ var self = this, view = self.getView(), style = view.style; document.body.appendChild(view); // シーンの基になる div を body に追加します style.left = ' 0';//HT はすべてのコンポーネントの基になる div の位置を絶対に設定します style.right = '0'; style.top = '0'; window.addEventListener('resize', function () { self.iv(); }, false); // ウィンドウ サイズの変更をイベントでリッスンし、コンポーネントの変更と更新を通知します。
このようにして、すべてのコードが終了し、右クリックして自分で確認することができ、対応する json ファイルをネットワークから取得できます。
以上がこの記事の全内容です。皆様の学習のお役に立てれば幸いです。また、VeVb Wulin Network をご支援いただければ幸いです。