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 相关文章推荐
JavaScript null和undefined区别分析
Oct 14 Javascript
jQuery拖动图片删除示例
May 10 Javascript
js面向对象编程之如何实现方法重载
Jul 02 Javascript
node.js中的console用法总结
Dec 15 Javascript
JS模拟实现Select效果代码
Sep 24 Javascript
ajax在兼容模式下失效的快速解决方法
Mar 22 Javascript
Bootstrap3制作图片轮播效果
May 12 Javascript
jQuery的Each比JS原生for循环性能慢很多的原因
Jul 05 Javascript
Vue 中axios配置实例详解
Jul 27 Javascript
vue地址栏直接输入路由无效问题的解决
Nov 15 Javascript
解决layer.confirm快速点击会重复触发事件的问题
Sep 23 Javascript
jQuery实现视频展示效果
May 30 jQuery
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 向访客和爬虫显示不同的内容
2009/11/09 PHP
如何使用GDB调试PHP程序
2015/12/08 PHP
php基于session锁防止阻塞请求的方法分析
2017/08/07 PHP
Javascript创建自定义对象 创建Object实例添加属性和方法
2012/06/04 Javascript
JavaScript String.replace函数参数实例说明
2013/06/06 Javascript
谷歌浏览器调试JavaScript小技巧
2014/12/29 Javascript
jQuery扁平化风格下拉框美化插件FancySelect使用指南
2015/02/10 Javascript
深入浅析JavaScript中with语句的理解
2016/05/12 Javascript
jquery精度计算代码 jquery指定精确小数位
2017/02/06 Javascript
Bootstrap显示与隐藏简单实现代码
2017/03/06 Javascript
详解如何在NodeJS项目中优雅的使用ES6
2017/04/22 NodeJs
详解Angular结合zTree异步加载节点数据
2018/01/20 Javascript
基于Proxy的小程序状态管理实现
2019/06/14 Javascript
vue实现codemirror代码编辑器中的SQL代码格式化功能
2019/08/27 Javascript
简单分析js中的this的原理
2019/08/31 Javascript
Javascript执行上下文顺序的深入讲解
2020/11/04 Javascript
[02:51]DOTA2英雄基础教程 风暴之灵
2013/12/23 DOTA
Python双精度浮点数运算并分行显示操作示例
2017/07/21 Python
Python实现文件内容批量追加的方法示例
2017/08/29 Python
python实现决策树分类算法
2017/12/21 Python
Python面向对象编程之继承与多态详解
2018/01/16 Python
浅谈python配置与使用OpenCV踩的一些坑
2018/04/02 Python
Python2和3字符编码的区别知识点整理
2019/08/08 Python
django-rest-swagger的优化使用方法
2019/08/29 Python
Python paramiko模块使用解析(实现ssh)
2019/08/30 Python
python实现梯度下降法
2020/03/24 Python
python3.6.5基于kerberos认证的hive和hdfs连接调用方式
2020/06/06 Python
英国领先的电视购物零售商:Ideal World
2019/03/18 全球购物
教师实习自我鉴定
2013/12/18 职场文书
入党思想汇报
2014/01/05 职场文书
高中美术教学反思
2014/01/19 职场文书
乳制品整治工作方案
2014/05/29 职场文书
专题组织生活会思想汇报
2014/10/01 职场文书
2014年招商引资工作总结
2014/11/22 职场文书
2015毕业实习推荐信
2015/03/23 职场文书
《正比例》教学反思
2016/02/23 职场文书