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面向对象之静态与非静态类
Feb 03 Javascript
js加载之使用DOM方法动态加载Javascript文件
Nov 08 Javascript
jQuery应用之jQuery链用法实例
Jan 19 Javascript
jQuery中$.click()无效问题分析
Jan 29 Javascript
JavaScript中连接操作Oracle数据库实例
Apr 02 Javascript
jQuery树形下拉菜单特效代码分享
Aug 15 Javascript
JS中call/apply、arguments、undefined/null方法详解
Feb 15 Javascript
jquery做个日期选择适用于手机端示例
Jan 10 Javascript
jQuery选择器中的特殊符号处理方法
Sep 08 jQuery
js中let和var定义变量的区别
Feb 08 Javascript
echarts实现词云自定义形状的示例代码
Feb 20 Javascript
jQuery实现B2B网站后台管理系统侧导航
Jul 08 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
ThinkPHP模板Switch标签用法示例
2014/06/30 PHP
Smarty模板引擎缓存机制详解
2016/05/23 PHP
深入解析JavaScript中的变量作用域
2013/12/06 Javascript
js禁止回车提交表单的示例代码
2013/12/23 Javascript
js实现Select列表各项上移和下移的方法
2015/08/14 Javascript
利用jQuery和CSS将背景图片拉伸
2015/10/16 Javascript
非常实用的12个jquery代码片段
2015/11/02 Javascript
JS数组操作中的经典算法实例讲解
2017/07/26 Javascript
Javascript(es2016) import和require用法和区别详解
2017/08/11 Javascript
vue router学习之动态路由和嵌套路由详解
2017/09/21 Javascript
基于js中的存储键值对以及注意事项介绍
2018/03/30 Javascript
vue使用中的内存泄漏【推荐】
2018/07/10 Javascript
webpack中如何加载静态文件的方法步骤
2019/05/18 Javascript
js事件触发操作实例分析
2019/06/21 Javascript
微信小程序3种位置API的使用方法详解
2019/08/05 Javascript
[03:04]2018年国际邀请赛典藏宝瓶&莱恩声望物品展示 片尾有彩蛋
2018/06/04 DOTA
python如何爬取个性签名
2018/06/19 Python
Python操作MySQL数据库的方法
2018/06/20 Python
python安装本地whl的实例步骤
2019/10/12 Python
Python装饰器原理与基本用法分析
2020/01/07 Python
Python安装依赖(包)模块方法详解
2020/02/14 Python
DataFrame 数据合并实现(merge,join,concat)
2020/06/14 Python
Anaconda+spyder+pycharm的pytorch配置详解(GPU)
2020/10/18 Python
Python中免验证跳转到内容页的实例代码
2020/10/23 Python
Python爬虫破解登陆哔哩哔哩的方法
2020/11/17 Python
Kiwi.com中国:找到特价机票并发现新目的地
2019/10/27 全球购物
多媒体编辑专业毕业生推荐信
2013/11/05 职场文书
大学毕业生个人自荐书
2014/07/02 职场文书
单位授权委托书范本
2014/09/26 职场文书
科技活动周标语
2014/10/08 职场文书
2014年村计划生育工作总结
2014/11/14 职场文书
合理缓解职场压力,让你随时保持最佳状态!
2019/06/21 职场文书
Vue3.0写自定义指令的简单步骤记录
2021/06/27 Vue.js
C站最全Python标准库总结,你想要的都在这里
2021/07/03 Python
JavaScript模拟实现网易云轮播效果
2022/04/04 Javascript
SQL Server一个字符串拆分多行显示或者多行数据合并成一个字符串
2022/05/25 SQL Server