應項目需求,學習threejs將近半個月,現在總結一下我從網上搜集的比較有份量的資料和在項目中踩到的大小坑,以下內容如果有誤,感謝各位大神不吝賜教。
一、threejs學習的資料
二、項目中大小坑總結
1.實現效果:
2.疑難點
- 如何實現加載obj和mtl文件
//導入obj外部模型 function setObj() { //加載mtl for (let i =0;i<objUrl.length;i++){ //加載顯示進度 var onProgress = function ( xhr ) { if ( xhr.lengthComputable ) { var percentComplete = xhr.loaded/xhr.total * 100; console.log( Math.round( percentComplete, 2 ) + '% downloaded' ); } }; //加載出錯的時候被調用 var onError = function () { }; new THREE.MTLLoader() .setPath( 'source/') .load( mtlUrl[i]+'.mtl', function ( materials ) { materials.preload(); new THREE.OBJLoader() .setMaterials( materials ) .setPath( 'source/' ) .load( objUrl[i]+'.obj', function ( object ) { //設置模型的位置 object.position.x = 2+i*20; object.position.y = 2; object.position.z = 2; object.castShadow = true; object.receiveShadow = true; //將模型加入到場景中去 scene.add(object); // objects.push(object); }, onProgress, onError ); } ); } }
- 導入3d模型后怎么實現拖拽
拖拽的調用函數是THREE.DragControls,但是由於我們導入的是外部模型,就是說它的type是group,而dragControls認准的類型是拖拽類型是mesh,如果直接寫dragcontrols的模板,你會發現根本無法實現拖拽。
//對模型實現拖拽 function drag(objects) { //初始化拖拽控件 var dragControls = new THREE.DragControls( objects, camera, renderer.domElement ); dragControls.addEventListener('hoveron',function (event) { // 讓變換控件對象和選中的對象綁定 transform.attach(event.object); }); // 開始拖拽 dragControls.addEventListener( 'dragstart', function () { controls.enabled = false; } ); // 拖拽結束 dragControls.addEventListener( 'dragend', function () { controls.enabled = true; } ); }
這個時候你要查看一下threejs實現拖拽的原理,你要使用THREE.Raycaster,從相機上射出一條射線,捕獲鼠標的二維坐標,將鼠標的二維坐標轉換成世界坐標,通過鼠標點擊的位置(二維坐標)和當前相機的矩陣計算出射線位置,接着獲取與射線相交的對象數組,其中的元素按照距離排序,越近的越靠前,允許檢驗后代,最后才去調用dragControls。
注意:如果你想在移動端的網頁也實現拖拽,在這里要注意判斷當前網頁是在移動端還是pc端,即此時獲取的是鼠標的點擊位置還是觸屏的點擊位置:
// 判斷是在移動端還是在pc端 var os = function (){ var ua = navigator.userAgent, isWindowsPhone = /(?:Windows Phone)/.test(ua), isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone, isAndroid = /(?:Android)/.test(ua), isFireFox = /(?:Firefox)/.test(ua), isChrome = /(?:Chrome|CriOS)/.test(ua), isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)), isPhone = /(?:iPhone)/.test(ua) && !isTablet, isPc = !isPhone && !isAndroid && !isSymbian; return { isTablet: isTablet, isPhone: isPhone, isAndroid: isAndroid, isPc: isPc }; }(); ............ var mouse = new THREE.Vector2(); if (os.isAndroid || os.isPhone) { //移動端點擊位置 var touch = event.touches[0]; mouse.x = (touch.pageX / window.innerWidth) * 2 - 1; mouse.y = -(touch.pageY / window.innerHeight) * 2 + 1; } else if (os.isTablet) { //移動端點擊位置 var touch = event.touches[0]; mouse.x = (touch.pageX / window.innerWidth) * 2 - 1; mouse.y = -(touch.pageY / window.innerHeight) * 2 + 1; } else if(os.isPc) { // 通過鼠標點擊位置,計算出 raycaster 所需點的位置,以屏幕為中心點,范圍 -1 到 1 mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; }
- 如何改變坐標的顯示
需求要求用戶來控制坐標顯示是rotate還是translate,此時我們使用THREE中的gui來顯示。即:
init() { //設置gui選項 var gui_tag = new function () { this.translate = true; } var gui = new dat.GUI(); gui.add(gui_tag,'translate'); ...... } // 時刻渲染 function animate() { ...... if (gui_tag.translate === true){ transform.setMode("translate"); } else { transform.setMode("rotate"); } }
- 如何控制相機的旋轉和縮放
相機的旋轉使用orbitControl來控制,但是值得注意的是當你加上相機的旋轉的時候,拖拽模型的時候會發現相機也會一起移動,這個時候拖拽模型就會顯得不那么順心了,此時我們要加上:
transform.addEventListener( 'dragging-changed', function ( event ) { orbit.enabled = ! event.value; } );
完整的代碼是:
function init() { // 添加相機的旋轉 orbit = new THREE.OrbitControls(camera,renderer.domElement); orbit.enableDamping = true; orbit.update(); transform.addEventListener( 'dragging-changed', function ( event ) { orbit.enabled = ! event.value; } ); orbit.addEventListener( 'change', render ); } ... function animate() { .... orbit.update(); }
- threejs顯示的網頁如何嵌入到微信小程序上
微信小程序上有固定的標簽格式,此時我們可以使用webview來跳轉到指定的網頁去瀏覽threejs模型。
3.遺留問題
對於坐標的類型顯示方面在pc端網頁顯示可以正常轉換,但轉到移動端之后就會失靈。
4.注意容易犯的低級錯誤
- 文件中的index.html不可以直接雙擊打開,會報錯如下:
解決方法:可以使用webstorm來打開,此時是http:localhost請求。
- 在導入的文件中會出現監聽touchstart這些觸摸,此時會報錯如下:
此時在后面加上 {passive:false}即可。
例如:
scope.domElement.addEventListener( 'touchstart', onTouchStart, {passive: false} );