threejs(一):初步认识与使用
应项目需求,学习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} );