Three.js 快速上手以及在 React 中運用

github 的地址 歡迎 star!
javascript

以前項目中用到了 3D 模型演示的問題,整理了一下以前學習總結以及遇到的坑。3D 框架有老牌引擎 Three.js 和微軟的 Babylon.js php

image
對比一下仍是使用更爲廣泛的 Three.js

Three.js基礎概念

主要來自於《Three.js 開發指南》也能夠參考在線網站 threejs 教程 css

3個基礎概念:場景(scene)、相機(camera)和渲染器(renderer)。

  • Sence 場景:場景是一個載體,容器,全部的一切都運行在這個容器裏面(存放着全部渲染的物體和使用的光源)html

  • 相機 camera 的做用是定義可視域,至關於咱們的雙眼,生產一個個快照,最爲經常使用的是 PerspectiveCamera 透視攝像機,其餘還有 ArrayCamera 陣列攝像機(包含多個子攝像機,經過這一組子攝像機渲染出實際效果,適用於 VR 場景),CubeCamera 立方攝像機(建立六個 PerspectiveCamera(透視攝像機),適用於鏡面場景),StereoCamera 立體相機(雙透視攝像機適用於 3D 影片、視差效果)。相機主要分爲兩類正投影相機和透視相機,正投影相機的話, 全部方塊渲染出來的尺寸都同樣; 對象和相機之間的距離不會影響渲染結果,而透視相機接近真實世界,看物體會產生遠近高低各不一樣前端

  • PerspectiveCamera 透視攝像機--模擬人眼的視覺,根據物體距離攝像機的距離,近大遠小html5

image

  • 渲染器 renderer 則負責用如何渲染出圖像,是使用 WegGL 仍是 Canvas,相似於 react 中 render,產生實際的頁面效果

其餘一些概念

  • Mesh 網格:有了場景和攝像頭就能夠看到 3D 場景中的物體,場景中的咱們最爲經常使用的物體稱爲網格。網格由兩部分組成:幾何體和材質
  • 材料(Materials),紋理( Textures):物體的表面屬性能夠是單純的顏色,也能夠是很複雜的狀況,好比反射/透射/折射的狀況,還能夠有紋理圖案。好比包裝盒外面的貼圖。
  • Geometry 幾何形狀:threejs 使用 Geometry 定義物體的幾何形狀,其實 Geometry 的核心就是點集,之因此有這麼多的 Geometry,是爲了更方便的建立各類形狀的點集
  • 光照(Lights):組成部分。 若是 沒有 光源, 咱們 就不 可能 看到 任何 渲染 結果,具體介紹能夠查看光照效果和Phong光照模型。一些經常使用的光源:
    1. AmbientLight 環境光源,屬於基礎光源,爲場景中的全部物體提供一個基礎亮度。
    2. DirectionalLight 平行光源:相似太陽光,發出的光源都是平行的
    3. HemisphereLight 半球光源:只有圓球的半邊會發出光源。
    4. PointLight 點光源:一個點向四周發出光源,通常用於燈泡。
    5. SpotLight 聚光燈光源:一個圓錐體的燈光
  • 注意:並非每一種光源都能產生陰影(Shadow): DirectionalLight, PointLight, SpotLight 三種能產生陰影,另外如要開啓模型的陰影的話,模型是由多個 Mesh 組成的,只開啓父的 Mesh 的陰影是不行的,還須要遍歷父 Mesh 下全部的子 Mesh 爲其開啓投射陰影 castShadow 和接收投射陰影 receiveShadow。
  • 加載器(Loaders):用來解析的導入的模型文件,常見的有 OBJLoader(加載 .obj 文件)、JSONLoader,MTLLoader
// 場景是全部物體的容器
var scene = new THREE.Scene();
// 相機,相機決定了場景中那個角度的景色會顯示出來。相機就像人的眼睛同樣,人站在不一樣位置,擡頭或者低頭都可以看到不一樣的景色。
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
// 渲染器renderer的domElement元素,表示渲染器中的畫布,全部的渲染都是畫在domElement上的
var renderer = new THREE.WebGLRenderer();	// 渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 設置渲染器的大小爲窗口的內寬度,也就是內容區的寬度
document.body.appendChild(renderer.domElement);


// 渲染循環
function animate() {
render();
// 調用 requestAnimationFrame 函數,傳遞一個 callback 參數,則在下一個動畫幀時,會調用 callback 這個函數。
requestAnimationFrame( animate );
}


動畫方案:
一:改變camera
function animation()
            {
                //renderer.clear();
                camera.position.x =camera.position.x +1;
                renderer.render(scene, camera);
                requestAnimationFrame(animation);
            }
            
// camera.position.x =camera.position.x +1;
// 將相機不斷的沿着x軸移動1個單位,也就是相機向右移動,那麼相機中物體是向左移動的。
// 調用requestAnimationFrame(animation)函數,這個函數又會在下一個動畫幀出發animation()函數,這樣就不斷改變了相機的位置,從而物體看上去在移動了。
// 另外,必需要重視render函數,這個函數是從新繪製渲染結果,若是不調用這個函數,那麼即便相機的位置變化了,可是沒有從新繪製,仍然顯示的是上一幀的動畫  renderer.render(scene, camera);

二:改變物體自身位置--mesh
mesh就是指的物體,它有一個位置屬性position,這個position是一個THREE.Vector3類型變量,因此你要把它向左移動,只須要將x的值不斷的減小就能夠了。這裏咱們減去的是1個單位。

// [渲染真實性---光源運用](http://www.hewebgl.com/article/getarticle/60)

THREE.Light ( hex )

它有一個參數hex,接受一個16進制的顏色值。例如要定義一種紅色的光源,咱們能夠這樣來定義:

Var redLight = new THREE.Light(0xFF0000);

// [文理--3D物體的皮膚:](http://www.hewebgl.com/article/getarticle/68)
紋理類由THREE.Texture表示,其構造函數以下所示:

THREE.Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy )
複製代碼

一下就是 Three.js 的基本概念 java

image

而後給出一個簡單的例子react

// 引入 Three.js 庫
<script src="https://unpkg.com/three"></script>
function init () {
    // 獲取瀏覽器窗口的寬高,後續會用
    var width = window.innerWidth
    var height = window.innerHeight
    // 建立一個場景
    var scene = new THREE.Scene()
    // 建立一個具備透視效果的攝像機
    var camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 800)
    // 設置攝像機位置,並將其朝向場景中心
    camera.position.x = 10
    camera.position.y = 10
    camera.position.z = 30
    camera.lookAt(scene.position)
    // 建立一個 WebGL 渲染器,Three.js 還提供 <canvas>, <svg>, CSS3D 渲染器。
    var renderer = new THREE.WebGLRenderer()
    // 設置渲染器的清除顏色(即背景色)和尺寸。
    // 若想用 body 做爲背景,則能夠不設置 clearColor,而後在建立渲染器時設置 alpha: true,即 new THREE.WebGLRenderer({ alpha: true })
    renderer.setClearColor(0xffffff)
    renderer.setSize(width, height)
    // 建立一個長寬高均爲 4 個單位長度的立方體(幾何體)
    var cubeGeometry = new THREE.BoxGeometry(4, 4, 4)
    // 建立材質(該材質不受光源影響)
    var cubeMaterial = new THREE.MeshBasicMaterial({
        color: 0xff0000
    })
    // 建立一個立方體網格(mesh):將材質包裹在幾何體上
    var cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
    // 設置網格的位置
    cube.position.x = 0
    cube.position.y = -2
    cube.position.z = 0
    // 將立方體網格加入到場景中
    scene.add(cube)
    // 將渲染器的輸出(此處是 canvas 元素)插入到 body 中
    document.body.appendChild(renderer.domElement)
    // 渲染,即攝像機拍下此刻的場景
    renderer.render(scene, camera)
}
init()
複製代碼

在線的例子點擊webpack

實際運用

three 性能監視器 stats

主要是用來顯示性能幀數的git

  • FPS:最後一秒的幀數,越大越流暢

  • MS:渲染一幀須要的時間(毫秒),越低越好

  • MB:佔用的內存信息

  • CUSTOM:自定義面板

    image

var stats = new Stats()
stats.showPanel(1)
document.body.appendChild(stats.dom)
function animate() {
  requestAnimationFrame(animate)
}
requestAnimationFrame(animate)
複製代碼

具體一些不用導入的例子

能夠在 github.com/mrdoob/thre… 下載文件,查看\three.js-master\examples中例子熟悉相應的代碼

導入 3D 模型例子

導入模型類型介紹

導入模型文件須要用到相應的 loader,經常使用 3d 軟件導出的格式,項目中主要是用了 OBJ 和 MTL 類型,OBJ 定義了幾何體,MTL 定義了材質

//當mtl中引用了dds類型的圖片時,還需導入DDSLoader文件。
//這裏的src路徑視實際開發而定
<script src="js/loaders/DDSLoader.js"></script>
<script src="js/loaders/MTLLoader.js"></script>
<script src="js/loaders/OBJLoader.js"></script>
 
 
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
 
var mtlLoader = new THREE.MTLLoader();
//設置路徑,也可不是設置,在load中加載完整路徑也可
    mtlLoader.setPath( 'obj/male02/' );
    mtlLoader.load( 'male02_dds.mtl', 
 // 資源加載成功後執行的函數 
 //@params materials THREE.MTLLoader.MaterialCreator
function( materials ) {
    materials.preload();
    var objLoader = new THREE.OBJLoader();
        objLoader.setMaterials( materials );
        objLoader.setPath( 'obj/male02/' );
        objLoader.load( 'male02.obj', function ( object ) {
    		object.position.y = - 95;
    		scene.add( object );
    	});
});
複製代碼

具體例子能夠查看

導入 OBJ 存在問題,模型導出爲 obj 格式後,文件太大(放在服務器就會產生嚴重的性能問題),並且還須要額外對 MTL 導入,否則只會顯示幾何模型
glTF 模型格式

.obj 是靜態模型,不支持動畫數據存儲,沒法使用模型的動畫,並且體積大, glTF 是由 Khronos Group 開發的 3D 模型文件格式,該格式的特色是最大程度的減小了 3D 模型文件的大小,提升了傳輸、加載以及解析 3D 模型文件的效率,而且它可擴展,可互操做。

.gltf 包含場景中節點層次結構、攝像機、網格、材質以及動畫等描述信息

Three.js 中使用 glTF 格式需額外引入 GLTFLoader.js 加載器。

var gltfLoader = new THREE.gltfLoader()
gltfLoader.load('./assets/box.gltf', function(sence) {
  var object = scene.gltf // 模型對象
  scene.add(object) // 將模型添加到場景中
})
複製代碼

glTF 模型中可使用 Blender 建模軟件製做動畫,導出後使用 GLTFLoader 加載到 Three.js 中,能夠拿到一個 animations 數組,animations 裏包含了模型的每一個動畫 Action 動做。

爲了獲取更好的網絡性能,還可使用 Draco工具進行壓縮,只有在模型文件不少時,才推薦壓縮(由於壓縮後格式改變,須要引入其餘的解析工具)

動畫

上面說到了動畫,關於動畫,能夠直接三方庫 Tween 動畫,在許同事提供的研究裏面有相關的運用。通常在 Three.js 動畫是使用 requestAnimationFrame(),當你須要更新屏幕畫面時就能夠調用此方法。在瀏覽器下次重繪前執行回調函數。回調的次數一般是每秒60次。

對模型實現淡入淡出、縮放、位移、旋轉等動畫推薦使用 GSAP 來實現更爲簡便。

let tween = new TimelineMax()
tween
  .to(box.scale, 1, { // 從 1 縮放至 2,花費 1 秒
    x: 2,
    y: 2,
    z: 2,
    ease: Power0.easeInOut, // 速度曲線
    onStart: function() {
      // 監聽動畫開始
    },
    onUpdate: function() {
      // 監聽動畫過程
    },
    onComplete: function() {
      // 監聽動畫結束
    }
  })
  .to(box.position, 1, { // 縮放結束後,位移 x 至 10,花費 1 秒
    x: 10,
    y: 0,
    z: 0
  })
複製代碼

控制 orbitcontrols

場景控制器,OrbitControls 是用於調試 Camera 的方法,實例化後能夠經過鼠標拖拽來旋轉 Camera 鏡頭的角度,鼠標滾輪能夠控制 Camera 鏡頭的遠近距離,旋轉和遠近都會基於場景的中心點,在調試預覽則會輕鬆許多。

// 引入文件
<script src="js/OrbitControls.js"></script>
    
 //場景控制器初始化
    function initControls() {
      controls = new THREE.OrbitControls(camera, renderer.domElement);
      controls.enabled = true; // 鼠標控制是否可用

      // 是否自動旋轉
      controls.autoRotate = true;
      controls.autoRotateSpeed = 0.05;

      //是否可旋轉,旋轉速度(鼠標左鍵)
      controls.enableRotate = true;
      controls.rotateSpeed = 0.3;

      //controls.target = new THREE.Vector();//攝像機聚焦到某一個點
      //最大最小相機移動距離(景深相機)
      controls.minDistance = 10;
      controls.maxDistance = 40;

      //最大仰視角和俯視角
      controls.minPolarAngle = Math.PI / 4; // 45度視角
      controls.maxPolarAngle = Math.PI / 2.4; // 75度視角

      //慣性滑動,滑動大小默認0.25
      controls.enableDamping = true;
      controls.dampingFactor = 0.25;

      //是否可平移,默認移動速度爲7px
      controls.enablePan = true;
      controls.panSpeed = 0.5;
      //controls.screenSpacePanning	= true;

      //滾輪縮放控制
      controls.enableZoom = true;
      controls.zoomSpeed = 1.5;

      //水平方向視角限制
      //controls.minAzimuthAngle = -Math.PI/4;
      //controls.maxAzimuthAngle = Math.PI/4;
    }
複製代碼

點擊交互

在3D模型中,鼠標點擊是重要的交互。對於 Three.js,它沒有相似 DOM 的層級關係,而且處於三維環境中,那麼咱們則須要經過如下方式來判斷某對象是否被選中。

function onDocumentMouseDown(event) {
    // 點擊位置建立一個 THREE.Vector3 向量
    var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);
    // vector.unproject 方法將屏幕上的點擊位置轉換成 Three.js 場景中的座標
    vector = vector.unproject(camera);
    // 使用 THREE.Raycaster 能夠向場景中發射光線
    var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
    // 使用 raycaster.intersectObjects 方法來判斷指定的對象中哪些被該光線照射到的,
    // 從而顯示不一樣的顏色
    var intersects = raycaster.intersectObjects([sphere, cylinder, cube]);
    if (intersects.length > 0) {
        console.log(intersects[0]);
        // 點擊後改變透明度
        intersects[0].object.material.transparent = true;
        intersects[0].object.material.opacity = 0.1;
        
        <!--...... 在這裏能夠實現你所須要的交互-->
    }
}
複製代碼

react中實踐運用

// 引入相關的依賴
npm i -S three


<!--GisThree.js-->
<!--固然 這個代碼還有很大的優化空間啊!-->

import React, { Component, Fragment } from 'react';
import './GisThree.less';
import OBJLoader from './threejsLibs/OBJLoader';
import Orbitcontrols from './threejsLibs/OrbitControls';
import MTLLoader from './threejsLibs/MTLLoader_module';
import { Icon } from 'antd';

import exhibitObj from './modal/exhibit2.obj';
import exhibitMtl from './modal/exhibit2.mtl';

let THREE = require('three');
Orbitcontrols(THREE);
OBJLoader(THREE);
MTLLoader(THREE);

// 排除這些名字的3D模型
const objectArrName = [ "房屋1101", "房屋1150", "房屋600", "房屋70", "房屋45", "房屋362", "房屋363", "房屋364", "房屋500" ];

class GisThree extends Component {

  constructor( props ) {
    super(props);
    this.state = {
      isModel: false,
      currentName: '暫無名字',
      clientX: 0,
      clientY: 0
    };
    this.threeRef = React.createRef();
  }

  componentDidMount() {
    const width = window.innerWidth;
    const height = window.innerHeight;
    // todo 初始化場景
    const scene = new THREE.Scene();
    // todo 加載相機
    const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 80);
    camera.position.set(0, 25, 25);
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    //todo 加載光線
    const ambLight = new THREE.AmbientLight(0x404040, 0.5);
    const pointLight = new THREE.PointLight(0x404040, 0.8);
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
    pointLight.position.set(100, 10, 0);
    pointLight.receiveShadow = true;
    scene.add(ambLight);
    scene.add(pointLight);
    scene.add(directionalLight);

    //todo  renderer
    const renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    renderer.setSize(width, height - 10);
    //renderer.setClearColor(0xb9d3ff,1);
    renderer.setClearColor(0x000000, 1.0);

    //todo  加載模型model
    let mtlLoader = new THREE.MTLLoader();
    mtlLoader.load(exhibitMtl,
      function ( materials ) {
        console.log('sdj exhibit.obj', materials)
        materials.preload();
        let objLoader = new THREE.OBJLoader();
        objLoader.setMaterials(materials);
        objLoader.load(exhibitObj, function ( object ) {
          console.log('sdj exhibit.obj')
          console.log('sdj exhibit.obj object', object);

          for ( let i = 0; i < object.children.length; i++ ) {
            let material = object.children[ i ].material;
            let meshObj = new THREE.Mesh(object.children[ i ].geometry, material);
            meshObj.receiveShadow = true;
            meshObj.castShadow = true;
            meshObj.scale.set(0.02, 0.02, 0.02);
            meshObj.name = "房屋" + i;
            meshObj.position.x = 0;
            meshObj.position.y = 0;
            meshObj.position.z = -20;

            scene.add(meshObj);
          }
        });
      }
    );

    // todo 場景控制器初始化
    const controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.enabled = true; // 鼠標控制是否可用

    // 是否自動旋轉
    controls.autoRotate = true;
    controls.autoRotateSpeed = 0.05;

    //是否可旋轉,旋轉速度(鼠標左鍵)
    controls.enableRotate = true;
    controls.rotateSpeed = 0.3;

    //controls.target = new THREE.Vector();//攝像機聚焦到某一個點
    //最大最小相機移動距離(景深相機)
    controls.minDistance = 10;
    controls.maxDistance = 40;

    //最大仰視角和俯視角
    controls.minPolarAngle = Math.PI / 4; // 45度視角
    controls.maxPolarAngle = Math.PI / 2.4; // 75度視角

    //慣性滑動,滑動大小默認0.25
    controls.enableDamping = true;
    controls.dampingFactor = 0.25;

    //是否可平移,默認移動速度爲7px
    controls.enablePan = true;
    controls.panSpeed = 0.5;
    //controls.screenSpacePanning	= true;

    //滾輪縮放控制
    controls.enableZoom = true;
    controls.zoomSpeed = 1.5;

    //水平方向視角限制
    //controls.minAzimuthAngle = -Math.PI/4;
    //controls.maxAzimuthAngle = Math.PI/4;

    //todo 綁定到類上
    this.scene = scene;
    this.camera = camera;
    this.renderer = renderer;
    this.controls = controls;
    //鼠標移入和移出事件高亮顯示選中的模型
    this.currentObjectColor = null; //移入模型的顏色
    this.currentObject = null; //鼠標移入的模型

    // 初始化場景
    // 加載到dom元素上
    this.threeRef.current.appendChild(this.renderer.domElement)

    this.start();

    window.addEventListener('resize',this.resizeFunc1 ,false);
    window.addEventListener('resize',this.resizeFunc2 ,false);
  }

  componentWillUnmount() {
    this.stop();
    this.threeRef.current.removeChild(this.renderer.domElement);
    window.removeEventListener('resize',this.resizeFunc1 ,false);
    window.removeEventListener('resize',this.resizeFunc2 ,false);
  }

  // 初始化
  start = () => {
    if(!this.frameId){
      this.frameId = requestAnimationFrame(this.animate)
    }
  }

  // 卸載組件的時候去除
  stop = () => {
    cancelAnimationFrame(this.frameId);
  }

  // 更新狀態
  animate = () => {
    this.controls.update();
    this.renderScene();
    this.frameId = requestAnimationFrame(this.animate);
  }

  renderScene = () => {
    this.renderer.render(this.scene, this.camera);
  }

  // 是否展現彈窗
  changeModel = ( e ) => {
    e.stopPropagation();
    this.setState({
      isModel: !this.state.isModel
    })
  }

  closeModel = ( e ) => {
    e.stopPropagation();
    if (this.controls && !this.controls.autoRotate){
      this.controls.autoRotate = true;
    }
    this.setState({
      isModel: false
    })
  }

  // 點擊3D模型匹配
  mouseClick = (e) => {
    // 鼠標座標映射到三維座標
    e.preventDefault();
    const that = this;
    const mouse = new THREE.Vector2();
    mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    if(!this.camera || !this.scene) return;
    let vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(this.camera);
    let raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize());
    let intersects = raycaster.intersectObjects(this.scene.children, true); //選中的三維模型
    console.log('sdj position',intersects)
    if (intersects.length > 0) {
      let SELECTED = intersects[0];
      let currentName = SELECTED.object.name;
      console.log('sdj position', e.clientX, e.clientY, e.screenX, e.screenY);
      if (objectArrName.indexOf(currentName) == -1) {
        if (this.controls.autoRotate){
          this.controls.autoRotate = false;
        }
        that.changeModel(e);
        that.setState({
          currentName,
          clientX: e.clientX,
          clientY: (e.clientY - 60)
        })
        console.log("你選中的物體的名字是:" + currentName);
      }
    }
  }

  // 鼠標聚焦
  mouseenterObject = (e) => {
    // 鼠標座標映射到三維座標
    e.preventDefault();

    let mouse = new THREE.Vector2();
    mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    let vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(this.camera);
    let raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize());
    let intersects = raycaster.intersectObjects(this.scene.children, true); //選中的三維模型
    if (!intersects.length && this.currentObjectColor && this.currentObject) { //從模型處移到外面
      this.currentObject.object.material.color.setHex(this.currentObjectColor);
      this.currentObjectColor = null;
      this.currentObject = null;
    }
    if (intersects.length > 0) {
      let SELECTED = intersects[0];
      let currentName = SELECTED.object.name;
      if (objectArrName.indexOf(currentName) == -1) {
        if (this.currentObject && currentName === this.currentObject.object.name) {
          return;
        }
        if (this.currentObjectColor && this.currentObject && currentName !== this.currentObject.object.name) { //color值是一個對象
          this.currentObject.object.material.color.setHex(this.currentObjectColor);
        }
        this.currentObject = SELECTED;
        this.currentObjectColor = SELECTED.object.material.color.getHex();
        SELECTED.object.material.color.set(0x74bec1);
      } else {
        if (this.currentObjectColor && this.currentObject && currentName !== this.currentObject.object.name) { //color值是一個對象
          this.currentObject.object.material.color.setHex(this.currentObjectColor);
        }
        this.currentObjectColor = null;
        this.currentObject = null;
      }
    }
  }

  resizeFunc1 = () => {
    this.controls.update();
  }

  resizeFunc2 = (e) =>  {
    this.camera.aspect = window.innerWidth / window.innerHeight;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(window.innerWidth, window.innerHeight);
  }

  render() {
    return (
      <Fragment>
        <div
          className={ this.props.className || 'three-component' }
          id="d3"
          ref={ this.threeRef }
          onClick={this.mouseClick}
          onMouseMove={this.mouseenterObject}
        />
        {
          this.state.isModel && (
            <div
              className="three-modal"
              style={ {
                top: this.state.clientY,
                left: this.state.clientX
              } }
            >
              <Icon
                className="three-modal-close"
                type="close" theme="outlined"
                onClick={ this.closeModel }
              />
              <ul>
                <li>
                  <span className="modal-title">出租屋編碼</span>
                  <span className="modal-data">{ this.state.currentName }</span>
                </li>
                <li>
                  <span className="modal-title">地址</span>
                  <span className="modal-data">社區一號</span>
                </li>
                <li>
                  <span className="modal-title">每層樓棟數</span>
                  <span className="modal-data">6</span>
                </li>
                <li>
                  <span className="modal-title">層數</span>
                  <span className="modal-data">16</span>
                </li>
              </ul>
            </div>
          )
        }
      </Fragment>
    )
  }
}

export default GisThree;


複製代碼

在服務器出現的錯誤,而本地服務器沒有問題 參考 stackoverflow.com/questions/4…

objLoader.js:624 Uncaught Error: THREE.OBJLoader: Unexpected line: "<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><title>智慧社區_管理後臺</title><link href="/static/css/main.bdb0e864.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="/config.js"></script><script type="text/javascript" src="/static/js/charts.24f90613.js"></script><script type="text/javascript" src="/static/js/vendor.0b9068d0.js"></script><script type="text/javascript" src="/static/js/main.cfa93993.js"></script></body></html>"
    at OBJLoader.parse (objLoader.js:624)
    at objLoader.js:385
    at XMLHttpRequest.<anonymous> (three1.js:630)




objLoader.js:624 Uncaught Error: THREE.OBJLoader: Unexpected line: "<!doctype html>"
    at OBJLoader.parse (objLoader.js:624)
    at objLoader.js:385
    at XMLHttpRequest.<anonymous> (three1.js:630)
複製代碼

最後發現棄用 mtl-loader 以後(且升級到 webpack4 )正確顯示了材質,以及出現了 git 忽略了 .obj 問題,看博客,全局的 gitignore_global.txt 中忽略了 .obj 問題,好坑!!!

若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝!

參考:

  1. 入門--Three.js 現學現賣
  2. threejs 官方文檔
  3. Three.js 中文教程
  4. 1.orbit controls 插件 ---2.詳解
  5. 3D 模型導入
  6. react three.js
  7. 首個threejs項目-前端填坑指南 主要介紹了 C4D 轉 json 以及一些動畫模型注意點
  8. 【Three.js】OrbitControl 旋轉插件
  9. 讀取 blender 模型並導入動畫
  10. 十分鐘打造 3D 物理世界
相關文章
相關標籤/搜索