博客 / 詳情

返回

3D 場景預加載應用實現 | 圖撲軟件

image.png

預加載是在進入正式場景之前提前加載所需模型、材質、圖片等資源的技術手段,其核心價值在於消除資源加載等待,確保場景首次渲染即可完整呈現,從而提供無縫、流暢的用户體驗。在複雜的 Web 3D 可視化應用中,資源預加載尤為重要,可有效解決首次加載時的卡頓、白屏及交互延遲等問題。

預加載實現方案

基礎實現原理

HT for Web 中所有資源的請求都會經過 ht.Default.convertURL 方法,該方法提供了統一的資源請求入口:

  • 當返回字符串路徑時,框架會按此路徑發起資源請求;
  • 當返回 {data: content} 格式對象時,框架會直接使用 content 作為資源內容。

基於此特性,我們可實現預加載機制

// 資源緩存映射表
let sourceMap = {}; 
// 判斷是否為本地資源
functionisLocalUrl(url) {
    return url.startsWith('data:') || url.startsWith('blob:');
}
// 重寫 convertURL 方法
let oldFunc = ht.Default.convertURL;
ht.Default.convertURL = function(url) {
    if (isLocalUrl(url)) return url;
    
    let source = sourceMap[url];
    if (source) {
        return { data: source };
    }
    
    return oldFunc(url);
};

資源加載策略

部分資源預加

適用於資源量較大但部分關鍵資源需要優先加載的場景。

// 關鍵資源列表
let resources = [
    "models/model.json",
    "assets/texture.png",
    "symbols/symbols.json"
]

image.png

全量資源預加載

獲取所有已註冊資源進行預加載:

functiongetAllResources() {
    return [
        ...Object.keys(ht.Default.getImageMap()),
        ...Object.keys(ht.Default.getCompTypeMap()),
        ...Object.keys(ht.Default.getShape3dModelMap()),
        ...Object.keys(ht.Default.getMaterialMap()),
        ...Object.keys(ht.Default.getHdrTextureMap())
    ];
}
let allResources = getAllResources();

image.png

資源加載實現

// 資源類型判斷
const ResourceType = {
    IMAGE: /\.(png|jpg|gif|jpeg|bmp|svg)$/i,
    BUFFER: /\.(fbx|gltf|glb)$/i,
    HDR: /\.hdr$/i,
    TGA: /\.tga$/i,
    JSON: /\.json$/i
};

class ResourceLoader {
    constructor(resources) {
        this.resources = resources;
        this.loaded = 0;
    }

    load() {
        this.resources.forEach(url => {
            if (ResourceType.BUFFER.test(url)) {
                this.loadBuffer(url);
            }
            else if(ResourceType.IMAGE.test(url)) {
                this.loadImage(url);
            } // 其他資源類型處理...        
        });
    }

    loadBuffer(url) {
        ht.Default.xhrLoad(url, res => {
            this.onResourceLoaded(url, res);
        }, {
            responseType: 'arraybuffer'
        });
    }

    // 其他加載方法...
    onResourceLoaded(url, res) {
        sourceMap[url] = res;
        this.loaded++;
        this.onProgress(this.loaded, this.resources.length);
        if (this.loaded === this.resources.length) {
            this.onAllLoaded();
        }
    }

    // 加載進度回調
    onProgress(loaded, total) {
        console.log(`加載進度: ${loaded}/${total}`);
    };

    onAllLoaded() {
        console.log("所有資源預加載完成");
        // 進入主場景...   
    }
}

image.png

加載頁面優化

進度展示設計

class LoadingView{
    constructor() {
        this.initView();
    }

    initView(){
        this.dm = new ht.DataModel();
        this.view = new ht.graph.GraphView(this.dm);
        
        let style = this.view.getView().style;
        style.left = "0";
        style.right = "0";
        style.top = "0";
        style.bottom = "0";
        document.body.appendChild(this.view.getView());
    }
    
    // 更新進度
    updateProgress(percent) {
        
    }
    
    // 移除加載頁面
    remove() {
        setTimeout(() => {
            this.view.getView().remove();
            this.enterMainScene();
        }, 1);
    }
    
    // 進入主場景
    enterMainScene(){

    }
}

視覺優化建議

  • 進度條設計:使用圖標實現平滑動畫。

    image.png

  • 背景動畫:添加輕量級背景動畫提升等待體驗。

    image.png

  • 分段進度:將加載過程分為資源加載、場景初始化等階段。
  • 預估時間:基於已加載時間預測剩餘時間。

性能優化方案

Service Worker 離線緩存

// sw.js
let CACHE_NAME = 'ht-resources-v1';

self.addEventListener('fetch', event => {
    event.respondWith(caches.match(event.request).then(function (response) {
        if (response !== undefined) {
            return response;
        } else {
            return fetch(event.request).then(function (response) {
                if (event.request.url.indexOf('storage') != -1) {
                    let responseClone = response.clone();
                    caches.open(CACHE_NAME).then(function (cache) {
                        cache.put(event.request, responseClone);
                    });
                }
                return response;
            }).catch(function () {
                return caches.match('./');
            });
        }
    }));
});

self.addEventListener('activate', function (event) {
    event.waitUntil(clients.claim());
    event.waitUntil(
        caches.keys().then(function (keyList) {
            returnPromise.all(keyList.map(function (key) {
                if (key !== CACHE_NAME) {
                    // 刪除舊緩存
                    return caches.delete(key);
                }
            }));
        })
    );
});

資源壓縮策略

  • 模型優化:輕量化模型,對模型進行減面。
  • 圖片優化:圖片資源壓縮。

效果對比與總結

加載效果對比

預加載方案雖然初始顯示時間較長,但提供了更優的整體用户體驗:

  • 完整的場景一次性呈現,避免零碎加載造成的視覺割裂。
  • 無卡頓的交互體驗,所有資源已就緒。
  • 可控的等待預期,進度反饋降低用户焦慮。
  • 更穩定的性能表現,避免運行時資源加載導致的卡頓。

預加載效果如下
image.png

常規加載效果
image.png

image.png

適用場景

  • 大型 3D 場景:包含複雜模型和紋理的場景。

    image.png

  • 儀表盤應用:需要快速響應的監控系統。
  • 移動端應用:網絡條件不穩定的環境。
  • 演示系統:需要流暢演示效果的場合。

實施建議

  • 分階段實施:先對關鍵資源預加載,再逐步擴展。
  • 性能監控:添加加載時間統計和分析。
  • A/B 測試:對比不同策略的實際效果。

總結

綜上所述,本預加載方案能顯著提升 HT for Web 應用的加載性能和用户體驗,特別適合對流暢性要求高的可視化應用場景。在實際項目中,應根據資源規模和用户場景靈活調整預加載策略,平衡加載時間和用户體驗。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.