React + Threejs + Swiper complete code to achieve panoramic effect

React + Threejs + Swiper complete code to achieve panoramic effect

Let’s take a look at the panoramic view effect: Display address
screenshot:

Panorama

After experiencing it, do you feel that the surrounding environment has rotated in a circle and that the world is round? 😁
That’s right! Congratulations on getting it right! The earth is round! 👀

Panoramic effect realization

With the above tips, friends who have a little knowledge of threejs may have guessed that this panoramic effect is actually achieved using a sphere~ and we just pasted a texture map on the inner surface of the sphere (you can see this sphere by rolling the wheel outwards. It looks like a glass ball, which is very beautiful. There is also an easter egg 😁 (well, it’s not an easter egg if you say it out)):

insert image description here

Initially, our perspective is at the center of the sphere, and the movement of the perspective is controlled by the OrbitControls tool provided by threejs.

Then the code to create this sphere is as follows:

const geometry = new THREE.SphereBufferGeometry(500, 32, 32);
geometry.scale(-1, 1, 1); // Reverse the texture const material = new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load(imglist[0].default) // Pass in the URL or path of the image, or a Data URI.
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

const controls = new OrbitControls(camera, renderer.domElement);
controls.enablePan = false;
controls.maxDistance = 1000;

If you don't know what Data URI is, you can check out the MDN documentation

Carousel

The carousel is implemented using the swiper library, which is very convenient to use. You can refer to the documentation for details.
When sliding the carousel, an onSliderChange event will be triggered. This event passes the current swiper as a parameter. We can get the image through the currently activated element and replace the texture map of the sphere:

onSliderChange = curSwiper => {
    const mesh = this.mesh;
    const texture = imglist[curSwiper.activeIndex].default;
    mesh.material.map = new THREE.TextureLoader().load(texture);
};

Below is my swiper settings, where SwiperSlider is a slidable carousel card, EffectCoverflow is the effect triggered when sliding, and swiper provides four optional effects: Fade, Coverflow, Flip and Cube. imglist is a group of images, where imglist[i].default attribute stores the base64 encoding of the image.

import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { EffectCoverflow } from 'swiper';
import 'swiper/swiper.min.css';
import 'swiper/components/effect-coverflow/effect-coverflow.min.css';

SwiperCore.use([EffectCoverflow]);

//....
<Swiper
    className='panoramic-imgs'
    spaceBetween={50} // Spacing slidesPerView={3} // Number of images that can be previewed in the slideshow onSlideChange={this.onSliderChange} // Callback triggered when sliding onSwiper={(swiper) => console.log(swiper)} // Callback triggered on initial load direction='vertical' // Slideshow direction, horizontal by default
    effect={'coverflow'} // Slide effect grabCursor={true} // Whether to display drag when the mouse is placed on the carousel centeredSlides={true} // Whether the currently active image should be centered coverflowEffect={{ // Coverflow effect parameter settings, you can adjust it yourself "rotate": 50,
        "stretch": 0,
        "depth": 100,
        "modifier": 1,
        "slideShadows": true
    }}
    {
        imglist.map((img, idx) => {
            return <SwiperSlide key={idx}>
                <img src={img.default} className='panoramic-img'></img>
            </SwiperSlide>
        })
    }
</Swiper>

That's all about the implementation of the panoramic effect. Of course, if you have any questions, you can leave a message or refer to my code (posted below). As long as you have a certain understanding of threejs and react, I believe it is not difficult to achieve such an effect, and the amount of code is also very small~

Complete code

import React, { Component } from 'react';

import Layout from '@theme/Layout';
import Head from '@docusaurus/Head';

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import * as _ from 'underscore';
import { message } from 'antd';

import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { EffectCoverflow } from 'swiper';
import 'swiper/swiper.min.css';
import 'swiper/components/effect-coverflow/effect-coverflow.min.css';

import './index.css';
import imgs from './imgs.json';

SwiperCore.use([EffectCoverflow]);

const imglist = imgs.map(img => {
    return require('../../../static/img/panoramic/' + img.name);
});

export default class Panormatic extends Component {
    constructor() {
        super();
        this.renderer = null;
        this.camera = null;
        this.scene = null;
        this.container = null;
        this.controls = null;
        this.showMessage = true; // Easter egg prompt}

    componentDidMount() {
        const container = document.getElementById('panoramic-canvas-container');
        const canvas = document.getElementById('panoramic-canvas');
        const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });

        renderer.setClearColor(0xffffff); // b2e0df mung bean paste colorrenderer.setPixelRatio( window.devicePixelRatio );
        const height = container.clientHeight;
        const width = container.clientWidth;
        renderer.setSize(width, height);
        
        const camera = new THREE.PerspectiveCamera(60, width / height, 1, 30000);
        camera.position.set(0, 0, 1);
        camera.center = new THREE.Vector3(0, 0, 0);

        const scene = new THREE.Scene();

        const geometry = new THREE.SphereBufferGeometry(500, 32, 32);
        geometry.scale(-1, 1, 1); // Reverse the texture const material = new THREE.MeshBasicMaterial({
            map: new THREE.TextureLoader().load(imglist[0].default)
        });
        const mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh);

        const controls = new OrbitControls(camera, renderer.domElement);
        // controls.enableZoom = false;
        controls.enablePan = false;
        controls.maxDistance = 1000;

        this.renderer = renderer;
        this.camera = camera;
        this.scene = scene;
        this.container = container;
        this.controls = controls;
        this.mesh = mesh;

        // Set the global configuration of the prompt box message.config({
            top: 100,
            duration: 3.5,
            maxCount: 1,
        });

        this.onControlsChange = _.throttle(this.onChange, 100);
        controls.addEventListener('change', this.onControlsChange);
        window.addEventListener('resize', this.onWindowResize);
        this.renderLoop();
    }

    componentWillUnmount() {
        const mesh = this.mesh;
        mesh.material.dispose();
        mesh.geometry.dispose();
        this.scene.remove(mesh);
        window.removeEventListener('resize', this.onWindowResize);
        this.controls.removeEventListener('change', this.onControlsChange);
        message.destroy();
    }

    onChange = (e) => {
        const camera = this.camera;
        if (camera.position.distanceTo(camera.center) >= 700) {
            if (this.showMessage) {
                message.success('🎊Congratulations on discovering the little secret of panoramic effect~🎉');
                this.showMessage = false;
            }
        } else {
            this.showMessage = true;
        }
    }

    onSliderChange = (curSwiper) => {
        const mesh = this.mesh;
        const texture = imglist[curSwiper.activeIndex].default;
        mesh.material.map = new THREE.TextureLoader().load(texture);
    };

    onWindowResize = () => {
        const camera = this.camera;
        const renderer = this.renderer;
        const width = this.container.clientWidth;
        const height = this.container.clientHeight;
        
        camera.aspect = width / height;
        camera.updateProjectionMatrix();
        
        renderer.setSize(width, height);
    };

    renderLoop = () => {
        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(this.renderLoop);
    };

    render() {
        return (
            <Layout>
                <Head>
                    <title>Panorama | Yle</title>
                </Head>
                <div id='panoramic-container'>
                    <Swiper
                        className='panoramic-imgs'
                        spaceBetween={50}
                        slidesPerView={3}
                        onSlideChange={this.onSliderChange}
                        onSwiper={(swiper) => console.log(swiper)}
                        direction='vertical'
                        effect={'coverflow'}
                        grabCursor={true}
                        centeredSlides={true}
                        coverflowEffect={{
                            "rotate": 50,
                            "stretch": 0,
                            "depth": 100,
                            "modifier": 1,
                            "slideShadows": true
                        }}
                    >
                        {
                            imglist.map((img, idx) => {
                                return <SwiperSlide key={idx}>
                                    <img src={img.default} className='panoramic-img'></img>
                                </SwiperSlide>
                            })
                        }
                    </Swiper>
                    <div id='panoramic-canvas-container'>
                        <canvas id='panoramic-canvas'></canvas>
                    </div>
                </div>
                
                
            </Layout>
        );
    }
}

This is the end of this article about the complete code of React + Threejs + Swiper to achieve panoramic effect. For more relevant React panorama content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • A complete example of using three.js to realize panorama in Vue
  • Realizing 360-degree panoramic images based on Three.js
  • Create 360-degree panorama based on Three.js plug-in
  • THREE.JS Getting Started Tutorial (6) Steps to create your own panorama
  • JS realizes 360 degree rotation of panoramic image effect

<<:  How to solve the problem that VMware virtual machine bridge mode cannot access the Internet

>>:  Detailed explanation of the 10061 unknown error when using Navicat to connect to a remote Linux MySQL database

Recommend

VMware Workstation virtual machine installation operation method

Virtual machines are very convenient testing soft...

A brief analysis of different ways to configure static IP addresses in RHEL8

While working on a Linux server, assigning static...

mysql 5.7.11 winx64.zip installation and configuration method graphic tutorial

Install and configure the MySql database system. ...

Summary of Creating and Using Array Methods in Bash Scripts

Defining an array in Bash There are two ways to c...

CSS text alignment implementation code

When making forms, we often encounter the situati...

How to implement Linux deepin to delete redundant kernels

The previous article wrote about how to manually ...

Install three or more tomcats under Linux system (detailed steps)

If you want to install multiple tomcats, you must...

Let's talk about the LIMIT statement in MySQL in detail

Table of contents question Server layer and stora...

Multiple methods to modify MySQL root password (recommended)

Method 1: Use the SET PASSWORD command MySQL -u r...

Vue Element UI custom description list component

This article example shares the specific code of ...

Detailed process of configuring NIS in Centos7

Table of contents principle Network environment p...

WeChat applet implements text scrolling

This article example shares the specific code for...

Let's take a look at some powerful operators in JavaScript

Table of contents Preface 1. Null coalescing oper...