three.js實(shí)現(xiàn)炫酷的全景3D重力感應(yīng)
本文實(shí)例為大家分享了three.js 全景重力感應(yīng)的具體代碼,供大家參考,具體內(nèi)容如下
實(shí)現(xiàn)three.js 全景圖 demo
使用three.js 寫了球體和圓柱體版本的3D重力感應(yīng)全景圖,支持手指觸摸和陀螺儀感應(yīng),也支持PC端的鼠標(biāo)。給大家介紹一下基于移動端H5球體的實(shí)現(xiàn)方法,圓柱體類似。
設(shè)置容器和展示的樣式
設(shè)置容器的寬高為全屏展示,清除body的margin,引用three.min.js(3D渲染框架) 和orienter.js (陀螺儀經(jīng)緯度計(jì)算)

<div id="CanvasBody"></div> <script src="js/three.min.js"></script> <!--重力感應(yīng)--> <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;}
設(shè)置html的data-dpr 屬性,設(shè)置html 的fontSize
設(shè)置html的fontSize,重新計(jì)算body的實(shí)際可展示尺寸,這樣可以使渲染出來的畫面更清晰,分辨率最完美。
(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);
定義相關(guān)變量
var camera,//攝像機(jī) scene,//舞臺 renderer,//渲染器 isUserInteracting = false,//用戶是否正在操作 onMouseDownMouseX = 0, onMouseDownMouseY = 0,//鼠標(biāo)點(diǎn)擊的x和Y坐標(biāo) lon = 0, onMouseDownLon = 0, onPointerDownLon= 0.0,onPointerDownPointerX = 0,//經(jīng)度 lat = 0, onMouseDownLat = 0, onPointerDownLat= 0.0,onPointerDownPointerY = 0,//緯度 phi = 0, theta = 0,//計(jì)算相機(jī)位置的重要參數(shù) o = new Orienter(),//陀螺儀方法對象 new_longitude=0,last_longitude=0,move_longitude=0,//改變的經(jīng)度的計(jì)算 new_latitude=0,last_latitude=0,move_latitude=0,//改變的緯度的計(jì)算 is_touch=false,is_start=false,isPlay=true,isMusicPlay=true,tsa=100.1,ppl=''; var raycaster = new THREE.Raycaster();//拾取場景里面的物體,可判斷點(diǎn)擊或交互事件對應(yīng)的元素 var mouse = new THREE.Vector2();//二維向量的對象,鼠標(biāo)計(jì)算
初始化舞臺的元素和內(nèi)容
圖片的長寬控制在4096px以內(nèi),部分機(jī)型性能不夠,渲染不了超大的圖片
function init() {/**初始化**/
var container, mesh;//容器和素材
container = document.getElementById( 'CanvasBody' );//容器
camera = new THREE.PerspectiveCamera( 72, window.innerWidth / window.innerHeight, 0.01, 1100 );//相機(jī)
camera.target = new THREE.Vector3( 0, 0, 0 );//相機(jī)位置
scene = new THREE.Scene();//舞臺
scene.updateMatrixWorld(true);
var geometry = new THREE.SphereGeometry(1, 32, 16);//球體
geometry.scale( 1, 1, -1 );
//設(shè)置球體的背景貼圖
var textureBg = new THREE.TextureLoader().load("img/bg.jpg");
textureBg.generateMipmaps = true;
textureBg.magFilter = THREE.LinearFilter;//設(shè)置貼紙素材的質(zhì)量
textureBg.minFilter = THREE.LinearFilter;
var material = new THREE.MeshBasicMaterial( {
map: textureBg,//圓柱體貼圖,全景圖
//color:0xFF0000,
//transparent: true
} );
mesh = new THREE.Mesh( geometry, material );
//這里可以設(shè)置對應(yīng)的動畫效果
// new TWEEN.Tween( mesh).to( {transform:"rotate(90deg)"}, 800 ).repeat( false ).delay( 300 ).yoyo( true ).easing( TWEEN.Easing.Cubic.InOut ).start();
scene.add( mesh );
// 旋轉(zhuǎn)預(yù)設(shè) 攝影機(jī)看到的角度 Start//
scene.rotation.set(0,0,0); //首頁
//初始化渲染器,追加到容器
renderer = new THREE.WebGLRenderer({precision: 'highp' ,mipmap: 'highp',antialias:false});//加上precision 和 mipmap參數(shù),調(diào)整畫面清晰度
renderer.setPixelRatio( window.devicePixelRatio );//設(shè)置像素比
renderer.setSize( window.innerWidth, window.innerHeight );//設(shè)置渲染窗口的大小
container.appendChild( renderer.domElement );//追加到容器中去
//鼠標(biāo)、手機(jī)touch的各個(gè)事件
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 );
}
監(jiān)聽的各事件和方法
//監(jiān)聽橫豎屏重新設(shè)置尺寸
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 );//第一個(gè)是最上面一層的元素
console.log("點(diǎn)擊的元素",intersects);
if ( intersects.length > 0 ) {//如果點(diǎn)到小圓點(diǎn) 就執(zhí)行回調(diào)函數(shù)回調(diào)函數(shù)為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) {//重力感應(yīng)計(jì)算角度
if(is_start){
//最新經(jīng)度
new_longitude = obj.lon;
move_longitude=new_longitude-last_longitude;
//最新緯度
new_latitude = obj.lat;
move_latitude = new_latitude-last_latitude;
//判斷經(jīng)緯度
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;
}
//計(jì)算得出重力感應(yīng)的經(jīng)緯度
lon=lon-move_longitude;
last_longitude = obj.lon;
lat = lat-move_latitude;
last_latitude = obj.lat;
}
};
function update() {//更新攝像機(jī)位置,旋轉(zhuǎn)平移
//lat = Math.max( -6, Math.min( 6, lat ) );//設(shè)置lat緯度的范圍,只在一個(gè)范圍內(nèi)旋轉(zhuǎn)
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );//X軸的坐標(biāo)
camera.target.y = 500 * Math.cos( phi );//y軸的坐標(biāo)
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta ) ;//z軸的坐標(biāo)
camera.lookAt( camera.target );
renderer.render( scene, camera );//重新渲染
}
執(zhí)行所有
//執(zhí)行所有 is_start=true; init(); o.init(); animate();
綜上,炫酷的3D重力感應(yīng)H5就出來啦!
源碼 GitHub
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JS實(shí)現(xiàn)網(wǎng)頁Div層Clone拖拽效果
這篇文章主要介紹了JS實(shí)現(xiàn)網(wǎng)頁Div層Clone拖拽效果,涉及JavaScript響應(yīng)鼠標(biāo)事件動態(tài)改變頁面元素位置屬性及層級屬性的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09
JS Object.preventExtensions(),Object.seal()與Object.freeze()用
這篇文章主要介紹了JS Object.preventExtensions(),Object.seal()與Object.freeze()用法,結(jié)合實(shí)例形式分析了javascript控制對象擴(kuò)展、密封、凍結(jié)等相關(guān)函數(shù)與操作技巧,需要的朋友可以參考下2018-08-08
JS實(shí)現(xiàn)Date日期格式的本地化的方法小結(jié)
為了更好的更新多語言日期的顯示,所以希望實(shí)現(xiàn)日期的本地化格式顯示要求,常規(guī)的特殊字符型格式化無法滿足顯示要求,這里整理了幾種我思考實(shí)現(xiàn)的本地化實(shí)現(xiàn)功能2024-06-06
JavaScript+Node.js寫一款markdown解析器
這篇文章主要介紹了利用JavaScript和Node.js寫一款markdown解析器,首先編寫getHtml函數(shù),傳入markdown文本字符串,下面更多詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2022-02-02
Javascript實(shí)現(xiàn)運(yùn)算符重載詳解
本文給大家匯總介紹了Javascript實(shí)現(xiàn)運(yùn)算符重載的方法,實(shí)現(xiàn)的思路很簡單,有需要的小伙伴可以來看看2018-04-04
用javascript實(shí)現(xiàn)分割提取頁面所需內(nèi)容
用javascript實(shí)現(xiàn)分割提取頁面所需內(nèi)容...2007-05-05
JS格式化數(shù)字(每三位加逗號)的方法總結(jié)
這篇文章總結(jié)了JS格式化數(shù)字(每三位加逗號)的幾種方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06

