国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > 不到30行代碼實現(xiàn)一個酷炫H5全景

不到30行代碼實現(xiàn)一個酷炫H5全景

時間:2023-07-23 08:54:02 | 來源:網(wǎng)站運營

時間:2023-07-23 08:54:02 來源:網(wǎng)站運營

不到30行代碼實現(xiàn)一個酷炫H5全景:前言: 本文將圍繞:了解什么是全景 --> 怎么構(gòu)成全景 --> 全景交互原理來進行講解,手把手教你從零基礎(chǔ)實現(xiàn)一個酷炫的 Web 全景,并講解其中的原理。小白也能學(xué)習(xí),建議收藏學(xué)習(xí),有任何疑問,請在評論區(qū)討論,筆者經(jīng)常查看并回復(fù)。

一、了解什么是全景

1.1 全景定義

定義:全景是某一空間的全部景色。

通俗地說:大家都拍過照片,那我們想想一下拍照片的過程:站在某個空間,拿著相機,朝著某一角度拍攝,就可以獲得這角度的景色照片了,而全景呢?是站在某個空間,拿著相機站著,朝著 360 角度拍攝,獲得所有角度的景色照片,組合起來,再通過專門的技術(shù)展示給大家看的可交互的照片。

全景示例:




Jietu20210527-113413-HD.gif



體驗二維碼(支持微信掃碼):







1.2 全景展示方式

全景的展示方式有很多中,比如:柱體全景、立方體全景、球體全景等等……

Jietu20210527-152042-HD.gif



最最通俗的理解:用一個大的紙箱套在頭上,看的場景(這種展示方式就是立方體全景)




image.png



柱體、立方體存在交叉區(qū)域,界面在交叉區(qū)域交互會呈現(xiàn)死角。所以,最好全景呈現(xiàn)方式是球體全景,360 度無死角,本文將以球體全景來講解。

二、怎么構(gòu)成全景

2.1 認識 ThreeJS

目前主流全景的前端實現(xiàn)方式:

實現(xiàn)方式費用是否開源學(xué)習(xí)成本開發(fā)難度兼容性擴展性能
CSSS 3D免費支持 CSS3D 的瀏覽器
ThreeJS免費支持 WebGL 的部分瀏覽器
全景工具(Krpano)收費支持 flash 和 canvas 的瀏覽器
作為一個有追求(瞎折騰)前端開發(fā),當(dāng)然要選擇ThreeJS?。?!

ThreeJS 是 Three(3D)+JS(JavaScript),它封裝了底層的 WebGL 接口,使得我們能夠在不了解圖形學(xué)知識的前提下,也能用簡單的代碼實現(xiàn)三維場景的渲染。

要想在屏幕中展示 3D 圖像,大致思路:

以上是 ThreeJS 渲染物體的固定寫法,不理解的話記住也行的

球體全景所需的圖片素材(下圖):寬是高的兩倍,數(shù)值是 2 的整數(shù)倍最好,建議圖片寬高為 2048px*1024px(后面實現(xiàn)全景會用到哈)



image.png



下載地址:https://github.com/azuoge/Opanorama/blob/master/res/1.jpg

具體代碼實現(xiàn):

<!DOCTYPE html><html lang="en"> <head> <meta charset="utf-8" /> <title>手把手教你制作酷炫Web全景</title> <meta name="viewport" id="viewport" content="width=device-width,initial-scale=1,minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" /> </head> <body> <div id="wrap" style="position: absolute;z-index: 0;top: 0;bottom: 0;left: 0;right: 0;width: 100%;height: 100%;overflow: hidden;" ></div> <script src="https://cdn.bootcdn.net/ajax/libs/three.js/r128/three.js"></script> <script> const width = window.innerWidth const height = window.innerHeight const radius = 50 // 球體半徑 // 第一步:創(chuàng)建場景 const scene = new THREE.Scene() // 第二步:繪制一個球體 const geometry = new THREE.SphereBufferGeometry(radius, 32, 32) const material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('./img/1.jpeg'), // 上面的全景圖片,注意引用目錄 }) const mesh = new THREE.Mesh(geometry, material) scene.add(mesh) // 第三步:創(chuàng)建相機,并確定相機位置 const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000) camera.position.x = 0 // 確定相機位置 camera.position.y = 0 camera.position.z = radius * 3 // 走遠了看 camera.target = new THREE.Vector3(0, 0, 0) // 設(shè)定對焦點 // 第四步:拍照并繪制到canvas const renderer = new THREE.WebGLRenderer() renderer.setSize(width, height) // 設(shè)置照片大小 document.querySelector('#wrap').appendChild(renderer.domElement) // 繪制到canvas function render() { camera.lookAt(camera.target) // 對焦 renderer.render(scene, camera) // 拍照 // 不斷渲染,因為圖片加載和處理需要時間,不確定何時拍照合適 requestAnimationFrame(render) } render() </script> </body></html>瀏覽器頁面效果(記得開啟手機模擬調(diào)試):




image.png



2.2 基礎(chǔ)知識點

2.2.1 經(jīng)緯度

本文是使用經(jīng)緯度來操作全景,需要科普一下經(jīng)緯度的知識

經(jīng)緯度是經(jīng)度與緯度的合稱組成一個坐標(biāo)系統(tǒng)。稱為地理坐標(biāo)系統(tǒng),它是一種利用三度空間的球面來定義地球上的空間的球面坐標(biāo)系統(tǒng),能夠標(biāo)示地球表面上的任何一個位置。




image.png



如圖所示,經(jīng)度:lon,取值范圍:[0,360],緯度:lat,取值范圍:[-90,90];

2.2.2 經(jīng)緯度轉(zhuǎn)換三維坐標(biāo)

球面的點{lon,lat},其中 R 為球體的半徑,求球面的點的在 ThreeJS 的坐標(biāo)的位置為:




image.png



解:

X = R cos(lat) sin( lon )
Y = R * sin( lat )
Z = R * cos( lat )*cos( lon )

注:ThreeJS 中默認的坐標(biāo)系是右手坐標(biāo)系,X 軸為左右,Y 軸為上下,Z 軸為前后。

2.3 生成全景的步驟

在 2.1 的章節(jié)中,我們已經(jīng)完成了繪制一個球體,繪制全景是在其基礎(chǔ)上要做調(diào)整:

具體代碼實現(xiàn):

<!DOCTYPE html><html lang="en"> <head> <meta charset="utf-8" /> <title>手把手教你制作酷炫Web全景</title> <meta name="viewport" id="viewport" content="width=device-width,initial-scale=1,minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover" /> </head> <body> <div id="wrap" style="position: absolute;z-index: 0;top: 0;bottom: 0;left: 0;right: 0;width: 100%;height: 100%;overflow: hidden;" ></div> <script src="https://cdn.bootcdn.net/ajax/libs/three.js/r128/three.js"></script> <script> const width = window.innerWidth, height = window.innerHeight // 屏幕寬高 const radius = 50 // 球體半徑 // 第一步:創(chuàng)建場景 const scene = new THREE.Scene() // 第二步:繪制一個球體 const geometry = new THREE.SphereBufferGeometry(radius, 32, 32) geometry.scale(-1, 1, 1) // 球面反轉(zhuǎn),由外表面改成內(nèi)表面貼圖 const material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('./img/1.jpeg'), // 下載上面的全景圖片到./img目錄下 }) const mesh = new THREE.Mesh(geometry, material) scene.add(mesh) // 第三步:創(chuàng)建相機,并確定相機位置 const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000) camera.position.x = 0 // 確定相機位置移到球心 camera.position.y = 0 camera.position.z = 0 camera.target = new THREE.Vector3(radius, 0, 0) // 設(shè)置一個對焦點 // 第四步:拍照并繪制到canvas const renderer = new THREE.WebGLRenderer() renderer.setPixelRatio(window.devicePixelRatio) renderer.setSize(width, height) // 設(shè)置照片大小 document.querySelector('#wrap').appendChild(renderer.domElement) // 繪制到canvas renderer.render(scene, camera) let lat = 0, lon = 0 function render() { lon += 0.003 // 每幀加一個偏移量 // 改變相機的對焦點,計算公式參考:2.2.2章節(jié) camera.target.x = radius * Math.cos(lat) * Math.cos(lon) camera.target.y = radius * Math.sin(lat) camera.target.z = radius * Math.cos(lat) * Math.sin(lon) camera.lookAt(camera.target) // 對焦 renderer.render(scene, camera) requestAnimationFrame(render) } render() </script> </body></html>效果:




Jietu20210527-172203-HD.gif



至此,我們?nèi)爸谱饕呀?jīng)完成了,(只統(tǒng)計 js 代碼:共28 行代碼,我才不是標(biāo)題黨呢? )。

三、全景交互原理

3.1 手勢交互之旋轉(zhuǎn)

手勢交互之旋轉(zhuǎn)指單指滑動操作,這與滑動地球儀的交互是一致的。

屏幕坐標(biāo)系,左上角為原點,X 軸:由左向右,Y 軸:由上到下, 手指在屏幕滑動會依次觸發(fā)三個事件:touchstart、touchmove 和 touchend;event 對象中記錄了手指屏幕的位置




TeamTalk_IMG_2021-05-27-173217.jpg



手指在屏幕滑動過程:

image.png



那么單指在屏幕的滑動,由 P1 (clientX1,clientY1)移動到 P2 (clientX1,clientY1)長度為,對應(yīng)經(jīng)緯度變化:

distanceX = clientX1 - clientX2 // X軸方向distanceY = clientY1 - clientY2 // Y軸方向// 其中R為球體半徑,根據(jù)弧長公式:lon = distanX / Rlat = distanY / R代碼實現(xiàn):

// 增加touch事件監(jiān)聽let lastX, lastY // 上次屏幕位置let curX, curY // 當(dāng)前屏幕位置const factor = 1 / 10 // 靈敏系數(shù)const $wrap = document.querySelector('#wrap')// 觸摸開始$wrap.addEventListener('touchstart', function (evt) { const obj = evt.targetTouches[0] // 選擇第一個觸摸點 startX = lastX = obj.clientX startY = lastY = obj.clientY})// 觸摸中$wrap.addEventListener('touchmove', function (evt) { evt.preventDefault() const obj = evt.targetTouches[0] curX = obj.clientX curY = obj.clientY // 參考:弧長公式 lon -= ((curX - lastX) / radius) * factor // factor為了全景旋轉(zhuǎn)平穩(wěn),乘以一個靈敏系數(shù) lat += ((curY - lastY) / radius) * factor lastX = curX lastY = curY})單指操作效果:

Jietu20210527-172203-HD.gif



上面的代碼已經(jīng)加上全景的單指交互,但是,缺少了旋轉(zhuǎn)慣性。接下來,我們加一下慣性動畫:

滑動慣性實現(xiàn),手指在屏幕滑動過程:

代碼實現(xiàn):

let lastX, lastY // 上次屏幕位置let curX, curY // 當(dāng)前屏幕位置let startX, startY // 開始觸摸的位置,用于計算速度let isMoving = false // 是否停止單指操作let speedX, speedY // 速度const factor = 1 / 10 // 靈敏系數(shù),經(jīng)驗值const deceleration = 0.1 // 減速度,慣性動畫使用const $wrap = document.querySelector('#wrap')// 觸摸開始$wrap.addEventListener('touchstart', function (evt) { const obj = evt.targetTouches[0] // 選擇第一個觸摸點 startX = lastX = obj.clientX startY = lastY = obj.clientY startTime = Date.now() isMoving = true})// 觸摸中$wrap.addEventListener('touchmove', function (evt) { evt.preventDefault() const obj = evt.targetTouches[0] curX = obj.clientX curY = obj.clientY // 參考:弧長公式 lon -= ((curX - lastX) / radius) * factor // factor為了全景旋轉(zhuǎn)平穩(wěn),乘以一個系數(shù) lat += ((curY - lastY) / radius) * factor lastX = curX lastY = curY})// 觸摸結(jié)束$wrap.addEventListener('touchend', function (evt) { isMoving = false var t = Date.now() - startTime speedX = (curX - startX) / t // X軸方向的平均速度 speedY = (curY - startY) / t // Y軸方向的平均速度 subSpeedAnimate() // 慣性動畫})let animateInt// 減速度動畫function subSpeedAnimate() { lon -= speedX * factor // X軸 lat += speedY * factor // 減速度 speedX = subSpeed(speedX) speedY = subSpeed(speedY) // 速度為0或者有新的觸摸事件,停止動畫 if ((speedX === 0 && speedY === 0) || isMoving) { if (animateInt) { cancelAnimationFrame(animateInt) animateInt = undefined } } else { requestAnimationFrame(subSpeedAnimate) }}// 減速度function subSpeed(speed) { if (speed !== 0) { if (speed > 0) { speed -= deceleration; speed < 0 && (speed = 0); } else { speed += deceleration; speed > 0 && (speed = 0); } } return speed;}預(yù)覽地址:https://azuoge.github.io/Opanorama/

3.2 手勢交互之縮放

手勢交互之縮放是雙指操作,跟放大圖片一樣。

前面介紹 ThreeJS,提到過相機,全景縮放也是依據(jù)相機拍照時,縮放拍攝照片內(nèi)容的原理是一樣的。




image.png



使用 ThreeJS 創(chuàng)建相機代碼如下:

const camera = new THREE.PerspectiveCamera(fov, aspect, near, fear)參數(shù)說明:




image.png



其中,

其實,很好理解,睜大眼睛,我們就看的視野就廣,看到物體就顯得小些【縮小】,反之,瞇著眼,看到的視野就窄,看到物體就顯得大【放大】,可以通過修改右圖的 fov 的值來縮放全景圖片

那么如何計算 fov 呢?這時候我們需要雙指交互,同計算,開始觸摸計算第一次雙指的距離,在雙指移動中不斷計算雙指距離,與上一次距離相除即為縮放倍數(shù)。

關(guān)鍵代碼如下:

// 其中,(clientX1,clientY1)和(clientX2,clientY2)為雙指在屏幕的當(dāng)前位置// 計算距離,簡化運輸不用平方計算const distance = Math.abs(clientX1 - clientX2) + Math.abc(clientY1 - clientY)// 計算縮放比const scale = distance / lastDiance// 計算新的視角fov = camera.fov / scale// 視角范圍取值camera.fov = Math.min(90, Math.max(fov, 60)) // 90 > fov > 60 ,從參數(shù)說明中選取// 視角需要主動更新camera.updateProjectionMatrix()體驗地址:https://azuoge.github.io/Opanorama/

3.3 手機陀螺儀交互

html5 事件中,deviceorientation 事件,此事件是檢測設(shè)備方向變化時的事件。

H5 有兩份坐標(biāo):




image.png



當(dāng)將手機垂直,且正面(90 度)沖著自己。




image.png



從上圖觀察,并結(jié)合 ThreeJS 的坐標(biāo)系,可以得出關(guān)鍵結(jié)論:

這 alpha 角度就不再這次全景交互中。

當(dāng)將將手機垂直,且正面(90 度)沖著自己,轉(zhuǎn)動手機方向演示




Jietu20210530-104349-HD.gif



Chrome 瀏覽器是可以開啟陀螺儀模擬,操作如下:




image.png



那么代碼就很簡單:

// 角度換算弧度公式const L = Math.PI / 180// 陀螺儀交互window.addEventListener('deviceorientation', function (evt) { lon = evt.alpha * L lat = (evt.beta - 90) * L})效果如下:




Jietu20210530-112850-HD.gif



需要注意的是:H5 獲取的手機方向數(shù)值,在部分 android 手機,存在明顯的抖動,就算手機靜止放在桌面上,陀螺儀輸出的數(shù)據(jù)也會抖動;(該問題不屬于原理,只是在全景應(yīng)用過程遇到的問題,不感興趣的同學(xué)可以跳過 )




Jietu20210530-112405-HD.gif



我們需要對陀螺儀的輸出的數(shù)字做處理,這里采用信號傳輸中使用的 低通濾波算法

公式如下:




image.png
當(dāng) K=1 時,就是真實的數(shù)據(jù),大于 1,就可以稀釋變化值。

但是又有了新的問題:靈敏度和平穩(wěn)度的矛盾




image.png



通過統(tǒng)計數(shù)據(jù)得出的結(jié)論, K 取值為 10,靈敏度和平穩(wěn)度表現(xiàn)較好。

體驗地址:https://azuoge.github.io/Opanorama/

3.4 手勢和陀螺儀交互結(jié)合

手勢和陀螺儀的交互都轉(zhuǎn)化成經(jīng)緯度來驅(qū)動全景,那么,兩者結(jié)合也就很簡單了。

具體思路如下:

lat = touch.lat + orienter.lat + fix.lat // 取值范圍:[-90,90]lon = touch.lon + orienter.lon + fix.lon // 取值范圍:[0,360]其中,touch 為手勢影響,orienrer 為陀螺儀影響,fix 為修正因子,保證經(jīng)緯度在換算的結(jié)果始終符合取值范圍。




本文完整的代碼放在: https://github.com/azuoge/Opanorama,歡迎查閱和討論。

關(guān)鍵詞:實現(xiàn)

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關(guān)閉