HTML5 + WebGLに基づく3Dウインドファーム可視化システム

序文

風力エネルギーは、開発中の一種のクリーンエネルギーであり、無尽蔵で無尽蔵です。もちろん、風力発電所の建設では、まず気象条件と社会の自然条件を考慮する必要があります。近年、中国の洋上および陸上風力は急速に発展しています。海水と土地は私たちの風力発電に良い地質学的保証を提供します。私たちの風力に無尽蔵のエネルギーを提供するのはこれらのサイトです。現在、私たちはこれらの領域を探索するために懸命に取り組んでいます。

この記事では、風力発電所の全体的なプロセスを実装します。誰でも完全な風力発電プレビューシステムを見ることができます。

このプロジェクトはHightopo  の  HT for Web  製品を使用して構築されたことに注意してください 

プレビューアドレス:hightopo.com/demo/wind-p ...

一般的なプロセス

以下は、プロジェクト全体のフローチャートです。ホームページからサイト配信ページ、集中管理ページに入ることができます。

サイト配布ページには、2つの異なる3Dシーン、すなわち、陸風空港と海風空港があります。2つの3D風力空港をクリックすると、最終的に3D風力タービンのシーンに入ります。

プレビュー効果

ホーム:

1.世界地図効果

2.中国地図効果

2.都市マップ効果

中央管理センターページ(アニメーション効果なし):

サイト配布ページ(アニメーション効果なし)

ランドウィンド空港:

シーウィンド空港:

コードの実装

ホームページの地球には、世界地図、中国地図、都市地図の3つの視点状態があります。各ステータスカメラをクリックして、対応する位置に移動します。その前に、対応する中心  とを事前に保存する必要が  あり  ます。

 特にデータを提供するために使用される新しいdata.jsファイルを作成する方がよいでしょう 

関連する疑似コードは次のとおりです。

// 记录位置
var cameraLocations = {
    earth: {
        eye: [-73, 448, 2225],
        center: [0, 0, 0]
    },

    china: {
        eye: [-91, 476, 916],
        center: [0, 0, 0]
    },

    tsankiang: {
        eye: [35, 241, 593],
        center: [0, 0, 0]
    }
}

データを取得した後、わかりました。次にイベントを聞いてみます。ボタンをクリックするか、強調表示された領域(世界地図ではボタンのみクリック可能)をクリックして、中国地図のパースペクティブに入ることができます。

最初にこれらの2つのノードを取得してから、それらのクリックイベントに対して同じことを行うことができます。しかし、私はこの方法を最適化し、考え方に置き換えることができると思います。

最初にイベントをフィルタリングできます。2つ配列を作成します。1つはclickやonEnterのよう に実行できるイベントを保持し、もう1つは  イベントをトリガーできるすべてのノードを保持します。これにより、構造を維持し、より明確にすることができます。

次の図では、現在のノードにイベントのアクセス許可がないか、現在のイベント自体にアクセス許可がない場合、フィルターで除外されていることがわかります。両方が正常に戻ることができる場合、対応するイベントが実行されます。

関連する疑似コードは次のとおりです。

// 权限事件
this.eventMap = {
    clickData: true,
    onEnter: true,
    onLeave: true
}

// 权限节点
this.nodeMap = {
    outline: true,
    outline2: true,
    earth: true,
    bubbles: true,
    circle: true
}

/**
  * 监听事件
  */
initMonitor() {
    var gv = this.gv
   var self = this
    var evntFlow = function (e) {
        var event = e.kind
        var tag = e.data && e.data.getTag()

         // 检查当前事件或者节点是否能够被执行
         if (!self.eventMap[event] && !self.nodeMap[tag]) return false

         self.nodeEvent(event, tag)
    }

    gv.mi(eventFlow)
}    

現在実行しているノードが要件を満たしている限り、  イベント (現在実行中のイベント)  と  タグ(ノードタグ  を実行関数  nodeEventに渡して  実行します** **これは、これらの無効なイベントまたはノードを処理するためのリソースを無駄にしません。

nodeEventの  処理方法を見てみましょう 

関連する疑似コードは次のとおりです。

/**
 * 气泡事件
 * @param { string } event 当前事件
 * @param { string } propertyName 当前节点标签
 */
bubblesEvent(event, propertyName) {
    var dm = this.dm
    var account = dm.getDataByTag('account')
    var currentNode = dm.getDataByTag(propertyName)
    var self = this

    var clickData = function() {
        // 执行清除动作
        self.clearAction()
    }

    var onEnter = function() {
       // do something
    }

    var onLeave = function() {
    // do something
    }

    var allEvent = { clickData, onEnter, onLeave }

    allEvent[event] && allEvent[event]()
}

ご覧のとおり、  propertyName(ノードラベル)  文字列を使用してメソッド名を作成できます。たとえば、現在のノードラベルは  バブルです。 この[`$ {properName} Event`]の  後 のメソッドは、  この['bubblesEvent']です。もちろん、この方法は事前に定義されています。

特定のノードメソッドで、対応するイベント関数を作成しました。渡されたイベントに従って   、対応するメソッドがあるかどうかを判断します。ある場合は実行し、ない場合はfalseを返し  ます  。これの利点は、デカップリング、単純な構造、問題の迅速な位置付けです。

しかし、よく考えてみると、世界地図と中国地図をクリックしても機能はほぼ同じです!それらをマージできると、はるかに便利になります!コードを変更してみましょう。

関連する疑似コードは次のとおりです。

/**
  * 执行节点事件
  */
nodeEvent(event, propertyName) {
    // 过滤是否有可以合并的事件
    var filterEvents = function(propertyName) {
        var isCombine = false     
        var self = this
        if (['earth', 'china'].includes(propertyName)) {
            self.changeCameraLocaltions(event, propertyName)
            isCombine = true
        }

        return !isCombine
    }
   var eventFun = this[`${propertyName}Event`]
   // 执行对应的节点事件
   filterEvents(propertyName)
   &&
   eventFun  
   &&
   eventFun(event, propertyName)
}

現在のイベントをマージできるかどうかを事前に判断し、それが** false **を返した場合、次のコードを実行しないで、独自の関数を実行します。

この時点で、我々はから、対応するノードのラベルを渡すことができ  data.js cameraLocationsが  対応する変数に取られ  、中央の目を。

関連する疑似コードは次のとおりです。

/**
 * 移动镜头动画
 * @param { object } config 坐标对象
 */
moveCameraAnim(gv, config) {
  var eye = config.eye  var center = config.center  // 如果动画已经存在,进行清空
   if(globalAnim.moveCameraAnim) {
      globalAnim.moveCameraAnim.stop()    globalAnim.moveCameraAnim = null
   }

   var animConfig = {
     duration: 2e3
   }

   globalAnim.moveCameraAnim = gv.moveCamera(eye, center, animConfig)
}

// 需要改变相机位置
changeCameraLocaltions(event, properName) {
    var config = cameraLocations[properName]

    // 移动相机
    this.moveCameraAnim(this.gv, config)
}

カメラの移動アニメーションは、gv  の  moveCamera  メソッドを使用します。この  メソッドは、目(カメラ)、**センター(ターゲット)、およびアニメーション構成(アニメーション構成)の3つのパラメーターを受け入れます。**その後、我々は、現在のアニメーション戻す  globalAnim  の  moveCameraAnimは、  私たちはクリーンアップを支援するための属性。

次に、ページを切り替える時間です。これには細心の注意が必要です。特定の属性がクリアされないと、メモリリークなどの問題が発生し、パフォーマンスが徐々に低下します。ページがフリーズします!

したがって、特にデータモデルをクリアするための関数** clearActionが必要です。**すべてのアニメーションオブジェクトを1つのオブジェクトまたは配列に配置する必要があります。ページ切り替え時の整理に便利です。

関連する疑似コードは次のとおりです。

/**
 * 清除动作
 */
clearAction(index) {
    var { dm, gv } = this
    var { g3d, d3d } = window

    allListener.mi3d && g3d.umi(allListener.mi3d)
    allListener.mi2d && gv.umi(allListener.mi2d)
    dm.removeScheduleTask(this.schedule)

    dm && dm.clear()
    d3d && d3d.clear()

    window.d3d = null
    window.dm = null

    for (var i in globalAnim) {
        globalAnim[i] && globalAnim[i].pause()
        globalAnim[i] = null
    }

    // 清除对应的 3D 图纸
    ht.Default.removeHTML(g3d)

    gv.addToDOM()
    ht.Default.xhrLoad(`displays/HT-project_2019/风电/${index}.json`, function (text) {
        let json = ht.Default.parse(text)
        gv.deserialize(json, function(json, dm2, gv2, datas) {
            if (json.title) document.title = json.title

            if (json.a['json.background']) {
                let bgJSON = json.a['json.background']
                if (bgJSON.indexOf('scenes') === 0) {
                    var bgG3d

                    if (g3d) {
                        bgG3d = g3d
                    } else {
                        bgG3d = new ht.graph3d.Graph3dView()
                    }

                    var bgG3dStyle = bgG3d.getView()
                    bgG3dStyle.className = index === 1 ? '' : index === 3 ? 'land' : 'offshore'

                    bgG3d.deserialize(bgJSON, function(json, dm3, gv3, datas) {
                        init3d(dm3, gv3)
                    })

                    bgG3d.addToDOM()
                    gv.addToDOM(bgG3dStyle)
                }
                gv.handleScroll = function () {}
            }

            init2d(dm2, gv2)
        })
    })
}

まず、dm(データモデル)  と  gv(描画)を削除する必要があり   ます。また、注意してください:マイル(モニター機能)スケジュール(スケジュールされたタスク)  必要があります  ()dm.clear  前に  削除すべてのアニメーションはstop()  操作を実行してから、  その値をnullに設定し  ます  。ここで 、stopの実行後、finishFunc  コールバック関数が1回呼び出される  ことに注意してください 

2D図面に3D背景が含まれている場合、3Dインスタンスが既に存在するかどうかを確認する必要があります。存在する場合は、再度作成する必要はありません。興味がある場合は、webGLアプリケーションのメモリリークの問題について学ぶことができます。

2つの3Dシーンを入力する場合、最初のgifのように開始アニメーションが必要です。したがって、2つの開始アニメーションの中心と目を、定義したcameraLocationsに保存する必要があり   ます。

// 记录位置
var cameraLocations = {
    earth: {
        eye: [-73, 448, 2225],
        center: [0, 0, 0]
    },

    china: {
        eye: [-91, 476, 916],
        center: [0, 0, 0]
    },

    tsankiang: {
        eye: [35, 241, 593],
        center: [0, 0, 0]
    },

    offshoreStart: {
        eye: [-849, 15390, -482],
        center: [0, 0, 0]

    },

    landStart: {
        eye: [61, 27169, 55],
        center: [0, 0, 0]
    },

    offshoreEnd: {
        eye: [-3912, 241, 834],
        center: [0, 0, 0]
    },

    landEnd: {
        eye: [4096, 4122, -5798],
        center: [1261, 2680, -2181]
    }
}

offshoreStart、offshoreEnd、landStart、landEnd  は、オフショアおよびオンショアの発電所の開始位置と終了位置を表します**。**

現在の負荷がオフショア発電所かオンショア発電所かを判断する必要があります。対応する図面をロードするときにclassNameを追加でき  ます。

indexActionをclearAction  関数で  定義しました。陸上発電所をクリックすると3になり、オフショア発電所であれば4になります。

たとえば、ランドファームを読み込む必要がある場合は、g3d.className = index === 3? 'Land': 'offshore'と判断してclassName 追加  でき  ます。

その後、initで初期化判定を行います。

関連する疑似コードは次のとおりです。

init() {
    var className = g3d.getView().className
    
    // 执行单独的事件
    this.selfAnimStart(className)
    this.initData()

    // 监听事件
    this.monitor()
}

対応する** className、**パスを取得し、対応するタイプを渡して、対応する初期化イベントを実行します。 カメラアニメーションは、定義したmoveCameraAnim関数によって実行され  ます。

関連する疑似コードは次のとおりです。

/**
  * 不同风电场的开场动画
  */
selfAnimStart(type) {
    var gv = this.gv
    var { eye, center } = cameraLocations[`${type}End`]
    var config = {
        duration: 3000,
        eye,
        center,
     }

     this.moveCameraAnim(gv, config)
}

まとめ


このプロジェクトにより、風力発電についての理解が深まりました。それが風力発電所の地域的な利点であるか、または風力タービンの構造と動作原理であるかどうか。

このプロジェクトを終えて、多くの成長と洞察を得ました。テクノロジーの急速な成長のための良い方法は、常に詳細を選択することです。このプロジェクトは芸術作品であり、満足のいくものになるまで継続的に磨かれる必要があります。微妙なポイントごとに、背後のパフォーマンスに影響します。ですから、職人の精神で何でもやるべきです。

もちろん、一部のパートナーには、産業用インターネットの分野を探求する勇気があることを願っています。それだけではありません。これには、この分野にさらに楽しく実用的なデモを追加するための想像力が必要です。また、業界で多くの知識を学ぶことができます。

元の記事を314件公開 138のような 390,000以上にアクセス

おすすめ

転載: blog.csdn.net/u013161495/article/details/104276250