This article comes from flying pig front classmate @ sweet shrimp, teach you to use N ways to achieve a 360 panoramic effect with JS.

An overview of the

360 panoramic browsing is a cost-effective virtual reality solution, giving a brand new browsing experience, so that you can feel the on-site environment without leaving home. The technology is widely used in real estate, hotels, home furnishing and other fields. In an era where Internet booking has become popular, displaying a panoramic view of the hotel’s various dining and accommodation facilities on the website is a good way to attract customers. Using the network, the remote virtual browsing of the hotel’s appearance, hall, guest room, conference hall and other service places, show the comfortable environment of the hotel, give customers a real feeling, promote customers to book rooms. In the lobby of the hotel to provide a panoramic display of the rooms, no longer need to trouble customers in each room shuttle venue, you can watch the real scene of each room, more convenient for customers to confirm and select rooms, thus improving efficiency, better user experience.

Below we discuss the implementation of a 360 panorama using three methods.

A, CSS 3

Using CSS properties such as transformations and rotations, you can achieve a 360 panorama. The basic idea is as follows:

  • Use CSS3 to make a 3D cube.
  • Set target images on 6 sides of the cube (6 images exported by panorama tool).
  • Change the size of the view using properties such as perspective, translateZ, transform-style: preserve-3D, and so on.
  • Add touch events to change the number of translateX and translateY angles to achieve a basic panorama effect.

Let’s try to make a 3D cube using div:

  1. Write six sides:

    <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>
    Copy the code

    Perspective Specifies the distance between the observer and the plane z=0, so that elements with three-dimensional position transformations can have perspective effects (near larger than far smaller principle). Transform-style specifies whether it provides a 2D or 3D scene for the child element. As shown in the figure below, the height of the element is 600px, the distance from the screen z=0 is 300px, and the distance between the Angle of view and the screen is 1000px. According to the principle of similar triangles, the projection height of the element on the screen can be calculated as 857px, that is, the actual height of the element we see.

    A detailed explanation of this property can be found in the CSS3 series through Perspective.

  2. Write basic positioning

    .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;
    }
    Copy the code

  3. Rotate the faces so that the “three pairs” of faces are perpendicular to each other to achieve the following effect

    .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);
    }
    Copy the code

    The result is shown below

  4. I don’t see how it works, right? We can see the effect more intuitively by slightly adjusting the rotation Angle of the above two overlapping faces:

  5. The surfaces continue to move to the sides

    .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);
    }
    Copy the code

    The whole process can be illustrated in the following figure:

    End result:

  6. Place the perspective in the cube by adjusting the value of the Perspective property in the container style. Enlarge the size of each face, add the panorama, cut out the 6 sides of the picture, add the mouse event, you can achieve 360 panorama effect.

    Scan code to see the effect:

Two, Three. Js

Three.js is a webGL engine running in the browser. We can use it to create various 3D scenes, including camera, light and shadow, material and other objects. With Three.js, we can easily achieve all kinds of geometry we want. In the above scenario, we “spell” a cube with CSS3, while cubes and other geometers are the most common in webGL. We can take the cubes in Threejs and use them as a panorama. Think of the cubes as sky boxes, and paste the images seamlessly together so that they look like a scene in which the camera is in the center and you can’t see the cubes as long as you’re far enough away from the edges, but you can see them if you go beyond them. Here is a simple implementation:

  1. Initialize a cube geometry, color the material, and you get a cube:

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

  2. Change the texture color of the cube to an image texture:

    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);
    Copy the code

  3. Replace 6 panoramas with the same texture above:

    The images are loaded in the order of positive X(px.jpg), negative X(nx.jpg), positive Y(py.jpg), negative Y(ny.jpg), positive Z(pz.jpg) and negative Z(Nz.jpg), and assigned to each of the 6 textures for the cube skyBox.

    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);
    Copy the code

  4. Since the camera is inside the skyBox, and the inner plane will not be displayed, the magnification factor of X-axis or Z-axis should be turned negative to see the interior. When scale. Z =-1, it is equivalent to moving the positive plane of z-axis to the negative plane of Z-axis.

    skyBox.geometry.scale(1.1, -1);
    Copy the code

  5. Further optimize the experience:

    camera.position.z = 0.01; // Put the camera inside
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableZoom = false; // Disable zooming
    controls.enablePan = false; // Disable two-finger scaling
    controls.enableDamping = true; // Turn on the damping effect
    controls.rotateSpeed = -0.25; // Select the opposite direction of rotation, so that the internal rotation direction is consistent
    Copy the code

    Scan code to see the effect:

Third, pannellum

Pannellum is a lightweight, free, open source WebGL-based panorama player. Supports a variety of projection methods, hot spots, roaming, video, etc., and the document is clear, simple to use, rich API, easy to expand functions. Use:

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',]});Copy the code

Scan code to see the effect:

Based on Pannellum, we packaged a 360 panoramic container component Cube, which supports all configurations of Pannellum, including cube projection and spherical projection, scene switching, gyroscope effect, etc. At present, this component has been applied in the details of Flying Pig Hotel. Use:

export default function CubeDemo() {
  const [scenes, setScenes] = useState([]);

  useEffect(() = > {
    // Simulate an asynchronous request
    setTimeout(() = > {
      setScenes(
        [
          {
            preview: 'https://img.alicdn.com/imgextra/i1/O1CN01dVOIEe1IhEcaIPw2z_!! 6000000000924-0-tps-100-100.jpg'.title: 'sitting room'./ / or panorama: 'https://img.alicdn.com/imgextra/i4/6000000007276/O1CN01Hp5gIf23cSOvzzA9k_!!!!! 6000000007276-0-hotel.jpg', //type: 'equirectangular' when required
            cubeMap: [
              // The order is: front, right, back, left, up, down
              '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'./ / or panorama: 'https://img.alicdn.com/imgextra/i4/6000000004110/O1CN01lKLSsP1gEQSNAMIsJ_!!!!! 6000000004110-0-hotel.jpg',
            title: 'study'.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,
      }} />
  );
}
Copy the code

Scan code to see the effect:

Spherical projection

All of the above implementation is implemented using cubes. In addition, we can also use cylindrical projection, which is supported by three.js and Pannellum.Three. In js:

const geometry = new THREE.SphereBufferGeometry(300.60.40); // Initialize a sphere
geometry.scale(-1.1.1); // Flip the X-axis so that all sides are facing inward
const texture = new THREE.TextureLoader().load(
  'https://img.alicdn.com/imgextra/i2/6000000004217/O1CN01djW9bE1h1QprTMP5d_!! 6000000004217-0-hotel.jpg'
); // Load the panorama texture
const material = new THREE.MeshBasicMaterial({ map: texture });
mesh = new THREE.Mesh(geometry, material);
Copy the code

Pannellum:

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

Webgl_panorama_equirectangular Panorama and cube Panorama have their own features, such as:

  • A spherical panorama is more closely modeled on the human eye, using only one image, but the size of the image can be large
  • Spherical panorama, from the equator to the poles, with increasing horizontal stretching, resulting in a distortion of the north and South poles
  • Cubes are more compatible and can also be implemented with CSS3
  • In terms of the model, the spherical shape is made up of a lot of triangular surfaces, which is more complex than the cube, so the cube has higher performance

In the details of Flying Pig Hotel, the server provides image resources for both of these two methods. However, when using the spherical panoramic mode, a few hotels upload too large pictures, leading to errors in loading resources on some components of mobile phones, so we finally decide to adopt the cube mode ().

Fourth, the krpano

Krpano is a professional panorama engine platform. Krpano is currently used in most of the larger panorama platforms on the market. However, due to its complex usage and certain learning costs, it is necessary to master various configurations of THE XML it uses, and its functions are heavy and charged, which is too heavy for simple business scenarios of hotel details. Therefore, pannellum is not selected after weighing, but a relatively light weight. Use:

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

Scan code to see the effect:

conclusion

This paper describes four different schemes to realize 360 panorama function, including CSS3, three.js, Pannellum and Krpano. In the scheme based on webGL, two main projection modes are introduced: Projection cube and spherical projection, and gives the demo code and page, recommended based on pannellum development of 360 panoramic container components, if there is a mistake, welcome to correct.

reference

  1. Front-End Challenge Accepted: CSS 3D Cube
  2. Intro to CSS 3D transforms
  3. Css3 series: Perspective
  4. threejs
  5. pannellum