three.js实现炫酷的全景3D重力感应


Posted in Javascript onDecember 30, 2018

本文实例为大家分享了three.js 全景重力感应的具体代码,供大家参考,具体内容如下

实现three.js 全景图 demo

使用three.js 写了球体和圆柱体版本的3D重力感应全景图,支持手指触摸和陀螺仪感应,也支持PC端的鼠标。给大家介绍一下基于移动端H5球体的实现方法,圆柱体类似。

设置容器和展示的样式
设置容器的宽高为全屏展示,清除body的margin,引用three.min.js(3D渲染框架) 和orienter.js (陀螺仪经纬度计算)

three.js实现炫酷的全景3D重力感应

<div id="CanvasBody"></div>

<script src="js/three.min.js"></script>
<!--重力感应-->
<script src="js/orienter.js"></script>
<!--动画效果-->
<script src="js/tween.js"></script>
<!-- 代码 -->
body {margin: 0;}
html, body, #CanvasBody {width: 100vw;height: 100vh;overflow: hidden;}
#CanvasBody {position: relative;}

设置html的data-dpr 属性,设置html 的fontSize

设置html的fontSize,重新计算body的实际可展示尺寸,这样可以使渲染出来的画面更清晰,分辨率最完美。

(function(_window) {
 var navigatorUserAgent = navigator.userAgent;
 var iPhone = navigatorUserAgent.indexOf("iPhone");
 if (iPhone > -1) {
  var dpr = Number(window.devicePixelRatio),
    one_dpr = 1 / dpr
 } else {
  var dpr = 1,
   one_dpr = 1
 }
 var writeText = "<meta name=\"viewport\" content=\"width=device-width,initial-scale=" + one_dpr + ",maximum-scale=" + one_dpr + ",minimum-scale=" + one_dpr + ",user-scalable=no\">\n  <meta name=\"'flexible\" content=\"initial-dpr=" + dpr + "\">";
 document.write(writeText);
 var html = document.getElementsByTagName("html");
 var F0 = 75;
 html[0].setAttribute("data-dpr", dpr);
 var getFontSize = function getFontSize() {
  var windowWidth = window.innerWidth;
  html[0].style.fontSize = F0 * windowWidth / 750 + "px"
 };
 getFontSize();
 _window.addEventListener("resize", getFontSize, false)
})(window);

定义相关变量

var camera,//摄像机
 scene,//舞台
 renderer,//渲染器
 isUserInteracting = false,//用户是否正在操作
 onMouseDownMouseX = 0, onMouseDownMouseY = 0,//鼠标点击的x和Y坐标
 lon = 0, onMouseDownLon = 0, onPointerDownLon= 0.0,onPointerDownPointerX = 0,//经度
 lat = 0, onMouseDownLat = 0, onPointerDownLat= 0.0,onPointerDownPointerY = 0,//纬度
 phi = 0, theta = 0,//计算相机位置的重要参数
 o = new Orienter(),//陀螺仪方法对象
 new_longitude=0,last_longitude=0,move_longitude=0,//改变的经度的计算
 new_latitude=0,last_latitude=0,move_latitude=0,//改变的纬度的计算
 is_touch=false,is_start=false,isPlay=true,isMusicPlay=true,tsa=100.1,ppl='';
 var raycaster = new THREE.Raycaster();//拾取场景里面的物体,可判断点击或交互事件对应的元素
 var mouse = new THREE.Vector2();//二维向量的对象,鼠标计算

初始化舞台的元素和内容

图片的长宽控制在4096px以内,部分机型性能不够,渲染不了超大的图片

function init() {/**初始化**/
    var container, mesh;//容器和素材
    container = document.getElementById( 'CanvasBody' );//容器
    camera = new THREE.PerspectiveCamera( 72, window.innerWidth / window.innerHeight, 0.01, 1100 );//相机
    camera.target = new THREE.Vector3( 0, 0, 0 );//相机位置

    scene = new THREE.Scene();//舞台
    scene.updateMatrixWorld(true);
    var geometry = new THREE.SphereGeometry(1, 32, 16);//球体

    geometry.scale( 1, 1, -1 );
    //设置球体的背景贴图
    var textureBg = new THREE.TextureLoader().load("img/bg.jpg");
    textureBg.generateMipmaps = true;
    textureBg.magFilter = THREE.LinearFilter;//设置贴纸素材的质量
    textureBg.minFilter = THREE.LinearFilter;
    var material = new THREE.MeshBasicMaterial( {
      map: textureBg,//圆柱体贴图,全景图
      //color:0xFF0000,
      //transparent: true
    } );
    mesh = new THREE.Mesh( geometry, material );
    //这里可以设置对应的动画效果
//    new TWEEN.Tween( mesh).to( {transform:"rotate(90deg)"}, 800 ).repeat( false ).delay( 300 ).yoyo( true ).easing( TWEEN.Easing.Cubic.InOut ).start();
    scene.add( mesh );

    // 旋转预设 摄影机看到的角度 Start//
    scene.rotation.set(0,0,0); //首?

    //初始化渲染器,追加到容器
    renderer = new THREE.WebGLRenderer({precision: 'highp' ,mipmap: 'highp',antialias:false});//加上precision 和 mipmap参数,调整画面清晰度
    renderer.setPixelRatio( window.devicePixelRatio );//设置像素比
    renderer.setSize( window.innerWidth, window.innerHeight );//设置渲染窗口的大小
    container.appendChild( renderer.domElement );//追加到容器中去

    //鼠标、手机touch的各个事件
    document.addEventListener( 'mousedown', onDocumentMouseDown, false );
    document.addEventListener( 'mousemove', onDocumentMouseMove, false );
    document.addEventListener( 'mouseup', onDocumentMouseUp, false );

    document.addEventListener( 'touchstart', onDocumentTouchDown, false );
    document.addEventListener( 'touchmove', onDocumentTouchMove, false );
    document.addEventListener( 'touchend', onDocumentTouchUp, false );
    // document.addEventListener( 'wheel', onDocumentMouseWheel, false );
    //
    document.addEventListener( 'dragover', function ( event ) {
      event.preventDefault();
      event.dataTransfer.dropEffect = 'copy';
    }, false );
    document.addEventListener( 'dragenter', function ( event ) {
      document.body.style.opacity = 0.5;
    }, false );
    document.addEventListener( 'dragleave', function ( event ) {
      document.body.style.opacity = 1;
    }, false );
    document.addEventListener( 'drop', function ( event ) {
      event.preventDefault();
      var reader = new FileReader();
      reader.addEventListener( 'load', function ( event ) {
        material.map.image.src = event.target.result;
        material.map.needsUpdate = true;
      }, false );
      reader.readAsDataURL( event.dataTransfer.files[ 0 ] );
      document.body.style.opacity = 1;
    }, false );
  }

监听的各事件和方法

//监听横竖屏重新设置尺寸
  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
  }
  function onDocumentMouseDown( event ) {
    event.preventDefault();
    isUserInteracting = true;
    onPointerDownPointerX = event.clientX;
    onPointerDownPointerY = event.clientY;
    onPointerDownLon = lon;
    onPointerDownLat = lat;

    // Click action
    mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
    raycaster.setFromCamera( mouse, camera );
    var intersects = raycaster.intersectObjects( scene.children );//第一个是最上面一层的元素
    console.log("点击的元素",intersects);
    if ( intersects.length > 0 ) {//如果点到小圆点 就执行回调函数回调函数为goto_p
      try {
        intersects[0].object.callback();
      }
      catch(err) {}
    }
  }
  function onDocumentMouseMove( event ) {
    if ( isUserInteracting === true ) {
      lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
      lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;
    }
  }
  function onDocumentMouseUp( event ) {
    isUserInteracting = false;
  }

  // touch event start
  function onDocumentTouchDown( event ) {
    is_touch=true;

    event.preventDefault();
    isUserInteracting = true;
    onPointerDownPointerX = event.touches[ 0 ].clientX;
    onPointerDownPointerY = event.touches[ 0 ].clientY;
    if(is_start){
      onPointerDownLon = lon;
      onPointerDownLat = lat;
    }
    // For Click action

    mouse.x = ( onPointerDownPointerX / renderer.domElement.clientWidth ) * 2 - 1;
    mouse.y = - ( onPointerDownPointerY / renderer.domElement.clientHeight ) * 2 + 1;

    raycaster.setFromCamera( mouse, camera );
    var intersects = raycaster.intersectObjects( scene.children );
    console.log('touchDown',lon,lat);
    if ( intersects.length > 0 ) {
      try {
        intersects[0].object.callback();
      }
      catch(err) {}
    }
  }
  function onDocumentTouchMove( event ) {
    if(is_start){
      if ( isUserInteracting === true ) {

        lon = ( onPointerDownPointerX - event.touches[ 0 ].clientX ) * 0.1 + onPointerDownLon;
        lat = ( event.touches[ 0 ].clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;
      }
    }
  }
  function onDocumentTouchUp( event ) {
    is_touch=false;
  }
  // touch event end

  // set
  function onDocumentTouchDown2( event ) {
    tsa = event.touches[0].clientY;
    console.log( '@:'+event.touches[0].clientY );
    event.preventDefault();

  }
  function onDocumentMouseWheel( event ) {
    camera.fov += event.deltaY * 0.05;
    camera.updateProjectionMatrix();
  }

动画播放和陀螺仪

function animate() {//播放动画
    if(isPlay){
      TWEEN.update();
      update();
      requestAnimationFrame( animate );
    }

  }
  o.onOrient = function (obj) {//重力感应计算角度
    if(is_start){
      //最新经度
      new_longitude = obj.lon;
      move_longitude=new_longitude-last_longitude;

      //最新纬度
      new_latitude = obj.lat;
      move_latitude = new_latitude-last_latitude;

      //判断经纬度
      if(move_longitude>=300){
        move_longitude=move_longitude-361;
      }else if(move_longitude<=-300){
        move_longitude=move_longitude+359;
      }


      if(move_latitude>=300){
        move_latitude=move_latitude-361;
      }else if(move_latitude<=-300){
        move_latitude=move_latitude+359;
      }

      if( is_touch ){
        move_longitude=0;
        move_latitude=0;
      }else{
        move_longitude=move_longitude*0.6;
        move_latitude=move_latitude*0.6;
      }
      //计算得出重力感应的经纬度
      lon=lon-move_longitude;
      last_longitude = obj.lon;
      lat = lat-move_latitude;
      last_latitude = obj.lat;
    }

  };

  function update() {//更新摄像机位置,旋转平移
    //lat = Math.max( -6, Math.min( 6, lat ) );//设置lat纬度的范围,只在一个范围内旋转
    phi = THREE.Math.degToRad( 90 - lat );
    theta = THREE.Math.degToRad( lon );
    camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );//X轴的坐标
    camera.target.y = 500 * Math.cos( phi );//y轴的坐标
    camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta ) ;//z轴的坐标
    camera.lookAt( camera.target );
    renderer.render( scene, camera );//重新渲染
  }

执行所有

//执行所有
  is_start=true;
  init();
  o.init();
  animate();

综上,炫酷的3D重力感应H5就出来啦!

源码 GitHub

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js DOM模型操作
Dec 28 Javascript
js 获取坐标 通过JS得到当前焦点(鼠标)的坐标属性
Jan 04 Javascript
javascript实现详细时间提醒信息效果的方法
Mar 11 Javascript
jQuery使用animate创建动画用法实例
Aug 07 Javascript
JS中artdialog弹出框控件之提交表单思路详解
Apr 18 Javascript
jQuery添加options点击事件并传值实例代码
May 18 Javascript
Bootstrap 3的box-sizing样式导致UEditor控件的图片无法正常缩放的解决方案
Sep 15 Javascript
jQuery Ajax File Upload实例源码
Dec 12 Javascript
微信小程序 input输入框控件详解及实例(多种示例)
Dec 14 Javascript
Bootstrap的popover(弹出框)在append后弹不出(失效)
Feb 27 Javascript
webpack组织模块打包Library的原理及实现
Mar 10 Javascript
微信小程序如何实现radio单选框单击打勾和取消
Jan 21 Javascript
Three.js实现3D机房效果
Dec 30 #Javascript
JavaScript对象的特性与实践应用深入详解
Dec 30 #Javascript
three.js搭建室内场景教程
Dec 30 #Javascript
Three.JS实现三维场景
Dec 30 #Javascript
Three.js实现简单3D房间布局
Dec 30 #Javascript
JavaScript数组特性与实践应用深入详解
Dec 30 #Javascript
微信小程序实现通过双向滑动缩放图片大小的方法
Dec 30 #Javascript
You might like
PHP与Java进行通信的实现方法
2013/10/21 PHP
php实现的Captcha验证码类实例
2014/09/22 PHP
支付宝接口开发集成支付环境小结
2015/03/17 PHP
php文件操作相关类实例
2015/06/18 PHP
HTML-CSS群中单选引发的“事件”
2007/03/05 Javascript
用js实现的模拟jquery的animate自定义动画(2.5K)
2010/07/20 Javascript
js 表单提交后按钮变灰的实例代码
2013/08/16 Javascript
原生js获取iframe中dom元素--父子页面相互获取对方dom元素的方法
2016/08/05 Javascript
第一次动手实现bootstrap table分页效果
2016/09/22 Javascript
移动开发之自适应手机屏幕宽度
2016/11/23 Javascript
jQuery 判断元素整理汇总
2017/02/28 Javascript
微信小程序 标签传入数据
2017/05/08 Javascript
JavaScript实现计算多边形质心的方法示例
2018/01/31 Javascript
JavaScript满天星导航栏实现方法
2018/03/08 Javascript
详解js中Array的方法及技巧
2018/09/12 Javascript
JS实现的A*寻路算法详解
2018/12/14 Javascript
vue实现element表格里表头信息提示功能(推荐)
2019/11/20 Javascript
Python实现的概率分布运算操作示例
2017/08/14 Python
利用信号如何监控Django模型对象字段值的变化详解
2017/11/27 Python
使用python socket分发大文件的实现方法
2019/07/08 Python
Python环境使用OpenCV检测人脸实现教程
2020/10/19 Python
美国眼镜网:GlassesUSA
2017/09/07 全球购物
意大利宠物用品购物网站:Bauzaar
2018/09/15 全球购物
即兴演讲稿
2014/01/04 职场文书
舞蹈比赛获奖感言
2014/02/04 职场文书
前厅部经理岗位职责范文
2014/02/04 职场文书
教师党性分析材料
2014/02/04 职场文书
股东合作协议书
2014/04/14 职场文书
团日活动总结怎么写
2014/06/25 职场文书
单位法人授权委托书范本
2014/10/09 职场文书
大学生违纪检讨书300字
2014/10/25 职场文书
趣味运动会开幕词
2015/01/28 职场文书
2015年环卫处个人工作总结
2015/07/27 职场文书
2019年描写人生经典诗句大全
2019/07/08 职场文书
Oracle设置DB、监听和EM开机启动的方法
2021/04/25 Oracle
Python编程super应用场景及示例解析
2021/10/05 Python