最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 实现一个 360 全景的 N 种方案

    正文概述 掘金(飞猪前端团队)   2021-04-02   549

    概述

    360 全景浏览是一种性价比很高的虚拟现实解决方案,给人一种全新的浏览体验,让你足不出户就能身临其境地感受到现场的环境。该技术被广泛地应用在房产、酒店、家居等领域。在互联网订房已经普及的时代,在网站上用全景展示酒店宾馆的各种餐饮和住宿设施,是吸引顾客的好办法。利用网络,远程虚拟浏览宾馆的外型、大厅、客房、会议厅等各项服务场所,展现宾馆舒适的环境,给客户以实在感受,促进客户预定客房。在酒店大堂提供客房的全景展示,再也不用麻烦客户在各个房间会场穿梭,就能观看各房间的真实场景,更方便客户确认和挑选客房,进而提高效率,用户体验更胜一筹。

    下面我们使用三种方法讨论一个 360 全景的实现。

    一、CSS3

    利用 CSS 中的变换、旋转等属性就可以实现一个 360 全景。实现的基本思路如下:

    • 利用 CSS3 做出一个 3D 立方体。
    • 在立方体的 6 个面设置目标图片(利用全景工具导出的图片,一共有 6 张)。
    • 使用 perspective、translateZ、transform-style: preserve-3d 等属性改变视图的大小。
    • 添加触摸事件改变 translateX、translateY 的角度数即可实现一个基本的全景图效果。

    下面我们尝试使用 div 做出一个 3D 立方体:

    1. 写出 6 个面:
    <div class="scene">
      <div class="cube">
        <div class="cube__face cube__face--front">front</div>
        <div class="cube__face cube__face--back">back</div>
        <div class="cube__face cube__face--right">right</div>
        <div class="cube__face cube__face--left">left</div>
        <div class="cube__face cube__face--top">top</div>
        <div class="cube__face cube__face--bottom">bottom</div>
      </div>
    </div>
    

    实现一个 360 全景的 N 种方案

    perspective 指定观察者与 z=0 平面的距离,使具有三维位置变换的元素产生透视效果(近大远小原理)。transform-style 指定其为子元素提供 2D 还是 3D 的场景。 如下图,高度为 600px 的元素,距离 z=0 的屏幕 300px,视角与屏幕距离 1000px,根据相似三角形的原理,可以计算出该元素在屏幕上的投影高度为 857px,即实际我们看到的元素高度。

    实现一个 360 全景的 N 种方案

    关于这个属性的详细讲解可查看css3 系列之详解 perspective。

    1. 写出基本定位

      .scene {
        width: 200px;
        height: 200px;
        perspective: 600px;
      }
      
      .cube {
        width: 100%;
        height: 100%;
        position: relative;
        transform-style: preserve-3d;
      }
      
      .cube__face {
        position: absolute;
        width: 200px;
        height: 200px;
      }
      

      实现一个 360 全景的 N 种方案

    2. 旋转各面,使“三对”面互相垂直,达到如下效果

      实现一个 360 全景的 N 种方案

      .cube__face--front {
        transform: rotateY(0deg);
      }
      .cube__face--right {
        transform: rotateY(90deg);
      }
      .cube__face--back {
        transform: rotateY(180deg);
      }
      .cube__face--left {
        transform: rotateY(-90deg);
      }
      .cube__face--top {
        transform: rotateX(90deg);
      }
      .cube__face--bottom {
        transform: rotateX(-90deg);
      }
      

      结果如下图

      实现一个 360 全景的 N 种方案

    3. 貌似看不出怎么变换的?我们将以上重合的两个面的旋转角度稍微调整下,就可以更直观地看出效果:

      实现一个 360 全景的 N 种方案

    4. 各面继续向两侧位移

      .cube__face--front {
        transform: rotateY(0deg) translateZ(100px);
      }
      .cube__face--right {
        transform: rotateY(90deg) translateZ(100px);
      }
      .cube__face--back {
        transform: rotateY(180deg) translateZ(100px);
      }
      .cube__face--left {
        transform: rotateY(-90deg) translateZ(100px);
      }
      .cube__face--top {
        transform: rotateX(90deg) translateZ(100px);
      }
      .cube__face--bottom {
        transform: rotateX(-90deg) translateZ(100px);
      }
      

      实现一个 360 全景的 N 种方案

      整个过程可用下图表示:

      实现一个 360 全景的 N 种方案

      最终效果:

      实现一个 360 全景的 N 种方案

    5. 通过调整容器样式的 perspective 属性值,将视角放置在立方体中。将每个面的尺寸放大,添加上全景图切出的 6 面图,添加上鼠标事件,便可实现 360 全景效果。

      实现一个 360 全景的 N 种方案

      扫码看效果:

      实现一个 360 全景的 N 种方案

    二、Three.js

    Three.js 是一款运行在浏览器中的 webGL 引擎,我们可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象,利用 Three.js 我们可以轻松地实现各种想要的几何体。 上面的方案中,我们用 CSS3 “拼”出了一个立方体,而 webGL 中立方体等各种几何体是最常见的。我们可以利用 Threejs 中的立方体实现全景图功能,把立方体当成天空盒子,将无缝衔接的图片贴上,看起来就像在一个场景中,相机一般放置在中央,只要离边缘足够远就看不出是立方体,但如果超出边界就能看到他们。 下面是简单的实现过程:

    1. 初始化一个立方体几何,给材料上色,便得到了一个立方体:

      const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
      const material = new THREE.MeshBasicMaterial({ color: 0x156289 });
      const skyBox = new THREE.Mesh(geometry, material);
      

      实现一个 360 全景的 N 种方案

    2. 将立方体的纹理颜色换成图片纹理:

      const texture = new THREE.TextureLoader().load('textures/crate.gif');
      const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
      const material = new THREE.MeshBasicMaterial({ map: texture });
      const skyBox = new THREE.Mesh(geometry, material);
      

      实现一个 360 全景的 N 种方案

    3. 将 6 张全景图片替换掉上面相同的纹理:

      图片加载的顺序是正 X(px.jpg),负 X(nx.jpg),正 Y(py.jpg),负 Y(ny.jpg),正 Z(pz.jpg)和负 Z(nz.jpg),将他们分别赋给 6 个材质的贴图,作为立方体 skyBox 的材质。

      实现一个 360 全景的 N 种方案

      const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
      const textures = [
        'https://img.alicdn.com/imgextra/i3/O1CN01LsO1Bk20QbKpFTUQr_!!6000000006844-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i3/O1CN01uTWCLc1XOCOuA92H0_!!6000000002913-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i4/O1CN016lU3YJ1JdrJuFTcWt_!!6000000001052-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i2/O1CN01nYe2Mn1ohkmBVyKpp_!!6000000005257-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i4/O1CN014TNffn1nlaTfA98Fg_!!6000000005130-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i1/O1CN01sS5m781ya6JgLSaVk_!!6000000006594-0-tps-1500-1500.jpg',
      ];
      const materials = [];
      const textureLoader = new THREE.TextureLoader();
      
      for (let i = 0; i < 6; i++) {
        materials.push(
          new THREE.MeshBasicMaterial({ map: textureLoader.load(textures[i]) })
        );
      }
      const skyBox = new THREE.Mesh(geometry, materials);
      

      实现一个 360 全景的 N 种方案

    4. 因为相机在 skyBox 的内部,而内部的面不会显示,所以要将 X 轴或者 Z 轴的放大倍数变为负数,这样才能看到内部,scale.z=-1 时相当于将 Z 轴正向的面移到 Z 轴负方向上。

      skyBox.geometry.scale(1, 1, -1);
      

      实现一个 360 全景的 N 种方案

    5. 进一步优化体验:

      camera.position.z = 0.01; // 将相机放在里面
      const controls = new OrbitControls(camera, renderer.domElement);
      controls.enableZoom = false; // 禁用放大
      controls.enablePan = false; // 禁用双指缩放
      controls.enableDamping = true; // 开启阻尼效果
      controls.rotateSpeed = -0.25; // 旋转方向取反,使内部拖拽旋转方向一致
      

      扫码看效果:

      实现一个 360 全景的 N 种方案

    三、pannellum

    pannellum 是一个轻量级、免费、开源的基于 webGL 的全景播放器。支持多种投影方式、支持热点、支持漫游、视频等,且文档清晰明了、简单易用、API 比较丰富,易于功能扩展。 使用:

    pannellum.viewer('container', {
      type: 'cubemap',
      cubeMap: [
        'https://img.alicdn.com/imgextra/i4/O1CN014TNffn1nlaTfA98Fg_!!6000000005130-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i3/O1CN01LsO1Bk20QbKpFTUQr_!!6000000006844-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i1/O1CN01sS5m781ya6JgLSaVk_!!6000000006594-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i3/O1CN01uTWCLc1XOCOuA92H0_!!6000000002913-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i4/O1CN016lU3YJ1JdrJuFTcWt_!!6000000001052-0-tps-1500-1500.jpg',
        'https://img.alicdn.com/imgextra/i2/O1CN01nYe2Mn1ohkmBVyKpp_!!6000000005257-0-tps-1500-1500.jpg',
      ],
    });
    

    扫码看效果:

    实现一个 360 全景的 N 种方案

    基于 pannellum 我们封装了一个 360 全景容器组件 cube,支持了 pannellum 的所有配置,支持了立方体投影和球型投影两种方式、场景切换、陀螺仪效果等。目前该组件已应用在飞猪酒店详情中。 使用:

    export default function CubeDemo() {
      const [scenes, setScenes] = useState([]);
    
      useEffect(() => {
        // 模拟异步请求
        setTimeout(() => {
          setScenes(
            [
              {
                preview: 'https://img.alicdn.com/imgextra/i1/O1CN01dVOIEe1IhEcaIPw2z_!!6000000000924-0-tps-100-100.jpg',
                title: '客厅',
                // 或 panorama: 'https://img.alicdn.com/imgextra/i4/6000000007276/O1CN01Hp5gIf23cSOvzzA9k_!!6000000007276-0-hotel.jpg', //type: 'equirectangular' 时需要
                cubeMap: [
                  // 顺序是:前、右、后、左、上、下
                  'https://gw.alicdn.com/imgextra/i3/O1CN01550SRA1JcwWgs0sIj_!!6000000001050-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i4/O1CN01e796bV1P2CRfCQkrA_!!6000000001782-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i4/O1CN01GcW84X29SHK4oJlWc_!!6000000008066-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i2/O1CN01ZHLck11GX2ZgBHA4o_!!6000000000631-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i2/O1CN019c9xKu1ig1aC7pWPk_!!6000000004441-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i4/O1CN01XfaKOu1kzNYODz7HD_!!6000000004754-0-tps-1500-1500.jpg'
                ]
              },
              {
                preview: 'https://img.alicdn.com/imgextra/i1/O1CN01KU3hrj1uJNO2OdyaC_!!6000000006016-0-tps-100-100.jpg',
                // 或 panorama: 'https://img.alicdn.com/imgextra/i4/6000000004110/O1CN01lKLSsP1gEQSNAMIsJ_!!6000000004110-0-hotel.jpg',
                title: '书房',
                cubeMap: [
                  'https://img.alicdn.com/imgextra/i1/O1CN01fWDIfB1bWgC3NnVVa_!!6000000003473-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i2/O1CN01xt97cb1YMeg4BOCbI_!!6000000003045-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i1/O1CN01xKTq1u1DR8cdeMeYt_!!6000000000212-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i3/O1CN01Zko8Qy1p1uCLUYBji_!!6000000005301-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i3/O1CN01k3AVvK28W71UNWXW7_!!6000000007939-0-tps-1500-1500.jpg',
                  'https://img.alicdn.com/imgextra/i1/O1CN015MBT6P1N8x3J83Fqo_!!6000000001526-0-tps-1500-1500.jpg'
                ]
              }
           		......
            ]
          );
        }, 1000);
      }, []);
    
      return (
        <Cube
          container="viwer"
          config={{
            type: 'cubemap', // or 'equirectangular'
            scenes,
          }} />
      );
    }
    

    扫码看效果:

    实现一个 360 全景的 N 种方案

    球型投影

    以上的实现全部是利用立方体实现的,除此之外,我们还可以使用圆柱体投影的方式,three.js 和 pannellum 都支持这种方式。 实现一个 360 全景的 N 种方案 three.js 中:

    const geometry = new THREE.SphereBufferGeometry(300, 60, 40); // 初始化一个球体
    geometry.scale(-1, 1, 1); // 翻转x轴,将所有面朝向里
    const texture = new THREE.TextureLoader().load(
      'https://img.alicdn.com/imgextra/i2/6000000004217/O1CN01djW9bE1h1QprTMP5d_!!6000000004217-0-hotel.jpg'
    ); // 加载全景纹理
    const material = new THREE.MeshBasicMaterial({ map: texture });
    mesh = new THREE.Mesh(geometry, material);
    

    pannellum 中:

    pannellum.viewer('panorama', {
      type: 'equirectangular',
      panorama:
        'https://img.alicdn.com/imgextra/i2/6000000004217/O1CN01djW9bE1h1QprTMP5d_!!6000000004217-0-hotel.jpg',
    });
    

    实现一个 360 全景的 N 种方案

    页面和代码可参考 webgl_panorama_equirectangular 球型全景和立方体全景两种方式比较起来各有自己的特点,比如:

    • 球型全景图更贴近人眼的构建模式,只用一张图,但图片的尺寸可能会很大
    • 球型全景图,从赤道到两极,横向拉伸不断加剧,导致南北极存在扭曲的情况
    • 立方体兼容性更好,还可以用 css3 实现
    • 从模型上来说,球型是由非常多的三角面拼接出来的,比立方体更复杂,所以立方体有更高的性能

    飞猪酒店详情中,服务端对这两种方式都提供了图片资源,但在使用球型全景的方式时少数酒店上传的图片过大,导致部分手机上组件加载资源出错,所以最终决定采用立方体的方式()。

    四、krpano

    krpano 是一款较专业的全景引擎平台。目前市场中较大的全景平台大部分都用了 krpano 。但由于它用法较复杂,有一定的学习成本,需要掌握其使用的 XML 的各种配置、功能较多较重且是收费的,对于酒店详情的简单的业务场景来说有点重了,所以权衡之后没有选择它,而是选择了相对轻量的 pannellum。 使用:

    <div id='container'></div>
    
    embedpano({
      xml:
        'https://raw.githubusercontent.com/xiaotianxia/three.js-learning/gh-pages/examples/config.xml',
      target: 'container',
      html5: 'auto',
      mobilescale: 1.0,
    });
    

    扫码看效果:

    实现一个 360 全景的 N 种方案

    总结

    本文叙述了实现 360 全景功能 4 种不同的方案,包括:CSS3、Three.js、pannellum 和 krpano,在基于 webGL 的方案中,介绍了两种主要的投影方式:立方体投影和球型投影,并给出了 demo 代码和页面,推荐了基于 pannellum 开发的 360 全景容器组件, 如有错误,欢迎指正。

    参考

    1. Front-End Challenge Accepted: CSS 3D Cube
    2. Intro to CSS 3D transforms
    3. css3 系列之详解 perspective
    4. threejs
    5. pannellum

    起源地下载网 » 实现一个 360 全景的 N 种方案

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元