JS 實現全景圖預覽

WebGL(全稱 Web Graphics Library)是一種 3D 繪圖協議,這種繪圖技術標準容許把 JavaScript 和 OpenGL ES 2.0 結合在一塊兒,經過增長 OpenGL ES 2.0 的一個 JavaScript 綁定,WebGL 能夠爲 HTML5 Canvas 提供硬件 3D 加速渲染,這樣 Web 開發人員就能夠藉助系統顯卡來在瀏覽器裏更流暢地展現 3D 場景和模型了,還能建立複雜的導航和數據視覺化。Three.js 是一款開源的主流 3D 繪圖 JS 引擎,它就像 jQuery 簡化了 HTML DOM 操做同樣,能夠簡化 WebGL 編程。css

1、Three.js 座標介紹

三維空間中主要有兩種幾何變換,一種是位置的變換,位置變換和二維空間的是同樣的,座標值進行增減就可。另外一種就是旋轉變換,二維空間的旋轉能夠看做是圍繞點的旋轉,只能基於平面上某個點爲圓心進行旋轉,而三維空間的旋轉則是圍繞一條線旋轉的,有多個方向。Three.js 是自持旋轉矩陣,歐拉角,四元數這三種旋轉方式。html

對於三維轉換空間的座標系,個人理解就是能夠分爲:世界座標系、屏幕座標系。世界座標系就是空間座標系,屏幕座標系就是咱們看到的平面上的座標系,其實應該有個中間過分的座標系,就是標準設備座標系 [-1,1],經過現有的數據處理函數來進行轉換,這個只是我的理解,可能有誤差。下面就介紹如何進行世界座標系和屏幕座標系的轉換。ios

屏幕座標轉世界座標:git

屏幕座標是從左上角爲原點,這裏全景展現設備座標是以畫布的中心爲原點,須要處理一下,獲得相機座標。編程

function convertTo3DCoordinate(clientX,clientY){
        var mv = new THREE.Vector3(
            (clientX / window.innerWidth) * 2 - 1,
            -(clientY / window.innerHeight) * 2 + 1,
            0.5 );  //0.5能夠改變,改變後得到的threejs座標的x,y,z數值上會改變,可是差值上不會改變
        mv.unproject(this.camera);   //用相機反投影該向量  這個地方個人理解就是設備座標[-1,1]轉化爲世界座標,這個函數實現的是先將設備座標轉換爲齊次座標,以後與相機的MP矩陣的逆矩陣相乘。有興趣的能夠先看下線性代數。
        return mv;
    }
複製代碼

世界座標轉屏幕座標:數組

function convertTo2DCoordinate() {
        //獲取網格模型boxMesh的世界座標
        var worldVector = new THREE.Vector3(
            boxMesh.position.x,
            boxMesh.position.y,
            boxMesh.position.z
            );
        var standardVector = worldVector.project(camera);//用相機投影該向量,世界座標轉標準設備座標
        var a = window.innerWidth / 2;
        var b = window.innerHeight / 2;
        var param = {}
        param.x = Math.round(standardVector.x * a + a);//標準設備座標轉屏幕座標
        param.y = Math.round(-standardVector.y * b + b);//標準設備座標轉屏幕座標
        return param
    }
    其中相機投影和反投影的源碼爲下:
    project: function () {
        var matrix = new Matrix4();
            return function project( camera ) {
            matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
            return this.applyMatrix4( matrix );
        };
    }(),
    unproject: function () {
        var matrix = new Matrix4();
        return function unproject( camera ) {
            matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
            return this.applyMatrix4( matrix );
        };
    }(),
複製代碼

經緯度轉化爲空間座標:瀏覽器

//經、緯度   球體半徑
    function lgltToxyz(longitude,latitude,radius){
            //返回角度轉換成弧度以後的值
        var lg = degToRad(longitude) , lt = degToRad(latitude);
        var y = radius * Math.sin(lt);
        var temp = radius * Math.cos(lt);
        var x = temp * Math.sin(lg);
        var z = temp * Math.cos(lg);
        return {x:x , y:y ,z:z}
    }
複製代碼

2、全景圖展現的原理

全景圖是一種廣角圖,它的原理是等距圓柱投影,引用百科的解釋:緩存

等距圓柱投影,又稱方格投影,是假想球面與圓筒面相切於赤道,赤道爲沒有變形的線。經緯線網格,同通常正軸圓柱投影,經緯線投影成兩組相互垂直的平行直線。其特性是:保持經距和緯距相等,經緯線成正方形網格;沿經線方向無長度變形;角度和麪積等變形線與緯線平行,變形值由赤道向高緯逐漸增大。該投影適合於低緯地區製圖。bash

說白了就是將一個球體上的全部的點,所有投影到一個圓柱體的側面上去,圓柱側面展開圖上包含了球體上全部的像素點,因此,一張標準的全景圖的長款比例爲 2:1。cookie

如今明白了全景圖的造成過程,那麼咱們要作的就是展現全景圖,怎麼辦,就是進行圓柱側面投影的逆運算,將這個過程反轉一下!因此咱們就能夠運用 WebGL 繪製一個球體,而後將全景圖直接貼在球體上就能夠了,固然,須要進行映射的處理過程。

貼圖的過程:

//新建一個球體 (球體半徑 /水平方向上分段數/和垂直方向上分段數)
    //這裏分割數就腦補一下吧   想一想西瓜上的條紋,水平方向上再腦補上就出來了這個概念
     var geometry = new THREE.SphereGeometry( 500, 100, 100 );
    //沿x軸進行-1的scale,讓球體的面朝內(由於咱們將從球內進行觀看)。 
    geometry.scale( - 1, 1, 1 ); 
    //加載圖片, 新建材質
    var material = new THREE.MeshBasicMaterial( {
    //設置紋理貼圖
     map: new THREE.TextureLoader().load( '1.jpg' )
      } ); 
    //將幾何體和材質進行結合。 
    mesh = new THREE.Mesh( geometry, material );
    //場景添加進去
    scene.add( mesh )
到這裏,再設置下相機爲球體的中心點

    var camera;
        function initCamera() {
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
            camera.target = new THREE.Vector3( 0, 100, 0 );
        }
複製代碼

基本上咱們已經能夠展現全景圖了,還能夠再優化一下,由於球狀模型的頂點與面的數量十分龐大, 這些元素的數量越多, 耗費的瀏覽器資源就會越多。咱們能夠建立一個立方體,將一張全景圖分割爲立方體的六個面,貼到立方體上,若是這個立方體足夠大,咱們就不足以感受到它的邊際存在,這樣就避免了兩極像素點的重疊。代碼以下:

let loader = new THREE.TextureLoader();  //設置紋理貼圖
    //全景圖分解
    const imgList = [
        'top.png',
        'right.png',
        'bottom.png',
        'left.png',
        'front.png',
        'behind.png'
    ];
    //採用異步編程
    Promise.all(imgList.map(function (val) {
        //加載圖片, 新建材質, 傳給下一個步驟.
        return new Promise(function (resolve, reject) {
            loader.load(val, function (texture) {
                        //定義材料外觀的對象
                resolve(new THREE.MeshBasicMaterial({
                    map: texture,
                    side: THREE.BackSide
                }));
            });
        });
    })).then(function (materials) {
        //建立正方體
        geometry = new THREE.BoxGeometry(200, 200, 200);
        cube = new THREE.Mesh(
            geometry,
            new THREE.MeshFaceMaterial(materials) //能夠在該容器中爲物體的各表面貼上圖片
        );
        scene.add(cube);
        animate();
    });
複製代碼

最後經過 WebGLRenderer 就能夠了。

3、photo-sphere-viewer 介紹

前面介紹了全景圖的相關背景,接下來開始使用 photo-sphere-viewer(如下簡稱 view.js)。three.js 能夠理解爲 jq,view.js 就是 jq 的插件。

須要的資源:view.min.css、three.js、D.min.js、uEvent.js、doT.js、CanvasRenderer.js、Projector.js、DeviceOrientationControls.js、view.min.js、zepto_1.1.3.js。

參數介紹:

  • panorama:(必選)全景圖的路徑。
  • container:(必選)放置全景圖的容器。
  • autoload:(默認爲 true)true 爲自動加載全景圖,false 爲遲點加載全景圖(經過load 方法)。
  • usexmpdata:(默認值爲 true)photo sphere viewer 是否必須讀入 xmp 數據,false 爲沒必要須。
  • cors_anonymous:(默認值爲 true)true 爲不能經過 cookies 得到用戶
  • pano_size:(默認值爲 null)全景圖的大小,是否裁切。
  • default_position:(默認值爲 0)定義默認位置,用戶看見的第一個點,例如:{long: math.pi, lat: math.pi/2}。
  • min_fov:(默認值爲 30)觀察的最小區域,單位 degrees,在 1-179 之間。
  • max_fov:(默認值爲 90)觀察的最大區域,單位 degrees,在 1-179 之間。
  • allow_user_interactions:(默認值爲 true)設置爲 false,則禁止用戶和全景圖交互(導航條不可用)。
  • allow_scroll_to_zoom:(默認值爲 true)若設置爲 false,則用戶不能經過鼠標滾動進行縮放圖片。
  • tilt_up_max:(默認值爲 math.pi/2)向上傾斜的最大角度,單位radians。
  • tilt_down_max:(默認值爲 math.pi/2)向下傾斜的最大角度,單位 radians。
  • min_longitude:(默認值爲 0)可以展現的最小經度。
  • max_longitude:(默認值爲 2PI)可以展現的最大維度。
  • zoome_level:(默認值爲 0)默認的縮放級別,值在 0-100 之間。
  • long_offset:(默認值爲 PI/360)mouse/touch 移動時每像素通過的經度值。
  • lat_offset:(默認值爲 PI/180)mouse/touch 移動時每像素通過的緯度值。
  • time_anim(默認值爲 2000)全景圖在 time_anim 毫秒後會自動進行動畫。(設置爲false禁用它)
  • reverse_anim:(默認值爲 true)當水平方向到達最大/最小的經度時,動畫方向是否反轉(僅僅是不能看到完整的圓)。
  • anim_speed:(默認值爲 2rpm)動畫每秒/分鐘多少的速度。
  • vertical_anim_speed:(默認值爲2rpm)垂直方向的動畫每秒/分鐘多少的速度。
  • vertical_anim_target:(默認值爲0)當自動旋轉時的維度,默認爲赤道。
  • navbar:(默認爲false)顯示導航條。
  • navbar_style:(默認值爲false)導航條的樣式。有效的屬性:
  • backgroundColor:導航條背景色(默認值rgba(61, 61, 61, 0.5));
  • buttonsColor:按鈕前景色(默認值 rgba(255, 255, 255, 0.7));
  • buttonBackgroundColor:按鈕激活時的背景色(默認值 rgba(255, 255, 255, 0.1));
  • buttonsHeight:按鈕高度,單位px(默認值 20);
  • autorotateThickness:自動旋轉圖片的層(默認值 1);
  • zoomRangeWidth:縮放遊標的寬度,單位px(默認值 50);
  • zoomRangeThickness:縮放遊標的層(默認值 1);
  • zoomRangeDisk:縮放遊標的放大率,單位 px(默認值 7);
  • fullscreenRatio:全屏圖標的比例(默認值 4/3);
  • fullscreenThickneee:全屏圖片的層,單位 px(默認值 2)
  • loading_msg:(默認值爲 Loading...)加載信息。
  • loading_img:(默認值爲 null)loading 圖片的路徑。
  • loading_html:(默認值 爲null)html 加載器(添加到容器中的元素或字符串)。
  • size:(默認值爲 null)全景圖容器的最終尺寸,例如 {width: 500, height: 300}。
  • onready:(默認值爲 null)全景圖準備好而且第一張圖片展現出來後的回調函數。

方法介紹:

  • addAction():添加事件(插件沒有提供執行事件的方法,彷佛是提供給插件內部使用的)。
  • fitToContainer():調整全景圖容器大小爲指定大小。
  • getPosition():獲取座標經緯度。
  • getPositionInDegrees():獲取經緯度度數。
  • getZoomLevel():獲取縮放級別。
  • load():加載全景圖()。
  • moveTo(longitude, latitude):根據經緯度移動到某一點。
  • rotate(dlong, dlat):根據經緯度度數移動到某一點。
  • toggleAutorotate():是否開啓全景圖自動旋轉。
  • toggleDeviceOrientation():是否開啓重力感應方向控制。
  • toggleFullscreen():是否開啓全景圖全屏。
  • toggleStereo():是否開啓立體效果(可用於 WebVR 哦)。
  • zoom(level):設置縮放級別。
  • zoomIn():放大。
  • zoomOut():縮小。

使用:

首先建立一個 div 來做爲渲染區域

<div id="photosphere"></div>
    //初始化
    var PSV = new PhotoSphereViewer({
    container: 'photosphere',
                    panorama: panos[0].url
    })
複製代碼

4、建立標記

展現全景圖的時候通常咱們會遇到以下場景:展現一個大範圍的區域,裏面包含了不少須要介紹的詳細地點,因此咱們能夠經過建立標記點,來給出相關的信息展現。

使用:

//定義在new裏面  自執行
    markers: (function() {
               return common();  //將建立marker  封裝
            }())
    function common() {
           var a = [];
           for(var i = 0; i < Math.PI * 2; i += Math.PI / 4) {
              for(var j = -Math.PI / 2 + Math.PI / 4; j < Math.PI / 2; j += Math.PI / 4) {
                   a.push({
                       id: '#' + a.length,
                       tooltip: '點我啊',  //點擊的提示信息
                       latitude: j,    //經緯度   可使用 x, y替換   表明全景圖上的像素座標
                       longitude: i,
                       image: '../img/pin2.png',   //圖片標記背景圖
                       width: 32,   //寬高
                       height: 32,
                       anchor: 'bottom center',
                       data: {  //設置數據
                           deletable: true
                       }
                   });
                  }
               }
               return a  //marker接受數組   因此以數組的形式反出
         }
複製代碼

自定義 SVG:

a.push({
        id: 'circle',   //id必選
        tooltip: 'A circle of radius 30',
        circle: 20,  //半徑
        svgStyle: {
            fill: 'rgba(255,255,0,0.3)',
            stroke: 'yellow',
            strokeWidth: '2px'
        },
        //                      longitude: 0.14842681258549928,
        //                      latitude: -0.8678522571819425,
        x: 8395,
        y: 6827,
        anchor: 'center right'
    });
自定義html

    a.push({
        id: 'text',
        longitude: -0.5,
        latitude: -0.28,
        html: '♥愛你愛你麼麼噠♥',  
        anchor: 'bottom right',
        style: {  //定義樣式
        maxWidth: '320px',
        color: 'red',
        fontSize: '20px',
        fontFamily: 'Helvetica, sans-serif',
        textAlign: 'center'
        },
        tooltip: {  //提示
        content: 'An HTML marker',
        position: 'right'
        }
    });
複製代碼

5、景圖的自動播放加背景音樂

<audio src="../libs/source.mp3" id="audios" style="position: absolute;left: 99999999999999px;top: 999999999px;" loop="true"></audio>

    PSV.on('ready',function(){  //準備就緒
         PSV.toggleAutorotate();   //自動播放
         $('#audios')[0].play();   //播放背景音樂
         $('.scene').hide();       //loading   下面提到
         $('.playBtn').show();   //音樂播放按鈕
     });
複製代碼

6、圖片緩存,場景切換

設置 cache_texture:number,參數爲緩存圖片,加載後不會重複加載,number 自定義。

點擊 marker 進行場景的切換:

PSV.on('select-marker', function(marker) { //監聽marker點擊事件。
    //進行場景的切換
    PSV.setPanorama(url, {longitude: 3.848,
                        latitude: -0.244}, true)
                            .then(function() {
                                $('.back').show();
                                $('.scene').hide();
                                PSV.setCaption('場景描述');
                            });
    })

    setPanorama參數:圖片地址、下一個場景的初始經緯度、transition 默認(false)
還能夠設置

    navbar: [
              'autorotate', 'zoom', 'download', 'markers',
              'spacer-1',
              {
                  title: 'Change image',
                  className: 'custom-button',
                  content: '點我',
                  onClick: (function() {
                      //自定義邏輯
                  }())
              }]
複製代碼

來自定義導航的內容。

再補充一下 panorama 能夠接受一個數組(六張 url),也能夠接受一個單獨的圖片 url。若是一張的話在 _loadEquirectangularTexture 這個函數裏面內部進行了裁剪,若是是一個數組,直接渲染,提升了性能。

切換場景的過程當中加載 loading,由於咱們開啓了緩存,因此須要設置一個變量(開關思想)來控制 loading 框的顯示次數,好比第一次場景 1 進入場景 2,須要 loading 加載,可是從場景 2 再跳回到場景 1 由於緩存就不不要 loading,只加載一次。loading 的製做是經過 svg 製做的,這裏沒有貼出代碼。

源代碼下載地址:

download.csdn.net/download/su…

相關文章
相關標籤/搜索