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

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

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

前言

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

一、Three.js簡介

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

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

2.1 初始化場景

首先,我們需要創(chuàng)建一個基本的Three.js場景:

// 創(chuà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)建地球幾何體

接下來,我們創(chuàng)建一個球體作為地球的基礎(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)移動處理
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)簽

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)簽可見性判斷

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

// 只有當(dāng)前面可見時才顯示標(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屬性來定位標(biāo)簽,這會導(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操作

通過跟蹤標(biāo)簽的可見狀態(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">拖動地球旋轉(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 = [];
        // 國家數(shù)據(jù) - 經(jīng)緯度坐標(biāo)
        const countries = [
            { name: "中國", lat: 35.8617, lng: 104.1954 },
            { name: "美國", 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: "英國", lat: 55.3781, lng: -3.4360 },
            { name: "法國", lat: 46.2276, lng: 2.2137 },
            { name: "德國", 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)
            );
        }
        // 初始化場景
        function init() {
            // 創(chuà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);
            // 添加國家標(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)聽器
            bindEventListeners();
            // 開始動畫循環(huán)
            animate();
        }
        // 創(chuàng)建國家標(biāo)簽
        function createCountryLabels() {
            // 為每個國家創(chuàng)建一個標(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)前面可見時才顯示標(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)聽器
        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)移動處理
        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)抬起或離開處理
        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);
        }
        // 動畫循環(huán)
        function animate() {
            requestAnimationFrame(animate);
            // 非拖動狀態(tài)下自動旋轉(zhuǎn)
            if (!isDragging) {
                earth.rotation.y += 0.001;
            }
            // 更新標(biāo)簽位置
            updateLabelPositions();
            renderer.render(scene, camera);
        }
        // 啟動應(yīng)用
        init();
    </script>
</body>
</html>

七、總結(jié)

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

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

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

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

相關(guān)文章

  • javascript 另一種圖片滾動切換效果思路

    javascript 另一種圖片滾動切換效果思路

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

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

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

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

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

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

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

    如何HttpServletRequest文件對象并儲存

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

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

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

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

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

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

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

    JavaScript 格式化金額常見方法

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

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

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

最新評論

东城区| 万安县| 邹城市| 德令哈市| 亳州市| 嘉黎县| 监利县| 金湖县| 绥江县| 察隅县| 武胜县| 科尔| 沈阳市| 马关县| 色达县| 蚌埠市| 新巴尔虎左旗| 原平市| 鄂托克旗| 贵德县| 自贡市| 外汇| 万年县| 信丰县| 哈密市| 土默特左旗| 吉木萨尔县| 丹棱县| 汨罗市| 旅游| 塘沽区| 双城市| 米泉市| 聊城市| 郁南县| 斗六市| 宣威市| 潮州市| 洛阳市| 镇江市| 郑州市|