最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳細(xì)介紹如何使用Three.js創(chuàng)建交互式3D地球模型

 更新時(shí)間:2026年04月04日 10:22:01   作者:Java猿_畢業(yè)設(shè)計(jì)  
在現(xiàn)代Web開(kāi)發(fā)中,Three.js作為一個(gè)強(qiáng)大的3D圖形引擎,已被廣泛應(yīng)用于地理信息可視化、數(shù)據(jù)展示和數(shù)字孿生等領(lǐng)域,這篇文章主要介紹了如何使用Three.js創(chuàng)建交互式3D地球模型的相關(guān)資料,需要的朋友可以參考下

前言

在現(xiàn)代Web開(kāi)發(fā)中,3D圖形可視化已經(jīng)成為一個(gè)熱門(mén)話(huà)題。Three.js作為最流行的3D庫(kù)之一,為我們提供了強(qiáng)大的工具來(lái)創(chuàng)建引人入勝的3D場(chǎng)景。本文將詳細(xì)介紹如何使用Three.js創(chuàng)建一個(gè)交互式的3D地球模型,并逐步優(yōu)化其性能,最終實(shí)現(xiàn)一個(gè)帶有國(guó)家名稱(chēng)標(biāo)簽的流暢3D地球。

一、Three.js簡(jiǎn)介

Three.js是一個(gè)基于WebGL的JavaScript 3D庫(kù),它封裝了復(fù)雜的WebGL API,讓開(kāi)發(fā)者能夠更輕松地創(chuàng)建和展示3D場(chǎng)景。Three.js提供了豐富的幾何體、材質(zhì)、燈光和相機(jī)等組件,使得3D開(kāi)發(fā)變得更加簡(jiǎn)單。

二、基礎(chǔ)3D地球模型構(gòu)建

2.1 初始化場(chǎng)景

首先,我們需要?jiǎng)?chuàng)建一個(gè)基本的Three.js場(chǎng)景:

// 創(chuàng)建場(chǎng)景
scene = new THREE.Scene();
// 創(chuàng)建相機(jī)
camera = new THREE.PerspectiveCamera(
    75, 
    window.innerWidth / window.innerHeight, 
    0.1, 
    1000
);
camera.position.z = 2.5;
// 創(chuàng)建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);

2.2 創(chuàng)建地球幾何體

接下來(lái),我們創(chuàng)建一個(gè)球體作為地球的基礎(chǔ)幾何體:

// 創(chuàng)建地球幾何體
const geometry = new THREE.SphereGeometry(1, 64, 64);
// 加載貼圖
const textureLoader = new THREE.TextureLoader();
const map = textureLoader.load('https://threejs.org/examples/textures/planets/earth_atmos_2048.jpg');
const bumpMap = textureLoader.load('https://threejs.org/examples/textures/planets/earth_normal_2048.jpg');
const specularMap = textureLoader.load('https://threejs.org/examples/textures/planets/earth_specular_2048.jpg');
// 創(chuàng)建材質(zhì)
const material = new THREE.MeshPhongMaterial({
    map: map,
    bumpMap: bumpMap,
    bumpScale: 0.05,
    specularMap: specularMap,
    specular: new THREE.Color(0x333333),
    shininess: 5
});
// 創(chuàng)建地球網(wǎng)格
earth = new THREE.Mesh(geometry, material);
earth.position.set(0, 0, 0);
scene.add(earth);

三、添加交互功能

為了讓地球可以旋轉(zhuǎn),我們需要實(shí)現(xiàn)鼠標(biāo)拖拽功能:

// 鼠標(biāo)按下處理
function onMouseDown(event) {
    isDragging = true;
    previousMousePosition = {
        x: event.clientX,
        y: event.clientY
    };
}
// 鼠標(biāo)移動(dòng)處理
function onMouseMove(event) {
    if (isDragging) {
        const deltaX = event.clientX - previousMousePosition.x;
        const deltaY = event.clientY - previousMousePosition.y;
        // 更新地球旋轉(zhuǎn)
        earth.rotation.y += deltaX * 0.01;
        earth.rotation.x += deltaY * 0.01;
        previousMousePosition = {
            x: event.clientX,
            y: event.clientY
        };
    }
}

四、添加國(guó)家名稱(chēng)標(biāo)簽

4.1 坐標(biāo)轉(zhuǎn)換

要將地理坐標(biāo)轉(zhuǎn)換為3D坐標(biāo),我們需要實(shí)現(xiàn)經(jīng)緯度到向量的轉(zhuǎn)換:

function latLongToVector3(lat, lng, radius) {
    const phi = (90 - lat) * Math.PI / 180;
    const theta = (lng + 180) * Math.PI / 180;
    return new THREE.Vector3(
        -radius * Math.sin(phi) * Math.cos(theta),
        radius * Math.cos(phi),
        radius * Math.sin(phi) * Math.sin(theta)
    );
}

4.2 標(biāo)簽可見(jiàn)性判斷

為了確保標(biāo)簽只在地球正面顯示,我們需要計(jì)算標(biāo)簽與相機(jī)的相對(duì)位置:

// 只有當(dāng)前面可見(jiàn)時(shí)才顯示標(biāo)簽
const cameraVector = new THREE.Vector3().subVectors(earth.position, camera.position).normalize();
const labelVector = new THREE.Vector3().copy(labelObj.position).applyEuler(earth.rotation);
const dotProduct = cameraVector.dot(labelVector);
if (dotProduct < 0) { // 標(biāo)簽在地球正面
    // 顯示標(biāo)簽
} else {
    // 隱藏標(biāo)簽
}

五、性能優(yōu)化

5.1 使用CSS Transform替代DOM屬性

在原實(shí)現(xiàn)中,我們使用lefttop屬性來(lái)定位標(biāo)簽,這會(huì)導(dǎo)致頻繁的DOM重排。優(yōu)化后使用transform屬性:

// 使用transform替代left/top,提高性能
labelObj.element.style.transform = `translate(${x - labelObj.element.offsetWidth / 2}px, ${y - labelObj.element.offsetHeight}px)`;

5.2 減少不必要的DOM操作

通過(guò)跟蹤標(biāo)簽的可見(jiàn)狀態(tài),避免重復(fù)設(shè)置相同的顯示狀態(tài):

if (!labelObj.visible) {
    labelObj.element.style.display = 'block';
    labelObj.visible = true;
}

六、完整代碼實(shí)現(xiàn)

以下是完整的交互式3D地球模型代碼:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D地球模型</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #000000;
            font-family: Arial, sans-serif;
        }
        #container {
            position: absolute;
            width: 100%;
            height: 100%;
        }
        #instructions {
            position: absolute;
            top: 20px;
            width: 100%;
            text-align: center;
            color: white;
            font-size: 14px;
            font-family: Arial, sans-serif;
            text-shadow: 2px 2px 4px #000000;
            pointer-events: none;
            z-index: 10;
        }
        .country-label {
            position: absolute;
            color: white;
            font-size: 12px;
            font-family: Arial, sans-serif;
            text-shadow: 1px 1px 2px black;
            pointer-events: none;
            white-space: nowrap;
            z-index: 100;
            transition: transform 0.1s ease;
        }
    </style>
</head>
<body>
    <div id="instructions">拖動(dòng)地球旋轉(zhuǎn) | Drag to rotate the Earth</div>
    <div id="container"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // 基本設(shè)置
        let scene, camera, renderer;
        let earth;
        let isDragging = false;
        let previousMousePosition = {
            x: 0,
            y: 0
        };
        let labelElements = [];
        // 國(guó)家數(shù)據(jù) - 經(jīng)緯度坐標(biāo)
        const countries = [
            { name: "中國(guó)", lat: 35.8617, lng: 104.1954 },
            { name: "美國(guó)", lat: 37.0902, lng: -95.7129 },
            { name: "俄羅斯", lat: 61.5240, lng: 105.3188 },
            { name: "加拿大", lat: 56.1304, lng: -106.3468 },
            { name: "巴西", lat: -14.2350, lng: -51.9253 },
            { name: "澳大利亞", lat: -25.2744, lng: 133.7751 },
            { name: "印度", lat: 20.5937, lng: 78.9629 },
            { name: "英國(guó)", lat: 55.3781, lng: -3.4360 },
            { name: "法國(guó)", lat: 46.2276, lng: 2.2137 },
            { name: "德國(guó)", lat: 51.1657, lng: 10.4515 },
            { name: "日本", lat: 36.2048, lng: 138.2529 },
            { name: "阿根廷", lat: -38.4161, lng: -63.6167 },
            { name: "埃及", lat: 26.0975, lng: 30.0444 },
            { name: "南非", lat: -30.5595, lng: 22.9375 },
            { name: "沙特", lat: 23.8859, lng: 45.0792 }
        ];
        // 將經(jīng)緯度轉(zhuǎn)換為3D坐標(biāo)
        function latLongToVector3(lat, lng, radius) {
            const phi = (90 - lat) * Math.PI / 180;
            const theta = (lng + 180) * Math.PI / 180;
            return new THREE.Vector3(
                -radius * Math.sin(phi) * Math.cos(theta),
                radius * Math.cos(phi),
                radius * Math.sin(phi) * Math.sin(theta)
            );
        }
        // 初始化場(chǎng)景
        function init() {
            // 創(chuàng)建場(chǎng)景
            scene = new THREE.Scene();
            // 創(chuàng)建相機(jī)
            camera = new THREE.PerspectiveCamera(
                75, 
                window.innerWidth / window.innerHeight, 
                0.1, 
                1000
            );
            camera.position.z = 2.5;
            // 創(chuàng)建渲染器
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.getElementById('container').appendChild(renderer.domElement);
            // 創(chuàng)建地球幾何體
            const geometry = new THREE.SphereGeometry(1, 64, 64);
            // 加載貼圖
            const textureLoader = new THREE.TextureLoader();
            const map = textureLoader.load('https://threejs.org/examples/textures/planets/earth_atmos_2048.jpg');
            const bumpMap = textureLoader.load('https://threejs.org/examples/textures/planets/earth_normal_2048.jpg');
            const specularMap = textureLoader.load('https://threejs.org/examples/textures/planets/earth_specular_2048.jpg');
            // 創(chuàng)建材質(zhì)
            const material = new THREE.MeshPhongMaterial({
                map: map,
                bumpMap: bumpMap,
                bumpScale: 0.05,
                specularMap: specularMap,
                specular: new THREE.Color(0x333333),
                shininess: 5
            });
            // 創(chuàng)建地球網(wǎng)格
            earth = new THREE.Mesh(geometry, material);
            earth.position.set(0, 0, 0);
            scene.add(earth);
            // 添加國(guó)家標(biāo)簽
            createCountryLabels();
            // 添加燈光
            const ambientLight = new THREE.AmbientLight(0x404040, 1);
            scene.add(ambientLight);
            const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
            directionalLight.position.set(2, 1, 3).normalize();
            scene.add(directionalLight);
            // 綁定事件監(jiān)聽(tīng)器
            bindEventListeners();
            // 開(kāi)始動(dòng)畫(huà)循環(huán)
            animate();
        }
        // 創(chuàng)建國(guó)家標(biāo)簽
        function createCountryLabels() {
            // 為每個(gè)國(guó)家創(chuàng)建一個(gè)標(biāo)簽元素
            countries.forEach(country => {
                const label = document.createElement('div');
                label.className = 'country-label';
                label.textContent = country.name;
                label.style.display = 'none';
                document.body.appendChild(label);
                // 計(jì)算3D位置
                const position = latLongToVector3(country.lat, country.lng, 1.02);
                labelElements.push({
                    element: label,
                    position: position,
                    country: country,
                    visible: false
                });
            });
        }
        // 更新標(biāo)簽位置
        function updateLabelPositions() {
            const vector = new THREE.Vector3();
            const canvas = renderer.domElement;
            labelElements.forEach(labelObj => {
                // 將3D位置轉(zhuǎn)換為屏幕坐標(biāo)
                vector.copy(labelObj.position);
                vector.applyEuler(earth.rotation);
                vector.project(camera);
                // 計(jì)算屏幕坐標(biāo)
                const x = Math.round((vector.x * 0.5 + 0.5) * canvas.clientWidth);
                const y = Math.round(( -vector.y * 0.5 + 0.5) * canvas.clientHeight);
                // 只有當(dāng)前面可見(jiàn)時(shí)才顯示標(biāo)簽
                const cameraVector = new THREE.Vector3().subVectors(earth.position, camera.position).normalize();
                const labelVector = new THREE.Vector3().copy(labelObj.position).applyEuler(earth.rotation);
                const dotProduct = cameraVector.dot(labelVector);
                if (dotProduct < 0) {
                    if (!labelObj.visible) {
                        labelObj.element.style.display = 'block';
                        labelObj.visible = true;
                    }
                    labelObj.element.style.transform = `translate(${x - labelObj.element.offsetWidth / 2}px, ${y - labelObj.element.offsetHeight}px)`;
                } else {
                    if (labelObj.visible) {
                        labelObj.element.style.display = 'none';
                        labelObj.visible = false;
                    }
                }
            });
        }
        // 綁定事件監(jiān)聽(tīng)器
        function bindEventListeners() {
            renderer.domElement.addEventListener('mousedown', onMouseDown, false);
            window.addEventListener('mousemove', onMouseMove, false);
            window.addEventListener('mouseup', onMouseUp, false);
            renderer.domElement.addEventListener('mouseleave', onMouseUp, false);
            window.addEventListener('resize', onWindowResize, false);
            renderer.domElement.addEventListener('contextmenu', function(e) {
                e.preventDefault();
            }, false);
        }
        // 鼠標(biāo)按下處理
        function onMouseDown(event) {
            isDragging = true;
            previousMousePosition = {
                x: event.clientX,
                y: event.clientY
            };
        }
        // 鼠標(biāo)移動(dòng)處理
        function onMouseMove(event) {
            if (isDragging) {
                const deltaX = event.clientX - previousMousePosition.x;
                const deltaY = event.clientY - previousMousePosition.y;
                // 更新地球旋轉(zhuǎn)
                earth.rotation.y += deltaX * 0.01;
                earth.rotation.x += deltaY * 0.01;
                previousMousePosition = {
                    x: event.clientX,
                    y: event.clientY
                };
            }
        }
        // 鼠標(biāo)抬起或離開(kāi)處理
        function onMouseUp() {
            isDragging = false;
        }
        // 窗口大小調(diào)整處理
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
            // 重新計(jì)算標(biāo)簽位置
            setTimeout(updateLabelPositions, 100);
        }
        // 動(dòng)畫(huà)循環(huán)
        function animate() {
            requestAnimationFrame(animate);
            // 非拖動(dòng)狀態(tài)下自動(dòng)旋轉(zhuǎn)
            if (!isDragging) {
                earth.rotation.y += 0.001;
            }
            // 更新標(biāo)簽位置
            updateLabelPositions();
            renderer.render(scene, camera);
        }
        // 啟動(dòng)應(yīng)用
        init();
    </script>
</body>
</html>

七、總結(jié)

本文詳細(xì)介紹了如何使用Three.js創(chuàng)建一個(gè)交互式的3D地球模型,并通過(guò)性能優(yōu)化解決了標(biāo)簽顯示不流暢的問(wèn)題。通過(guò)這個(gè)項(xiàng)目,我們可以學(xué)到:

  1. Three.js的基本場(chǎng)景構(gòu)建
  2. 3D幾何體的創(chuàng)建和材質(zhì)應(yīng)用
  3. 地理坐標(biāo)到3D坐標(biāo)的轉(zhuǎn)換
  4. DOM元素與3D場(chǎng)景的同步
  5. 性能優(yōu)化技巧

這個(gè)3D地球模型不僅具有教育意義,還可以作為數(shù)據(jù)可視化、地理信息系統(tǒng)等項(xiàng)目的基礎(chǔ)。通過(guò)進(jìn)一步擴(kuò)展,我們可以添加更多的地理信息、天氣數(shù)據(jù)或?qū)崟r(shí)信息,創(chuàng)建更加豐富的3D地球應(yīng)用。

到此這篇關(guān)于如何使用Three.js創(chuàng)建交互式3D地球模型的文章就介紹到這了,更多相關(guān)Three.js交互式3D地球模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • javascript 另一種圖片滾動(dòng)切換效果思路

    javascript 另一種圖片滾動(dòng)切換效果思路

    把圖片們用ul之類(lèi)的包起來(lái),并設(shè)置float。然后設(shè)置這個(gè)ul本身為absolute定位,其父標(biāo)簽用relative定位。通過(guò)設(shè)置ul的left或top值,實(shí)現(xiàn)圖片隊(duì)列的滾動(dòng)效果
    2012-04-04
  • JavaScript實(shí)現(xiàn)京東快遞單號(hào)查詢(xún)

    JavaScript實(shí)現(xiàn)京東快遞單號(hào)查詢(xún)

    這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)京東快遞單號(hào)查詢(xún),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • 下雪了 javascript實(shí)現(xiàn)雪花飛舞

    下雪了 javascript實(shí)現(xiàn)雪花飛舞

    下雪了,這篇文章主要介紹了javascript實(shí)現(xiàn)雪花飛舞,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • ES6新特性三: Generator(生成器)函數(shù)詳解

    ES6新特性三: Generator(生成器)函數(shù)詳解

    這篇文章主要介紹了ES6新特性之Generator(生成器)函數(shù),簡(jiǎn)單分析了Generator(生成器)函數(shù)的功能、定義、調(diào)用方法并結(jié)合實(shí)例形式給出了相關(guān)使用技巧,需要的朋友可以參考下
    2017-04-04
  • 如何HttpServletRequest文件對(duì)象并儲(chǔ)存

    如何HttpServletRequest文件對(duì)象并儲(chǔ)存

    這篇文章主要介紹了如何HttpServletRequest文件對(duì)象并儲(chǔ)存,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • uni-app頁(yè)面?zhèn)鲄⒖倎G值的3種解決方法

    uni-app頁(yè)面?zhèn)鲄⒖倎G值的3種解決方法

    文章介紹了三種在前端頁(yè)面間傳遞參數(shù)的方法:URL傳參、全局變量傳參和本地存儲(chǔ)傳參,每種方法都有其適用場(chǎng)景和注意事項(xiàng),需要的朋友可以參考下
    2025-12-12
  • ie支持function.bind()方法實(shí)現(xiàn)代碼

    ie支持function.bind()方法實(shí)現(xiàn)代碼

    在 google 一番技術(shù)資料后,發(fā)現(xiàn) firefox 原生支持一個(gè) bind 方法,該方法很好的滿(mǎn)足了我們的初衷,調(diào)用方法與 call 和 apply 一樣,只是定義完成后,在后期調(diào)用時(shí)該方法才會(huì)執(zhí)行,需要的朋友可以了解下
    2012-12-12
  • “不能執(zhí)行已釋放的Script代碼”錯(cuò)誤的原因及解決辦法

    “不能執(zhí)行已釋放的Script代碼”錯(cuò)誤的原因及解決辦法

    “不能執(zhí)行已釋放的Script代碼”錯(cuò)誤的原因及解決辦法...
    2007-09-09
  • JavaScript 格式化金額常見(jiàn)方法

    JavaScript 格式化金額常見(jiàn)方法

    這篇文章主要介紹了JavaScript 格式化金額最常見(jiàn)方法,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • JS簡(jiǎn)單實(shí)現(xiàn)城市二級(jí)聯(lián)動(dòng)選擇插件的方法

    JS簡(jiǎn)單實(shí)現(xiàn)城市二級(jí)聯(lián)動(dòng)選擇插件的方法

    這篇文章主要介紹了JS簡(jiǎn)單實(shí)現(xiàn)城市二級(jí)聯(lián)動(dòng)選擇插件的方法,涉及javascript實(shí)現(xiàn)select遍歷與設(shè)置技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下
    2015-08-08

最新評(píng)論

敦煌市| 中江县| 自贡市| 喀什市| 乌兰浩特市| 诸城市| 惠州市| 永兴县| 嘉鱼县| 赞皇县| 汝南县| 左贡县| 象州县| 景泰县| 邯郸市| 五台县| 图木舒克市| 昆明市| 濮阳县| 普宁市| 盐亭县| 铜陵市| 兰坪| 永寿县| 松潘县| 邵武市| 鄂温| 奇台县| 堆龙德庆县| 犍为县| 明水县| 巴青县| 图们市| 深州市| 青海省| 乐平市| 墨竹工卡县| 郓城县| 姜堰市| 日土县| 平泉县|