基於 HTML + WebGL 結合 23D 的疫情地圖實時大屏 PC 版

圖撲軟體發表於2020-04-06

前言

2019年12月以來,湖北省武漢市陸續發現了多例肺炎病例,現已證實為一種新型冠狀病毒感染引起的急性呼吸道傳染病並蔓延全國,肺炎疫情牽動人心,人們每天起來第一件事變成了關注疫情進展,期望這場天災早日結束,社會回歸正常秩序。不久前在前端小夥伴和設計小夥伴的合作下我們推出了移動版疫情地圖GIS版疫情地圖,而這次我們又推出了 PC 版的疫情地圖,淺色系的色調搭配 2D,3D 的方式綜合展示疫情的最新動態和歷史資料,希望能給大家帶來一點比較愉悅的觀感體驗,減輕下焦慮。

先開啟頁面感受一下:

點選預覽

還有 GIS 版的預覽:

GIS 版可在上方導航欄內點選開啟,想單獨開啟可以點選 GIS版疫情地圖

資料來源說明

CDC, 百度,騰訊,丁香園都有自己的疫情地圖,我們的資料也來源於此,這部分在之前的文章 《HTML5 WebGL 實現 3D 地圖助力新型冠狀病毒疫情實時資料視覺化》 有詳細說明,就不再贅述了。

系統介紹

資料展示部分:
1.先從左上的表格開始,這部分展示了各省的累計資料和該省份各地區的資料,可點選展開或摺疊,點選 3D 地圖相應的位置篩選出該地區的相應資料單獨展示,點選空白處恢復顯示全國資料。
2.接下來是左下的疫情播報部分,這部分的資料採集了各地衛健委資料輪播展示,這裡加上了表格縱偏移緩動效果使得滾動更自然美觀。
3.底部最顯眼的是最新的確診,疑似,治癒,死亡等資料,往上的位置還有一條時間軸,點選旁邊的播放按鈕會動態改變頁面的資料,根據不同日期各地區確診人數來對地圖染色,與旁邊的色卡對比能清晰地觀察出各地區疫情的嚴重程度。
4.右側圖表展示了按時間排序的確診,疑似,治癒,死亡等資料,一眼就能從圖表中讀出疫情的發展趨勢。
5.上方導航欄,點選可預覽 GIS 版的疫情地圖, CDC 官網, 還有移動版的疫情地圖。

3D 地圖部分:

  1. 漸入效果。開啟這個demo後 3D 地圖 緩緩地從遠處飛入眼前,過渡自然,這部分通過 ht 自帶的 startAnim 函式實現,可以指定任意的緩動函式來實現不同的飛入效果,這在接下來的篇幅裡會介紹。
  2. 省份點選的效果,從武漢向外輸出的流動動畫,各省省會位置的旋轉動畫。

程式的示意圖:

1)2D,3D 場景的執行和互動

2)全域性的事件管理器
整個專案中涉及到 2D, 3D 的互動比較多,因而用了一個全域性事件管理器類例項來管理:

class NotifierManager {
    constructor() {
        this._eventMap ={};
    }

    add(key, func, scope, first = false) {
        let notify = this._eventMap[key];
        if (!notify) notify = this._eventMap[key] = new ht.Notifier();

        notify.add(func, scope, first);
    }

    fire(key, e) {
        const notify = this._eventMap[key];
        if (!notify) return;

        notify.fire(e);
    }
}

const event = new NotifierManager();

主要程式碼:

1)點選 3D 地圖
使用 ht 的事件派發改變 2D 內容:

相關程式碼如下:

event.fire('clickProvince', {
    data : this.g3dProvince,
    node : data
});
clickProvince(dataList, node) { 
    let province = node.getDisplayName();
    // 選中的省份透明度為1,其他為0.4
    dataList.forEach(data => {
        data.s('shape3d.opacity', data === node ? 1: 0.4 );
    })
    // 只顯示該省份資料
    if (this.areaDatas && this.areaDatas.length > 0) {
        let provinceData = [];
        this.areaDatas.forEach((item) => {
            if (item.area === province || item.host === province) {
                item.expand = true;
                provinceData.push(item);
            }
        });
        this.detailTable.a('ht.dataSource', provinceData);
    }
}

2)滑塊播放
滑塊播放使用了 startAnim 的 Frame-Based 來開啟動畫:

相關程式碼如下:

// 滑塊播放
playDateSlider() {
    this.playMenu.s('state', '暫停');
    let value =  this.dateSlider.a('ht.value');
    let x = this.dateSlider.a('dateArea')[0];
    // 如果到了最後一天的資料重置
    if (value >= 100 || x >= SLIDER_TIP_END_X) {
        // 重置地圖背景色
        event.fire('resetMapColor');
        value = this.sliderValue = 0;
        x = SLIDER_TIP_START_X;
        this.dateSlider.s({
            'ht.value' : 0,
            'text' : this.sliderDateList[0],
        });
        this.dateSlider.a('dateArea', [SLIDER_TIP_START_X, SLIDER_TIP_Y, SLIDER_TIP_WIDTH, SLIDER_TIP_HEIGHT]);
    }
    // 開啟動畫
    this.sliderAnim = ht.Default.startAnim({
        frames: 100,
        interval: 300,
        action: () => {
            this.onPlay = true;
            this.timerJudgment(value);
            // 到最大值時停留
            if (value === 100) {
                this.onPlay = false;
                this.dateSlider.a('ht.value', value);
                // 最新資料
                this.initBottom(17);
                this.playMenu.s('state', '播放');
                this.sliderAnim.stop();
            };
            value += 1;
            this.sliderValue += 1;
            this.dateSlider.a('ht.value', value);
            x += STRIDE;
            if (x >= SLIDER_TIP_END_X) {
                this.dateSlider.a('dateArea', [SLIDER_TIP_END_X, SLIDER_TIP_Y, SLIDER_TIP_WIDTH, SLIDER_TIP_HEIGHT]);
            }
        }
    })
}

3)3D 入場動畫
對於視角變化類的動畫,ht 自帶了 moveCamera 方法。相關程式碼如下:

function flyToView(g3d, eye, center, cb) {
    g3d.moveCamera(eye, center, {
        duration: 3000,
        easing: Easing.swing,
        finishFunc: function () {
            if (cb) {
                cb();
            } else {
                return;
            }
        }
    });
}

4) 2D 表格動畫。
每隔 1.5 秒改變表格的縱向偏移值,並把這個過程通過幀動畫的方式播放出來,實現流暢的瀏覽體驗,相關程式碼如下:

// 疫情播報動畫
playTable() {
    let table =  this.newsTable;
    // 表格 Y 軸偏移
    let translateY =  table.a('ht.translateY');
    this.tableInterval = ht.Default.startAnim({
        frames: Infinity,
        interval: 1500,
        action: () => {
            if (translateY < 0) {
                translateY = 0;
            }

            let temp = this.articleDataSource.shift();
            this.articleDataSource.push(temp);
            
            ht.Default.startAnim({
                frames: 20,
                interval: 10,
                finishFunc: () => {
                    translateY -= 32;
                },
                action: (v, t) => {
                    table.a('ht.translateY', translateY - 32 * v); 
                }
            });
        }
    });
}

5) 預覽其他版本的疫情地圖和 CDC 官網
使用 ht 的事件派發進行圖紙載入和切換處理,以 GIS 版的為例:

相關程式碼如下:

if (kind === 'mobile') {
    event.fire('showMobile');
} 
else if (kind === 'cdc') {
    event.fire('showCdc');
} 
else if (kind === 'gis') {
    event.fire('showGis');
}

總結

疫情地圖 PC 版大致效果就是這些,可以感受到到現代瀏覽器支援的 3D 技術可實現非常直觀的效果,互動性也很好。物聯網和即將到來的 5G 給眾多行業帶來了新的機遇,也帶來了新的挑戰,海量的資料該如何更生動地展示在人們面前?通過跨平臺的瀏覽器無疑是最好的選擇。WebGL 技術依託於瀏覽器,對於資料的處理有著天然的優勢,圖撲軟體作為在工業視覺化領域的一線重度參與者,沉澱了許許多多寶貴的行業經驗,前不久對剛過去的 19 年做了總結回顧和分享,整理出了 《2019-分享數百個 HT 工業網際網路 2D 3D 視覺化應用案例分享》,希望各位看官們喜歡。

寫在最後

當前,在舉國上下一盤棋,戮力同心戰疫情的不懈奮鬥下,疫情的傳播與蔓延已經得到初步控制。當突發事件來臨時我很幸運自己是這個國家的一員,面對疫情時各個公共機構的執行力有效地扼制了疫情的擴散,一方有難八方支援,保證了各地區物資供給,也愈加堅定了全國人民打贏這場戰役的決心。但遺憾的是,新冠病毒的魔爪卻伸向了世界其他地方,但我相信在中國人民和世界各國人民的共同合作抗爭下,一定能阻止這場災難的蔓延。圖撲科技也將關注最新的動態,以自己的方式為疫情加油助力。這裡做個預告,世界版的疫情地圖很快就會與大家見面,敬請期待!

相關文章