基於 HTML5 WebGL 的 3D 水泥工廠生產線

前言

今天爲你們帶來一個很酷的做品,依然運用了強大的 HT for Web 的 3D 圖形組件,動做流暢性能好,你們能夠先來欣賞一下效果!dom

點我進入!函數

總體風格爲科技金屬風,製做精良,因爲上傳 gif 大小有限制,因此務必打開連接查看細節演示!性能

代碼實現

作完場景後,首先咱們要對它進行一些基本的設置,如:動畫

// 設置 camera 的位置
gv.setEye([457, 9047, 434])
// 設置中心點位置
gv.setCenter([-4, -1, 0])
// 設置遠端距離
gv.setFar(500000)

設置後可讓場景在反序列化後可以顯示出咱們想要的展現角度,設置遠端位置可以避免形成場景顯示不徹底等問題。spa

爲了使其看起來有一個進入的過程,咱們給場景增長一個入場的動畫來增色:設計

ht.Default.startAnim({
    duration: 3000, // 動畫週期毫秒數,默認採用`ht.Default.animDuration`
    action: function (v) { // action函數必須提供,實現動畫過程當中的屬性變化。
        gv.setEye([gv.getEye()[0] + (1117 - gv.getEye()[0]) * (v / 5), gv.getEye()[1] + (450 - gv.getEye()[1]) * (v / 5), gv.getEye()[2] + (1139 - gv.getEye()[2]) * (v / 5)])
    },
    finishFunc: function () { // 動畫結束後調用的函數。
        gv.scene = {
            eye: ht.Default.clone(gv.getEye()),
            center: ht.Default.clone(gv.getCenter()),
            far: ht.Default.clone(gv.getFar()),
            near: ht.Default.clone(gv.getNear())
        }
    }
})

這個動畫咱們的思路是經過改變 camera 的位置來的實現,使用動畫函數咱們能夠在指定的時間週期內完成動畫,可理解爲將某些屬性由起始值逐漸變到目標值的過程。經過在 action 函數中咱們對 carmera 進行細緻地調整,就能夠實現完美的入場效果了。finishFunc 函數中咱們作了一個複製的操做,目的是要記住這個位置,以便於咱們後面的功能實現,這個稍後會提到。3d

對了,咱們還要對整個場景的視角及範圍作限制:code

var mapInteractor = new ht.graph3d.MapInteractor(gv)
gv.setInteractors([
    mapInteractor
])
gv.mp(function (e) {
    if (e.property === "eye") {
        if (gv.getEye()[0] > 3500) {
            gv.getEye()[0] = 3500
        }
        if (gv.getEye()[0] < -3500) {
            gv.getEye()[0] = -3500
        }
        if (gv.getEye()[1] > 9000) {
            gv.getEye()[1] = 9000
        }
        if (gv.getEye()[2] > 3500) {
            gv.getEye()[2] = 3500
        }
        if (gv.getEye()[2] < -3500) {
            gv.getEye()[2] = -3500
        }
    }
})

這樣能夠限制翻轉到場景底面,而後再對 eye 作限制防止在拉遠的時候超出天空球包裹的範圍。blog

接下來咱們要加一個場景視角復位的功能:事件

gv.mi(function (e) {
    if (e.kind === 'doubleClickBackground') {
        gv.moveCamera(gv.scene.eye, gv.scene.center, true)
    }
    ...
})

事件監聽一下,在雙擊的時候經過 moveCamera() 來移動中心點的位置,座標就是咱們在入場動畫的操做中記錄的位置。

爲了增強性能及便利性,咱們在點擊事件中再添加一個控制面板開關的的邏輯,這樣能夠簡約化顯示:

if (e.kind === 'clickData') {
   if (e.data.getTag() === '按鈕') {
        var status = dm.getDataByTag('面板1').s('3d.visible')
        for (var i = 1; i <= 10; i++) {
            dm.getDataByTag('面板' + i).s('3d.visible', !status)
        }
    }
}

經過對 2D 面板的屬性改變來實現以下效果:

面板數值的變化也經過綁定的屬性來修改,爲了作演示,我用一些隨機數來代替,這裏就很少說了。

而後咱們要把管道的流動、履帶的運行、迴轉窯的運動以及磨輪轉動等動畫先實現出來:

function flow(name1, name2) {
    for (var i = 1; i <= 6; i++) {
        // uv 偏移
        if (name2) {
            dm.getDataByTag(name2 + i).s('shape3d.uv.offset', [dm.getDataByTag(name2 + i).s('shape3d.uv.offset')[0] + 0.005, dm.getDataByTag(name2 + i).s('shape3d.uv.offset')[1]])
        }
        // 設默認值
        else {
            dm.getDataByTag(name1 + i).s('shape3d.uv.offset', [0,0])
        }
    }
}
// 儲料罐
function tank(name, num, v) {
    for (var i = 1; i <= 8; i++) {
        dm.getDataByTag(name + i).setScaleTall(dm.getDataByTag(name + i).getScaleTall() + (num[i - 1] - dm.getDataByTag(name + i).getScaleTall()) * v)
    }
}
// 滾輪
function roller(name) {
    for (var i = 1; i <= 12; i++) {
        if (i % 2 === 0) {
            value = -0.1
        }
        else {
            value = 0.1
        }
        if (i <= 8) {
            dm.getDataByTag(name + i).r3(dm.getDataByTag(name + i).r3()[0], dm.getDataByTag(name + i).r3()[1] + value, dm.getDataByTag(name + i).r3()[2])
        }
        else {
            dm.getDataByTag(name + i).r3(dm.getDataByTag(name + i).r3()[0] + value, dm.getDataByTag(name + i).r3()[1], dm.getDataByTag(name + i).r3()[2])
        }
    }
}
anim()
function anim() {
    var num = []
    for (var i = 1; i <= 8; i++) {
        num.push(Math.random() * 6)
    }
    ht.Default.startAnim({
        duration: 1000,
        action: function (v, t) {
            dm.getDataByTag('流動2').r3(dm.getDataByTag('流動2').r3()[0] - 0.1, dm.getDataByTag('流動2').r3()[1], dm.getDataByTag('流動2').r3()[2])
            flow('流動', '流動')
            tank('儲料罐', num, v)
            roller('滾輪')
        },
        finishFunc: function () {
            anim()
        }
    })
}

我把他們統一放在一個動畫函數中循環播放,都是一些比較簡單的動畫,經過使高度、角度等屬性的變化來實現相應的動畫效果,如代碼所示不一一細述。這裏我稍微說一下關於這個管道和履帶流動的實現思路,我是利用了調整 UV 貼圖來完成的。

什麼是 UV ?通俗的講,UV 就是把三維立體模型的外表面剝離下來,展開鋪平成二維平面狀態,以便進行貼圖繪製,就如同香菸盒上的包裝圖案實際上是在紙盒片狀態下印刷完成的同樣。

首先咱們須要繪製一張二方連續貼圖(左右或上下能夠無縫銜接的貼圖),而且依照場景中管道和傳送帶流動的方向,將UV展成長條狀,與貼圖相匹配。

而後咱們在經過代碼驅動 UV 向 U 軸的正值方向偏移一個象限,並沒有限循環這一動做。回到三維場景中,你就會神奇的發現,管道和傳送帶在不間斷的流動着!

最後咱們來完成卡車的運行動畫,總體流程先設計好:

var truck1 = dm.getDataByTag('卡車1')
var truck2 = dm.getDataByTag('卡車2')
var truck3 = dm.getDataByTag('卡車3')
var cargo1 = dm.getDataByTag('貨鬥1')
var cargo2 = dm.getDataByTag('貨鬥2')
var coal = dm.getDataByTag('貨1')
var limestone = dm.getDataByTag('貨2')
var panel1 = dm.getDataByTag('面板8')
var panel2 = dm.getDataByTag('面板9')
anim1()
// 出發
function anim1() {
    ht.Default.startAnim({
        duration: 4000,
        easing: function (t) { return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2)) },
        action: function (v, t) {
            truck1.p3(truck1.p3()[0], truck1.p3()[1], truck1.p3()[2] + (700 - truck1.p3()[2]) * (v / 10))
            truck2.p3(truck2.p3()[0], truck2.p3()[1], truck2.p3()[2] + (700 - truck2.p3()[2]) * (v / 5))
            truck3.p3(truck3.p3()[0] + (300 - truck3.p3()[0]) * (v / 20), truck3.p3()[1], truck3.p3()[2])
        },
        finishFunc: function () {
            anim2()
        }
    })
}
// 掉頭
function anim2() {
    ht.Default.startAnim({
        duration: 1000,
        action: function (v, t) {
            truck1.r3(truck1.r3()[0], truck1.r3()[1] + (180 * Math.PI / 180 - truck1.r3()[1]) * v, truck1.r3()[2])
            truck2.r3(truck2.r3()[0], truck2.r3()[1] + (180 * Math.PI / 180 - truck2.r3()[1]) * v, truck2.r3()[2])
            truck3.r3(truck3.r3()[0], truck3.r3()[1] + (-90 * Math.PI / 180 - truck3.r3()[1]) *  v, truck3.r3()[2])
        },
        finishFunc: function () {
            anim3()
        }
    })
}
// 卸貨
function anim3() {
    ht.Default.startAnim({
        duration: 2000,
        action: function (v, t) {
            cargo1.r3(cargo1.r3()[0] + (70 * Math.PI / 180 - cargo1.r3()[0]) * v, cargo1.r3()[1],  cargo1.r3()[2])
            cargo2.r3(cargo2.r3()[0] + (70 * Math.PI / 180 - cargo2.r3()[0]) * v, cargo2.r3()[1],  cargo2.r3()[2])
            panel1.a('進度值', panel1.a('進度值') + (0 - panel1.a('進度值')) * v)
            panel2.a('進度值', panel2.a('進度值') + (0 - panel2.a('進度值')) * v)
            panel1.a('重量', panel1.a('進度值').toFixed(1))
            panel2.a('重量', panel2.a('進度值').toFixed(1))
        },
        finishFunc: function () {
            coal.s('3d.visible', false)
            limestone.s('3d.visible', false)
            anim4()
        }
    })
}
// 卸貨
function anim4() {
    ht.Default.startAnim({
        duration: 2000,
        action: function (v, t) {
            cargo1.r3(cargo1.r3()[0] + (0 * Math.PI / 180 - cargo1.r3()[0]) * v, cargo1.r3()[1],  cargo1.r3()[2])
            cargo2.r3(cargo2.r3()[0] + (0 * Math.PI / 180 - cargo2.r3()[0]) * v, cargo2.r3()[1],  cargo2.r3()[2])
        },
        finishFunc: function () {
            anim5()
        }
    })
}
// 返回
function anim5() {
    ht.Default.startAnim({
        duration: 4000,
        easing: function (t) { return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2)) },
        action: function (v, t) {
            truck1.p3(truck1.p3()[0], truck1.p3()[1], truck1.p3()[2] + (1180 - truck1.p3()[2]) * (v / 10))
            truck2.p3(truck2.p3()[0], truck2.p3()[1], truck2.p3()[2] + (1180 - truck2.p3()[2]) * (v / 5))
            truck3.p3(truck3.p3()[0] + (1180 - truck3.p3()[0]) * (v / 20), truck3.p3()[1], truck3.p3()[2])
        },
        finishFunc: function () {
            anim6()
        }
    })
}
// 掉頭
function anim6() {
    ht.Default.startAnim({
        duration: 1000,
        action: function (v, t) {
            truck1.r3(truck1.r3()[0], truck1.r3()[1] + (0 * Math.PI / 180 - truck1.r3()[1]) * v, truck1.r3()[2])
            truck2.r3(truck2.r3()[0], truck2.r3()[1] + (0 * Math.PI / 180 - truck2.r3()[1]) * v, truck2.r3()[2])
            truck3.r3(truck3.r3()[0], truck3.r3()[1] + (90 * Math.PI / 180 - truck3.r3()[1]) * v, truck3.r3()[2])
        },
        finishFunc: function () {
            panel1.a('進度值', 1)
            panel2.a('進度值', 1)
            panel1.a('重量', 10)
            panel2.a('重量', 10)
            coal.s('3d.visible', true)
            limestone.s('3d.visible', true)
            anim1()
        }
    })
}

這個是我實現卡車的整個運做流程的完整代碼,分別由幾段動畫協調組合而成,只要搞清楚順序以及每個動做實現的邏輯並不難辦到,無非就是方向、角度和距離的一些計算,還有面板進度條同步的設置。

通過咱們的努力後,一個炫酷專業的工廠流程系統的演示咱們就完成了!

總結

在互聯網+ 概念飛速發展的今天,有太多的領域在等待着咱們去挖掘,HT For Web 很是適用於各類的智慧建築,監控系統以及電力、燃氣等工業自動化 ( HMI / SCADA ) 領域。但願看了個人這篇博,你們能有所啓發,挑戰更多的不可能!

相關文章
相關標籤/搜索